487 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			487 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.util.worldupdate;
 | |
| 
 | |
| import com.google.common.collect.Lists;
 | |
| import com.google.common.util.concurrent.ThreadFactoryBuilder;
 | |
| import com.mojang.datafixers.DataFixer;
 | |
| import com.mojang.logging.LogUtils;
 | |
| import it.unimi.dsi.fastutil.objects.Reference2FloatMap;
 | |
| import it.unimi.dsi.fastutil.objects.Reference2FloatMaps;
 | |
| import it.unimi.dsi.fastutil.objects.Reference2FloatOpenHashMap;
 | |
| import java.io.File;
 | |
| import java.io.IOException;
 | |
| import java.nio.file.Files;
 | |
| import java.nio.file.Path;
 | |
| import java.util.List;
 | |
| import java.util.ListIterator;
 | |
| import java.util.Optional;
 | |
| import java.util.Set;
 | |
| import java.util.concurrent.CompletableFuture;
 | |
| import java.util.concurrent.CompletionException;
 | |
| import java.util.concurrent.ThreadFactory;
 | |
| import java.util.regex.Matcher;
 | |
| import java.util.regex.Pattern;
 | |
| import java.util.stream.Collectors;
 | |
| import net.minecraft.ReportedException;
 | |
| import net.minecraft.SharedConstants;
 | |
| import net.minecraft.Util;
 | |
| import net.minecraft.core.Registry;
 | |
| import net.minecraft.core.RegistryAccess;
 | |
| import net.minecraft.core.registries.Registries;
 | |
| import net.minecraft.nbt.CompoundTag;
 | |
| import net.minecraft.nbt.ListTag;
 | |
| import net.minecraft.network.chat.Component;
 | |
| import net.minecraft.resources.ResourceKey;
 | |
| import net.minecraft.util.datafix.DataFixTypes;
 | |
| import net.minecraft.world.level.ChunkPos;
 | |
| import net.minecraft.world.level.Level;
 | |
| import net.minecraft.world.level.chunk.ChunkGenerator;
 | |
| import net.minecraft.world.level.chunk.storage.ChunkStorage;
 | |
| import net.minecraft.world.level.chunk.storage.RecreatingChunkStorage;
 | |
| import net.minecraft.world.level.chunk.storage.RecreatingSimpleRegionStorage;
 | |
| import net.minecraft.world.level.chunk.storage.RegionFile;
 | |
| import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
 | |
| import net.minecraft.world.level.chunk.storage.SimpleRegionStorage;
 | |
| import net.minecraft.world.level.dimension.LevelStem;
 | |
| import net.minecraft.world.level.saveddata.SavedData;
 | |
| import net.minecraft.world.level.storage.DimensionDataStorage;
 | |
| import net.minecraft.world.level.storage.LevelStorageSource;
 | |
| import net.minecraft.world.level.storage.WorldData;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| import org.slf4j.Logger;
 | |
| 
 | |
| public class WorldUpgrader implements AutoCloseable {
 | |
| 	static final Logger LOGGER = LogUtils.getLogger();
 | |
| 	private static final ThreadFactory THREAD_FACTORY = new ThreadFactoryBuilder().setDaemon(true).build();
 | |
| 	private static final String NEW_DIRECTORY_PREFIX = "new_";
 | |
| 	static final Component STATUS_UPGRADING_POI = Component.translatable("optimizeWorld.stage.upgrading.poi");
 | |
| 	static final Component STATUS_FINISHED_POI = Component.translatable("optimizeWorld.stage.finished.poi");
 | |
| 	static final Component STATUS_UPGRADING_ENTITIES = Component.translatable("optimizeWorld.stage.upgrading.entities");
 | |
| 	static final Component STATUS_FINISHED_ENTITIES = Component.translatable("optimizeWorld.stage.finished.entities");
 | |
| 	static final Component STATUS_UPGRADING_CHUNKS = Component.translatable("optimizeWorld.stage.upgrading.chunks");
 | |
| 	static final Component STATUS_FINISHED_CHUNKS = Component.translatable("optimizeWorld.stage.finished.chunks");
 | |
| 	final Registry<LevelStem> dimensions;
 | |
| 	final Set<ResourceKey<Level>> levels;
 | |
| 	final boolean eraseCache;
 | |
| 	final boolean recreateRegionFiles;
 | |
| 	final LevelStorageSource.LevelStorageAccess levelStorage;
 | |
| 	private final Thread thread;
 | |
| 	final DataFixer dataFixer;
 | |
| 	volatile boolean running = true;
 | |
| 	private volatile boolean finished;
 | |
| 	volatile float progress;
 | |
| 	volatile int totalChunks;
 | |
| 	volatile int totalFiles;
 | |
| 	volatile int converted;
 | |
| 	volatile int skipped;
 | |
| 	final Reference2FloatMap<ResourceKey<Level>> progressMap = Reference2FloatMaps.synchronize(new Reference2FloatOpenHashMap<>());
 | |
| 	volatile Component status = Component.translatable("optimizeWorld.stage.counting");
 | |
| 	static final Pattern REGEX = Pattern.compile("^r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mca$");
 | |
| 	final DimensionDataStorage overworldDataStorage;
 | |
| 
 | |
| 	public WorldUpgrader(
 | |
| 		LevelStorageSource.LevelStorageAccess levelStorage,
 | |
| 		DataFixer dataFixer,
 | |
| 		WorldData worldData,
 | |
| 		RegistryAccess registryAccess,
 | |
| 		boolean eraseCache,
 | |
| 		boolean recreateRegionFiles
 | |
| 	) {
 | |
| 		this.dimensions = registryAccess.lookupOrThrow(Registries.LEVEL_STEM);
 | |
| 		this.levels = (Set<ResourceKey<Level>>)this.dimensions.registryKeySet().stream().map(Registries::levelStemToLevel).collect(Collectors.toUnmodifiableSet());
 | |
| 		this.eraseCache = eraseCache;
 | |
| 		this.dataFixer = dataFixer;
 | |
| 		this.levelStorage = levelStorage;
 | |
| 		SavedData.Context context = new SavedData.Context(null, worldData.worldGenOptions().seed());
 | |
| 		this.overworldDataStorage = new DimensionDataStorage(context, this.levelStorage.getDimensionPath(Level.OVERWORLD).resolve("data"), dataFixer, registryAccess);
 | |
| 		this.recreateRegionFiles = recreateRegionFiles;
 | |
| 		this.thread = THREAD_FACTORY.newThread(this::work);
 | |
| 		this.thread.setUncaughtExceptionHandler((thread, throwable) -> {
 | |
| 			LOGGER.error("Error upgrading world", throwable);
 | |
| 			this.status = Component.translatable("optimizeWorld.stage.failed");
 | |
| 			this.finished = true;
 | |
| 		});
 | |
| 		this.thread.start();
 | |
| 	}
 | |
| 
 | |
| 	public void cancel() {
 | |
| 		this.running = false;
 | |
| 
 | |
| 		try {
 | |
| 			this.thread.join();
 | |
| 		} catch (InterruptedException var2) {
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void work() {
 | |
| 		long l = Util.getMillis();
 | |
| 		LOGGER.info("Upgrading entities");
 | |
| 		new WorldUpgrader.EntityUpgrader().upgrade();
 | |
| 		LOGGER.info("Upgrading POIs");
 | |
| 		new WorldUpgrader.PoiUpgrader().upgrade();
 | |
| 		LOGGER.info("Upgrading blocks");
 | |
| 		new WorldUpgrader.ChunkUpgrader().upgrade();
 | |
| 		this.overworldDataStorage.saveAndJoin();
 | |
| 		l = Util.getMillis() - l;
 | |
| 		LOGGER.info("World optimizaton finished after {} seconds", l / 1000L);
 | |
| 		this.finished = true;
 | |
| 	}
 | |
| 
 | |
| 	public boolean isFinished() {
 | |
| 		return this.finished;
 | |
| 	}
 | |
| 
 | |
| 	public Set<ResourceKey<Level>> levels() {
 | |
| 		return this.levels;
 | |
| 	}
 | |
| 
 | |
| 	public float dimensionProgress(ResourceKey<Level> level) {
 | |
| 		return this.progressMap.getFloat(level);
 | |
| 	}
 | |
| 
 | |
| 	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;
 | |
| 	}
 | |
| 
 | |
| 	public void close() {
 | |
| 		this.overworldDataStorage.close();
 | |
| 	}
 | |
| 
 | |
| 	static Path resolveRecreateDirectory(Path path) {
 | |
| 		return path.resolveSibling("new_" + path.getFileName().toString());
 | |
| 	}
 | |
| 
 | |
| 	abstract class AbstractUpgrader<T extends AutoCloseable> {
 | |
| 		private final Component upgradingStatus;
 | |
| 		private final Component finishedStatus;
 | |
| 		private final String type;
 | |
| 		private final String folderName;
 | |
| 		@Nullable
 | |
| 		protected CompletableFuture<Void> previousWriteFuture;
 | |
| 		protected final DataFixTypes dataFixType;
 | |
| 
 | |
| 		AbstractUpgrader(final DataFixTypes dataFixType, final String type, final String folderName, final Component upgradingStatus, final Component finishedStatus) {
 | |
| 			this.dataFixType = dataFixType;
 | |
| 			this.type = type;
 | |
| 			this.folderName = folderName;
 | |
| 			this.upgradingStatus = upgradingStatus;
 | |
| 			this.finishedStatus = finishedStatus;
 | |
| 		}
 | |
| 
 | |
| 		public void upgrade() {
 | |
| 			WorldUpgrader.this.totalFiles = 0;
 | |
| 			WorldUpgrader.this.totalChunks = 0;
 | |
| 			WorldUpgrader.this.converted = 0;
 | |
| 			WorldUpgrader.this.skipped = 0;
 | |
| 			List<WorldUpgrader.DimensionToUpgrade<T>> list = this.getDimensionsToUpgrade();
 | |
| 			if (WorldUpgrader.this.totalChunks != 0) {
 | |
| 				float f = WorldUpgrader.this.totalFiles;
 | |
| 				WorldUpgrader.this.status = this.upgradingStatus;
 | |
| 
 | |
| 				while (WorldUpgrader.this.running) {
 | |
| 					boolean bl = false;
 | |
| 					float g = 0.0F;
 | |
| 
 | |
| 					for (WorldUpgrader.DimensionToUpgrade<T> dimensionToUpgrade : list) {
 | |
| 						ResourceKey<Level> resourceKey = dimensionToUpgrade.dimensionKey;
 | |
| 						ListIterator<WorldUpgrader.FileToUpgrade> listIterator = dimensionToUpgrade.files;
 | |
| 						T autoCloseable = dimensionToUpgrade.storage;
 | |
| 						if (listIterator.hasNext()) {
 | |
| 							WorldUpgrader.FileToUpgrade fileToUpgrade = (WorldUpgrader.FileToUpgrade)listIterator.next();
 | |
| 							boolean bl2 = true;
 | |
| 
 | |
| 							for (ChunkPos chunkPos : fileToUpgrade.chunksToUpgrade) {
 | |
| 								bl2 = bl2 && this.processOnePosition(resourceKey, autoCloseable, chunkPos);
 | |
| 								bl = true;
 | |
| 							}
 | |
| 
 | |
| 							if (WorldUpgrader.this.recreateRegionFiles) {
 | |
| 								if (bl2) {
 | |
| 									this.onFileFinished(fileToUpgrade.file);
 | |
| 								} else {
 | |
| 									WorldUpgrader.LOGGER.error("Failed to convert region file {}", fileToUpgrade.file.getPath());
 | |
| 								}
 | |
| 							}
 | |
| 						}
 | |
| 
 | |
| 						float h = listIterator.nextIndex() / f;
 | |
| 						WorldUpgrader.this.progressMap.put(resourceKey, h);
 | |
| 						g += h;
 | |
| 					}
 | |
| 
 | |
| 					WorldUpgrader.this.progress = g;
 | |
| 					if (!bl) {
 | |
| 						break;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				WorldUpgrader.this.status = this.finishedStatus;
 | |
| 
 | |
| 				for (WorldUpgrader.DimensionToUpgrade<T> dimensionToUpgrade2 : list) {
 | |
| 					try {
 | |
| 						dimensionToUpgrade2.storage.close();
 | |
| 					} catch (Exception var14) {
 | |
| 						WorldUpgrader.LOGGER.error("Error upgrading chunk", (Throwable)var14);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		private List<WorldUpgrader.DimensionToUpgrade<T>> getDimensionsToUpgrade() {
 | |
| 			List<WorldUpgrader.DimensionToUpgrade<T>> list = Lists.<WorldUpgrader.DimensionToUpgrade<T>>newArrayList();
 | |
| 
 | |
| 			for (ResourceKey<Level> resourceKey : WorldUpgrader.this.levels) {
 | |
| 				RegionStorageInfo regionStorageInfo = new RegionStorageInfo(WorldUpgrader.this.levelStorage.getLevelId(), resourceKey, this.type);
 | |
| 				Path path = WorldUpgrader.this.levelStorage.getDimensionPath(resourceKey).resolve(this.folderName);
 | |
| 				T autoCloseable = this.createStorage(regionStorageInfo, path);
 | |
| 				ListIterator<WorldUpgrader.FileToUpgrade> listIterator = this.getFilesToProcess(regionStorageInfo, path);
 | |
| 				list.add(new WorldUpgrader.DimensionToUpgrade(resourceKey, autoCloseable, listIterator));
 | |
| 			}
 | |
| 
 | |
| 			return list;
 | |
| 		}
 | |
| 
 | |
| 		protected abstract T createStorage(RegionStorageInfo regionStorageInfo, Path path);
 | |
| 
 | |
| 		private ListIterator<WorldUpgrader.FileToUpgrade> getFilesToProcess(RegionStorageInfo regionStorageInfo, Path path) {
 | |
| 			List<WorldUpgrader.FileToUpgrade> list = getAllChunkPositions(regionStorageInfo, path);
 | |
| 			WorldUpgrader.this.totalFiles = WorldUpgrader.this.totalFiles + list.size();
 | |
| 			WorldUpgrader.this.totalChunks = WorldUpgrader.this.totalChunks + list.stream().mapToInt(fileToUpgrade -> fileToUpgrade.chunksToUpgrade.size()).sum();
 | |
| 			return list.listIterator();
 | |
| 		}
 | |
| 
 | |
| 		private static List<WorldUpgrader.FileToUpgrade> getAllChunkPositions(RegionStorageInfo regionStorageInfo, Path path) {
 | |
| 			File[] files = path.toFile().listFiles((filex, string) -> string.endsWith(".mca"));
 | |
| 			if (files == null) {
 | |
| 				return List.of();
 | |
| 			} else {
 | |
| 				List<WorldUpgrader.FileToUpgrade> list = Lists.<WorldUpgrader.FileToUpgrade>newArrayList();
 | |
| 
 | |
| 				for (File file : files) {
 | |
| 					Matcher matcher = WorldUpgrader.REGEX.matcher(file.getName());
 | |
| 					if (matcher.matches()) {
 | |
| 						int i = Integer.parseInt(matcher.group(1)) << 5;
 | |
| 						int j = Integer.parseInt(matcher.group(2)) << 5;
 | |
| 						List<ChunkPos> list2 = Lists.<ChunkPos>newArrayList();
 | |
| 
 | |
| 						try (RegionFile regionFile = new RegionFile(regionStorageInfo, file.toPath(), path, true)) {
 | |
| 							for (int k = 0; k < 32; k++) {
 | |
| 								for (int l = 0; l < 32; l++) {
 | |
| 									ChunkPos chunkPos = new ChunkPos(k + i, l + j);
 | |
| 									if (regionFile.doesChunkExist(chunkPos)) {
 | |
| 										list2.add(chunkPos);
 | |
| 									}
 | |
| 								}
 | |
| 							}
 | |
| 
 | |
| 							if (!list2.isEmpty()) {
 | |
| 								list.add(new WorldUpgrader.FileToUpgrade(regionFile, list2));
 | |
| 							}
 | |
| 						} catch (Throwable var18) {
 | |
| 							WorldUpgrader.LOGGER.error("Failed to read chunks from region file {}", file.toPath(), var18);
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				return list;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		private boolean processOnePosition(ResourceKey<Level> dimension, T storage, ChunkPos chunkPos) {
 | |
| 			boolean bl = false;
 | |
| 
 | |
| 			try {
 | |
| 				bl = this.tryProcessOnePosition(storage, chunkPos, dimension);
 | |
| 			} catch (CompletionException | ReportedException var7) {
 | |
| 				Throwable throwable = var7.getCause();
 | |
| 				if (!(throwable instanceof IOException)) {
 | |
| 					throw var7;
 | |
| 				}
 | |
| 
 | |
| 				WorldUpgrader.LOGGER.error("Error upgrading chunk {}", chunkPos, throwable);
 | |
| 			}
 | |
| 
 | |
| 			if (bl) {
 | |
| 				WorldUpgrader.this.converted++;
 | |
| 			} else {
 | |
| 				WorldUpgrader.this.skipped++;
 | |
| 			}
 | |
| 
 | |
| 			return bl;
 | |
| 		}
 | |
| 
 | |
| 		protected abstract boolean tryProcessOnePosition(T chunkStorage, ChunkPos chunkPos, ResourceKey<Level> dimension);
 | |
| 
 | |
| 		private void onFileFinished(RegionFile regionFile) {
 | |
| 			if (WorldUpgrader.this.recreateRegionFiles) {
 | |
| 				if (this.previousWriteFuture != null) {
 | |
| 					this.previousWriteFuture.join();
 | |
| 				}
 | |
| 
 | |
| 				Path path = regionFile.getPath();
 | |
| 				Path path2 = path.getParent();
 | |
| 				Path path3 = WorldUpgrader.resolveRecreateDirectory(path2).resolve(path.getFileName().toString());
 | |
| 
 | |
| 				try {
 | |
| 					if (path3.toFile().exists()) {
 | |
| 						Files.delete(path);
 | |
| 						Files.move(path3, path);
 | |
| 					} else {
 | |
| 						WorldUpgrader.LOGGER.error("Failed to replace an old region file. New file {} does not exist.", path3);
 | |
| 					}
 | |
| 				} catch (IOException var6) {
 | |
| 					WorldUpgrader.LOGGER.error("Failed to replace an old region file", (Throwable)var6);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	class ChunkUpgrader extends WorldUpgrader.AbstractUpgrader<ChunkStorage> {
 | |
| 		ChunkUpgrader() {
 | |
| 			super(DataFixTypes.CHUNK, "chunk", "region", WorldUpgrader.STATUS_UPGRADING_CHUNKS, WorldUpgrader.STATUS_FINISHED_CHUNKS);
 | |
| 		}
 | |
| 
 | |
| 		protected boolean tryProcessOnePosition(ChunkStorage chunkStorage, ChunkPos chunkPos, ResourceKey<Level> resourceKey) {
 | |
| 			CompoundTag compoundTag = (CompoundTag)((Optional)chunkStorage.read(chunkPos).join()).orElse(null);
 | |
| 			if (compoundTag != null) {
 | |
| 				int i = ChunkStorage.getVersion(compoundTag);
 | |
| 				ChunkGenerator chunkGenerator = WorldUpgrader.this.dimensions.getValueOrThrow(Registries.levelToLevelStem(resourceKey)).generator();
 | |
| 				CompoundTag compoundTag2 = chunkStorage.upgradeChunkTag(
 | |
| 					resourceKey, () -> WorldUpgrader.this.overworldDataStorage, compoundTag, chunkGenerator.getTypeNameForDataFixer()
 | |
| 				);
 | |
| 				ChunkPos chunkPos2 = new ChunkPos(compoundTag2.getIntOr("xPos", 0), compoundTag2.getIntOr("zPos", 0));
 | |
| 				if (!chunkPos2.equals(chunkPos)) {
 | |
| 					WorldUpgrader.LOGGER.warn("Chunk {} has invalid position {}", chunkPos, chunkPos2);
 | |
| 				}
 | |
| 
 | |
| 				boolean bl = i < SharedConstants.getCurrentVersion().dataVersion().version();
 | |
| 				if (WorldUpgrader.this.eraseCache) {
 | |
| 					bl = bl || compoundTag2.contains("Heightmaps");
 | |
| 					compoundTag2.remove("Heightmaps");
 | |
| 					bl = bl || compoundTag2.contains("isLightOn");
 | |
| 					compoundTag2.remove("isLightOn");
 | |
| 					ListTag listTag = compoundTag2.getListOrEmpty("sections");
 | |
| 
 | |
| 					for (int j = 0; j < listTag.size(); j++) {
 | |
| 						Optional<CompoundTag> optional = listTag.getCompound(j);
 | |
| 						if (!optional.isEmpty()) {
 | |
| 							CompoundTag compoundTag3 = (CompoundTag)optional.get();
 | |
| 							bl = bl || compoundTag3.contains("BlockLight");
 | |
| 							compoundTag3.remove("BlockLight");
 | |
| 							bl = bl || compoundTag3.contains("SkyLight");
 | |
| 							compoundTag3.remove("SkyLight");
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				if (bl || WorldUpgrader.this.recreateRegionFiles) {
 | |
| 					if (this.previousWriteFuture != null) {
 | |
| 						this.previousWriteFuture.join();
 | |
| 					}
 | |
| 
 | |
| 					this.previousWriteFuture = chunkStorage.write(chunkPos, () -> compoundTag2);
 | |
| 					return true;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		protected ChunkStorage createStorage(RegionStorageInfo regionStorageInfo, Path path) {
 | |
| 			return (ChunkStorage)(WorldUpgrader.this.recreateRegionFiles
 | |
| 				? new RecreatingChunkStorage(
 | |
| 					regionStorageInfo.withTypeSuffix("source"),
 | |
| 					path,
 | |
| 					regionStorageInfo.withTypeSuffix("target"),
 | |
| 					WorldUpgrader.resolveRecreateDirectory(path),
 | |
| 					WorldUpgrader.this.dataFixer,
 | |
| 					true
 | |
| 				)
 | |
| 				: new ChunkStorage(regionStorageInfo, path, WorldUpgrader.this.dataFixer, true));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	record DimensionToUpgrade<T>(ResourceKey<Level> dimensionKey, T storage, ListIterator<WorldUpgrader.FileToUpgrade> files) {
 | |
| 	}
 | |
| 
 | |
| 	class EntityUpgrader extends WorldUpgrader.SimpleRegionStorageUpgrader {
 | |
| 		EntityUpgrader() {
 | |
| 			super(DataFixTypes.ENTITY_CHUNK, "entities", WorldUpgrader.STATUS_UPGRADING_ENTITIES, WorldUpgrader.STATUS_FINISHED_ENTITIES);
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		protected CompoundTag upgradeTag(SimpleRegionStorage regionStorage, CompoundTag chunkTag) {
 | |
| 			return regionStorage.upgradeChunkTag(chunkTag, -1);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	record FileToUpgrade(RegionFile file, List<ChunkPos> chunksToUpgrade) {
 | |
| 	}
 | |
| 
 | |
| 	class PoiUpgrader extends WorldUpgrader.SimpleRegionStorageUpgrader {
 | |
| 		PoiUpgrader() {
 | |
| 			super(DataFixTypes.POI_CHUNK, "poi", WorldUpgrader.STATUS_UPGRADING_POI, WorldUpgrader.STATUS_FINISHED_POI);
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		protected CompoundTag upgradeTag(SimpleRegionStorage regionStorage, CompoundTag chunkTag) {
 | |
| 			return regionStorage.upgradeChunkTag(chunkTag, 1945);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	abstract class SimpleRegionStorageUpgrader extends WorldUpgrader.AbstractUpgrader<SimpleRegionStorage> {
 | |
| 		SimpleRegionStorageUpgrader(final DataFixTypes dataFixType, final String type, final Component upgradingStatus, final Component finishedStatus) {
 | |
| 			super(dataFixType, type, type, upgradingStatus, finishedStatus);
 | |
| 		}
 | |
| 
 | |
| 		protected SimpleRegionStorage createStorage(RegionStorageInfo regionStorageInfo, Path path) {
 | |
| 			return (SimpleRegionStorage)(WorldUpgrader.this.recreateRegionFiles
 | |
| 				? new RecreatingSimpleRegionStorage(
 | |
| 					regionStorageInfo.withTypeSuffix("source"),
 | |
| 					path,
 | |
| 					regionStorageInfo.withTypeSuffix("target"),
 | |
| 					WorldUpgrader.resolveRecreateDirectory(path),
 | |
| 					WorldUpgrader.this.dataFixer,
 | |
| 					true,
 | |
| 					this.dataFixType
 | |
| 				)
 | |
| 				: new SimpleRegionStorage(regionStorageInfo, path, WorldUpgrader.this.dataFixer, true, this.dataFixType));
 | |
| 		}
 | |
| 
 | |
| 		protected boolean tryProcessOnePosition(SimpleRegionStorage chunkStorage, ChunkPos chunkPos, ResourceKey<Level> dimension) {
 | |
| 			CompoundTag compoundTag = (CompoundTag)((Optional)chunkStorage.read(chunkPos).join()).orElse(null);
 | |
| 			if (compoundTag != null) {
 | |
| 				int i = ChunkStorage.getVersion(compoundTag);
 | |
| 				CompoundTag compoundTag2 = this.upgradeTag(chunkStorage, compoundTag);
 | |
| 				boolean bl = i < SharedConstants.getCurrentVersion().dataVersion().version();
 | |
| 				if (bl || WorldUpgrader.this.recreateRegionFiles) {
 | |
| 					if (this.previousWriteFuture != null) {
 | |
| 						this.previousWriteFuture.join();
 | |
| 					}
 | |
| 
 | |
| 					this.previousWriteFuture = chunkStorage.write(chunkPos, compoundTag2);
 | |
| 					return true;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		protected abstract CompoundTag upgradeTag(SimpleRegionStorage regionStorage, CompoundTag chunkTag);
 | |
| 	}
 | |
| }
 |