diff --git a/build.gradle b/build.gradle index ae5d85a..6960f08 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { } group = 'me.hsgamer' -version = '1.3.0' +version = '1.4.0-SNAPSHOT' description = 'A (temporary) world manager for Folia' repositories { @@ -21,7 +21,7 @@ repositories { } dependencies { - paperweight.foliaDevBundle("1.20.2-R0.1-SNAPSHOT") + paperweight.foliaDevBundle("1.20.4-R0.1-SNAPSHOT") def core = "4.3.21" implementation "me.hsgamer:hscore-bukkit-baseplugin:$core" diff --git a/src/main/java/me/hsgamer/morefoworld/WorldUtil.java b/src/main/java/me/hsgamer/morefoworld/WorldUtil.java index e18cd3e..6155a91 100644 --- a/src/main/java/me/hsgamer/morefoworld/WorldUtil.java +++ b/src/main/java/me/hsgamer/morefoworld/WorldUtil.java @@ -1,14 +1,13 @@ package me.hsgamer.morefoworld; import com.google.common.collect.ImmutableList; -import com.mojang.datafixers.util.Pair; -import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.Dynamic; import com.mojang.serialization.Lifecycle; import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.NbtOps; -import net.minecraft.nbt.Tag; -import net.minecraft.resources.RegistryOps; +import net.minecraft.nbt.NbtException; +import net.minecraft.nbt.ReportedNbtException; import net.minecraft.resources.ResourceKey; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.WorldLoader; import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.DedicatedServerProperties; @@ -27,16 +26,17 @@ import net.minecraft.world.level.levelgen.PhantomSpawner; import net.minecraft.world.level.levelgen.WorldDimensions; import net.minecraft.world.level.levelgen.WorldOptions; +import net.minecraft.world.level.storage.LevelDataAndDimensions; import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.PrimaryLevelData; -import net.minecraft.world.level.storage.WorldData; +import net.minecraft.world.level.validation.ContentValidationException; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.World; import org.bukkit.WorldCreator; -import org.bukkit.craftbukkit.v1_20_R2.CraftServer; -import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; -import org.bukkit.craftbukkit.v1_20_R2.generator.CraftWorldInfo; +import org.bukkit.craftbukkit.v1_20_R3.CraftServer; +import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R3.generator.CraftWorldInfo; import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.WorldInfo; @@ -46,6 +46,10 @@ import java.util.List; import java.util.Locale; +/** + * @see net.minecraft.server.MinecraftServer#loadWorld0(String) + * @see CraftServer#createWorld(WorldCreator) + */ public final class WorldUtil { public static FeedbackWorld addWorld(WorldCreator creator) { CraftServer craftServer = (CraftServer) Bukkit.getServer(); @@ -54,11 +58,19 @@ public static FeedbackWorld addWorld(WorldCreator creator) { String name = creator.name(); String levelName = console.getProperties().levelName; - if (name.equals(levelName) - || (console.isNetherEnabled() && name.equals(levelName + "_nether")) - || (craftServer.getAllowEnd() && name.equals(levelName + "_the_end")) - ) { + ResourceKey worldKey = null; + if (name.equals(levelName)) { return Feedback.WORLD_DEFAULT.toFeedbackWorld(); + } else if (name.equals(levelName + "_nether")) { + if (console.isNetherEnabled()) { + return Feedback.WORLD_DEFAULT.toFeedbackWorld(); + } + worldKey = net.minecraft.world.level.Level.NETHER; + } else if (name.equals(levelName + "_the_end")) { + if (craftServer.getAllowEnd()) { + return Feedback.WORLD_DEFAULT.toFeedbackWorld(); + } + worldKey = net.minecraft.world.level.Level.END; } ChunkGenerator generator = creator.generator(); @@ -94,22 +106,59 @@ public static FeedbackWorld addWorld(WorldCreator creator) { LevelStorageSource.LevelStorageAccess worldSession; try { - worldSession = LevelStorageSource.createDefault(craftServer.getWorldContainer().toPath()).createAccess(name, actualDimension); - } catch (IOException ex) { + worldSession = LevelStorageSource.createDefault(craftServer.getWorldContainer().toPath()).validateAndCreateAccess(name, actualDimension); + } catch (IOException | ContentValidationException ex) { throw new RuntimeException(ex); } + Dynamic dynamic; + if (worldSession.hasWorldData()) { + net.minecraft.world.level.storage.LevelSummary worldinfo; + + try { + dynamic = worldSession.getDataTag(); + worldinfo = worldSession.getSummary(dynamic); + } catch (NbtException | ReportedNbtException | IOException ioexception) { + LevelStorageSource.LevelDirectory convertable_b = worldSession.getLevelDirectory(); + + MinecraftServer.LOGGER.warn("Failed to load world data from {}", convertable_b.dataFile(), ioexception); + MinecraftServer.LOGGER.info("Attempting to use fallback"); + + try { + dynamic = worldSession.getDataTagFallback(); + worldinfo = worldSession.getSummary(dynamic); + } catch (NbtException | ReportedNbtException | IOException ioexception1) { + MinecraftServer.LOGGER.error("Failed to load world data from {}", convertable_b.oldDataFile(), ioexception1); + MinecraftServer.LOGGER.error("Failed to load world data from {} and {}. World files may be corrupted. Shutting down.", convertable_b.dataFile(), convertable_b.oldDataFile()); + return Feedback.WORLD_FOLDER_INVALID.toFeedbackWorld(); + } + + worldSession.restoreLevelDataFromOld(); + } + + if (worldinfo.requiresManualConversion()) { + MinecraftServer.LOGGER.info("This world must be opened in an older version (like 1.6.4) to be safely converted"); + return Feedback.WORLD_FOLDER_INVALID.toFeedbackWorld(); + } + + if (!worldinfo.isCompatible()) { + MinecraftServer.LOGGER.info("This world was created by an incompatible version."); + return Feedback.WORLD_FOLDER_INVALID.toFeedbackWorld(); + } + } else { + dynamic = null; + } + boolean hardcore = creator.hardcore(); PrimaryLevelData worlddata; WorldLoader.DataLoadContext worldloader_a = console.worldLoader; net.minecraft.core.Registry iregistry = worldloader_a.datapackDimensions().registryOrThrow(Registries.LEVEL_STEM); - DynamicOps dynamicops = RegistryOps.create(NbtOps.INSTANCE, worldloader_a.datapackWorldgen()); - Pair pair = worldSession.getDataTag(dynamicops, worldloader_a.dataConfiguration(), iregistry, worldloader_a.datapackWorldgen().allRegistriesLifecycle()); + if (dynamic != null) { + LevelDataAndDimensions leveldataanddimensions = LevelStorageSource.getLevelDataAndDimensions(dynamic, worldloader_a.dataConfiguration(), iregistry, worldloader_a.datapackWorldgen()); - if (pair != null) { - worlddata = (PrimaryLevelData) pair.getFirst(); - iregistry = pair.getSecond().dimensions(); + worlddata = (PrimaryLevelData) leveldataanddimensions.worldData(); + iregistry = leveldataanddimensions.dimensions().dimensions(); } else { LevelSettings worldsettings; WorldOptions worldoptions = new WorldOptions(creator.seed(), creator.generateStructures(), false); @@ -117,7 +166,7 @@ public static FeedbackWorld addWorld(WorldCreator creator) { DedicatedServerProperties.WorldDimensionData properties = new DedicatedServerProperties.WorldDimensionData(GsonHelper.parse((creator.generatorSettings().isEmpty()) ? "{}" : creator.generatorSettings()), creator.type().name().toLowerCase(Locale.ROOT)); - worldsettings = new LevelSettings(name, getGameType(GameMode.SURVIVAL), hardcore, Difficulty.EASY, false, new GameRules(), worldloader_a.dataConfiguration()); + worldsettings = new LevelSettings(name, GameType.byId(craftServer.getDefaultGameMode().getValue()), hardcore, Difficulty.EASY, false, new GameRules(), worldloader_a.dataConfiguration()); worlddimensions = properties.create(worldloader_a.datapackWorldgen()); WorldDimensions.Complete worlddimensions_b = worlddimensions.bake(iregistry); @@ -130,11 +179,11 @@ public static FeedbackWorld addWorld(WorldCreator creator) { worlddata.checkName(name); worlddata.setModdedInfo(console.getServerModName(), console.getModdedStatus().shouldReportAsModified()); - long j = BiomeManager.obfuscateSeed(creator.seed()); + long j = BiomeManager.obfuscateSeed(worlddata.worldGenOptions().seed()); List list = ImmutableList.of(new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(worlddata)); LevelStem worlddimension = iregistry.get(actualDimension); - WorldInfo worldInfo = new CraftWorldInfo(worlddata, worldSession, creator.environment(), worlddimension.type().value(), worlddimension.generator(), craftServer.getHandle().getServer().registryAccess()); // Paper + WorldInfo worldInfo = new CraftWorldInfo(worlddata, worldSession, creator.environment(), worlddimension.type().value(), worlddimension.generator(), console.registryAccess()); if (biomeProvider == null && generator != null) { biomeProvider = generator.getDefaultBiomeProvider(worldInfo); } @@ -145,13 +194,15 @@ public static FeedbackWorld addWorld(WorldCreator creator) { ); } - ResourceKey worldKey; - worldKey = ResourceKey.create(Registries.DIMENSION, new net.minecraft.resources.ResourceLocation(creator.key().getNamespace().toLowerCase(java.util.Locale.ENGLISH), creator.key().getKey().toLowerCase(java.util.Locale.ENGLISH))); // Paper + if (worldKey == null) { + worldKey = ResourceKey.create(Registries.DIMENSION, new net.minecraft.resources.ResourceLocation(creator.key().getNamespace().toLowerCase(java.util.Locale.ENGLISH), creator.key().getKey().toLowerCase(java.util.Locale.ENGLISH))); // Paper + } ServerLevel internal = new ServerLevel(console, console.executor, worldSession, worlddata, worldKey, worlddimension, console.progressListenerFactory.create(11), worlddata.isDebugWorld(), j, creator.environment() == World.Environment.NORMAL ? list : ImmutableList.of(), true, null, creator.environment(), generator, biomeProvider); console.addLevel(internal); + int loadRegionRadius = ((32) >> 4); internal.randomSpawnSelection = new ChunkPos(internal.getChunkSource().randomState().sampler().findSpawnPosition()); for (int currX = -loadRegionRadius; currX <= loadRegionRadius; ++currX) { @@ -165,7 +216,7 @@ public static FeedbackWorld addWorld(WorldCreator creator) { internal.setSpawnSettings(true, true); - internal.keepSpawnInMemory = creator.keepSpawnLoaded().toBooleanOrElse(internal.getWorld().getKeepSpawnInMemory()); // Paper + internal.keepSpawnInMemory = creator.keepSpawnLoaded().toBooleanOrElse(internal.getWorld().getKeepSpawnInMemory()); return Feedback.SUCCESS.toFeedbackWorld(internal.getWorld()); }