--- a/net/minecraft/world/level/storage/PrimaryLevelData.java
+++ b/net/minecraft/world/level/storage/PrimaryLevelData.java
@@ -9,24 +9,24 @@
 import com.mojang.serialization.DynamicOps;
 import com.mojang.serialization.Lifecycle;
 import java.util.HashSet;
+import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import javax.annotation.Nullable;
 import net.minecraft.CrashReportCategory;
 import net.minecraft.SharedConstants;
 import net.minecraft.Util;
 import net.minecraft.core.BlockPos;
+import net.minecraft.core.HolderLookup;
+import net.minecraft.core.Registry;
 import net.minecraft.core.RegistryAccess;
 import net.minecraft.core.UUIDUtil;
-import net.minecraft.nbt.CompoundTag;
-import net.minecraft.nbt.ListTag;
-import net.minecraft.nbt.NbtOps;
-import net.minecraft.nbt.NbtUtils;
-import net.minecraft.nbt.StringTag;
-import net.minecraft.nbt.Tag;
 import net.minecraft.resources.RegistryOps;
 import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.ServerPlayer;
 import net.minecraft.util.datafix.DataFixTypes;
 import net.minecraft.world.Difficulty;
 import net.minecraft.world.level.GameRules;
@@ -35,14 +35,29 @@
 import net.minecraft.world.level.LevelSettings;
 import net.minecraft.world.level.WorldDataConfiguration;
 import net.minecraft.world.level.border.WorldBorder;
+import net.minecraft.world.level.dimension.LevelStem;
 import net.minecraft.world.level.dimension.end.EndDragonFight;
-import net.minecraft.world.level.levelgen.WorldGenSettings;
 import net.minecraft.world.level.levelgen.WorldOptions;
 import net.minecraft.world.level.timers.TimerCallbacks;
 import net.minecraft.world.level.timers.TimerQueue;
 import org.slf4j.Logger;
+import net.minecraft.core.registries.Registries;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.ListTag;
+import net.minecraft.nbt.NbtOps;
+import net.minecraft.nbt.NbtUtils;
+import net.minecraft.nbt.StringTag;
+import net.minecraft.nbt.Tag;
+import net.minecraft.network.protocol.game.ClientboundChangeDifficultyPacket;
+import net.minecraft.world.level.levelgen.WorldDimensions;
+import net.minecraft.world.level.levelgen.WorldGenSettings;
+import org.bukkit.Bukkit;
+import org.bukkit.event.weather.ThunderChangeEvent;
+import org.bukkit.event.weather.WeatherChangeEvent;
+// CraftBukkit end
 
 public class PrimaryLevelData implements ServerLevelData, WorldData {
+
     private static final Logger LOGGER = LogUtils.getLogger();
     protected static final String PLAYER = "Player";
     protected static final String WORLD_GEN_SETTINGS = "WorldGenSettings";
@@ -82,6 +97,20 @@
     private boolean wasModded;
     private final Set<String> removedFeatureFlags;
     private final TimerQueue<MinecraftServer> scheduledEvents;
+    // CraftBukkit start - Add world and pdc
+    public Registry<LevelStem> customDimensions;
+    private ServerLevel world;
+    protected Tag pdc;
+
+    public void setWorld(ServerLevel world) {
+        if (this.world != null) {
+            return;
+        }
+        this.world = world;
+        world.getWorld().readBukkitValues(pdc);
+        this.pdc = null;
+    }
+    // CraftBukkit end
 
     private PrimaryLevelData(@Nullable DataFixer dataFixer, int dataVersion, @Nullable CompoundTag playerData, boolean modded, int spawnX, int spawnY, int spawnZ, float spawnAngle, long time, long timeOfDay, int version, int clearWeatherTime, int rainTime, boolean raining, int thunderTime, boolean thundering, boolean initialized, boolean difficultyLocked, WorldBorder.Settings worldBorder, int wanderingTraderSpawnDelay, int wanderingTraderSpawnChance, @Nullable UUID wanderingTraderId, Set<String> serverBrands, Set<String> removedFeatures, TimerQueue<MinecraftServer> scheduledEvents, @Nullable CompoundTag customBossEvents, EndDragonFight.Data dragonFight, LevelSettings levelInfo, WorldOptions generatorOptions, PrimaryLevelData.SpecialWorldProperty specialProperty, Lifecycle lifecycle) {
         this.fixerUpper = dataFixer;
@@ -118,16 +147,42 @@
     }
 
     public PrimaryLevelData(LevelSettings levelInfo, WorldOptions generatorOptions, PrimaryLevelData.SpecialWorldProperty specialProperty, Lifecycle lifecycle) {
-        this((DataFixer)null, SharedConstants.getCurrentVersion().getDataVersion().getVersion(), (CompoundTag)null, false, 0, 0, 0, 0.0F, 0L, 0L, 19133, 0, 0, false, 0, false, false, false, WorldBorder.DEFAULT_SETTINGS, 0, 0, (UUID)null, Sets.newLinkedHashSet(), new HashSet<>(), new TimerQueue<>(TimerCallbacks.SERVER_CALLBACKS), (CompoundTag)null, EndDragonFight.Data.DEFAULT, levelInfo.copy(), generatorOptions, specialProperty, lifecycle);
+        this((DataFixer) null, SharedConstants.getCurrentVersion().getDataVersion().getVersion(), (CompoundTag) null, false, 0, 0, 0, 0.0F, 0L, 0L, 19133, 0, 0, false, 0, false, false, false, WorldBorder.DEFAULT_SETTINGS, 0, 0, (UUID) null, Sets.newLinkedHashSet(), new HashSet(), new TimerQueue<>(TimerCallbacks.SERVER_CALLBACKS), (CompoundTag) null, EndDragonFight.Data.DEFAULT, levelInfo.copy(), generatorOptions, specialProperty, lifecycle);
     }
 
     public static <T> PrimaryLevelData parse(Dynamic<T> dynamic, DataFixer dataFixer, int dataVersion, @Nullable CompoundTag playerData, LevelSettings levelInfo, LevelVersion saveVersionInfo, PrimaryLevelData.SpecialWorldProperty specialProperty, WorldOptions generatorOptions, Lifecycle lifecycle) {
-        long l = dynamic.get("Time").asLong(0L);
-        return new PrimaryLevelData(dataFixer, dataVersion, playerData, dynamic.get("WasModded").asBoolean(false), dynamic.get("SpawnX").asInt(0), dynamic.get("SpawnY").asInt(0), dynamic.get("SpawnZ").asInt(0), dynamic.get("SpawnAngle").asFloat(0.0F), l, dynamic.get("DayTime").asLong(l), saveVersionInfo.levelDataVersion(), dynamic.get("clearWeatherTime").asInt(0), dynamic.get("rainTime").asInt(0), dynamic.get("raining").asBoolean(false), dynamic.get("thunderTime").asInt(0), dynamic.get("thundering").asBoolean(false), dynamic.get("initialized").asBoolean(true), dynamic.get("DifficultyLocked").asBoolean(false), WorldBorder.Settings.read(dynamic, WorldBorder.DEFAULT_SETTINGS), dynamic.get("WanderingTraderSpawnDelay").asInt(0), dynamic.get("WanderingTraderSpawnChance").asInt(0), dynamic.get("WanderingTraderId").read(UUIDUtil.CODEC).result().orElse((UUID)null), dynamic.get("ServerBrands").asStream().flatMap((dynamicx) -> {
-            return dynamicx.asString().result().stream();
-        }).collect(Collectors.toCollection(Sets::newLinkedHashSet)), dynamic.get("removed_features").asStream().flatMap((dynamicx) -> {
-            return dynamicx.asString().result().stream();
-        }).collect(Collectors.toSet()), new TimerQueue<>(TimerCallbacks.SERVER_CALLBACKS, dynamic.get("ScheduledEvents").asStream()), (CompoundTag)dynamic.get("CustomBossEvents").orElseEmptyMap().getValue(), dynamic.get("DragonFight").read(EndDragonFight.Data.CODEC).resultOrPartial(LOGGER::error).orElse(EndDragonFight.Data.DEFAULT), levelInfo, generatorOptions, specialProperty, lifecycle);
+        long j = dynamic.get("Time").asLong(0L);
+        boolean flag = dynamic.get("WasModded").asBoolean(false);
+        int k = dynamic.get("SpawnX").asInt(0);
+        int l = dynamic.get("SpawnY").asInt(0);
+        int i1 = dynamic.get("SpawnZ").asInt(0);
+        float f = dynamic.get("SpawnAngle").asFloat(0.0F);
+        long j1 = dynamic.get("DayTime").asLong(j);
+        int k1 = saveVersionInfo.levelDataVersion();
+        int l1 = dynamic.get("clearWeatherTime").asInt(0);
+        int i2 = dynamic.get("rainTime").asInt(0);
+        boolean flag1 = dynamic.get("raining").asBoolean(false);
+        int j2 = dynamic.get("thunderTime").asInt(0);
+        boolean flag2 = dynamic.get("thundering").asBoolean(false);
+        boolean flag3 = dynamic.get("initialized").asBoolean(true);
+        boolean flag4 = dynamic.get("DifficultyLocked").asBoolean(false);
+        WorldBorder.Settings worldborder_c = WorldBorder.Settings.read(dynamic, WorldBorder.DEFAULT_SETTINGS);
+        int k2 = dynamic.get("WanderingTraderSpawnDelay").asInt(0);
+        int l2 = dynamic.get("WanderingTraderSpawnChance").asInt(0);
+        UUID uuid = (UUID) dynamic.get("WanderingTraderId").read(UUIDUtil.CODEC).result().orElse(null); // CraftBukkit - decompile error
+        Set set = (Set) dynamic.get("ServerBrands").asStream().flatMap((dynamic1) -> {
+            return dynamic1.asString().result().stream();
+        }).collect(Collectors.toCollection(Sets::newLinkedHashSet));
+        Set set1 = (Set) dynamic.get("removed_features").asStream().flatMap((dynamic1) -> {
+            return dynamic1.asString().result().stream();
+        }).collect(Collectors.toSet());
+        TimerQueue customfunctioncallbacktimerqueue = new TimerQueue<>(TimerCallbacks.SERVER_CALLBACKS, dynamic.get("ScheduledEvents").asStream());
+        CompoundTag nbttagcompound1 = (CompoundTag) dynamic.get("CustomBossEvents").orElseEmptyMap().getValue();
+        DataResult<EndDragonFight.Data> dataresult = dynamic.get("DragonFight").read(EndDragonFight.Data.CODEC); // CraftBukkit - decompile error
+        Logger logger = PrimaryLevelData.LOGGER;
+
+        Objects.requireNonNull(logger);
+        return new PrimaryLevelData(dataFixer, dataVersion, playerData, flag, k, l, i1, f, j, j1, k1, l1, i2, flag1, j2, flag2, flag3, flag4, worldborder_c, k2, l2, uuid, set, set1, customfunctioncallbacktimerqueue, nbttagcompound1, (EndDragonFight.Data) dataresult.resultOrPartial(logger::error).orElse(EndDragonFight.Data.DEFAULT), levelInfo, generatorOptions, specialProperty, lifecycle);
     }
 
     @Override
@@ -137,28 +192,34 @@
             playerNbt = this.loadedPlayerTag;
         }
 
-        CompoundTag compoundTag = new CompoundTag();
-        this.setTagData(registryManager, compoundTag, playerNbt);
-        return compoundTag;
+        CompoundTag nbttagcompound1 = new CompoundTag();
+
+        this.setTagData(registryManager, nbttagcompound1, playerNbt);
+        return nbttagcompound1;
     }
 
     private void setTagData(RegistryAccess registryManager, CompoundTag levelNbt, @Nullable CompoundTag playerNbt) {
-        levelNbt.put("ServerBrands", stringCollectionToTag(this.knownServerBrands));
+        levelNbt.put("ServerBrands", PrimaryLevelData.stringCollectionToTag(this.knownServerBrands));
         levelNbt.putBoolean("WasModded", this.wasModded);
         if (!this.removedFeatureFlags.isEmpty()) {
-            levelNbt.put("removed_features", stringCollectionToTag(this.removedFeatureFlags));
+            levelNbt.put("removed_features", PrimaryLevelData.stringCollectionToTag(this.removedFeatureFlags));
         }
 
-        CompoundTag compoundTag = new CompoundTag();
-        compoundTag.putString("Name", SharedConstants.getCurrentVersion().getName());
-        compoundTag.putInt("Id", SharedConstants.getCurrentVersion().getDataVersion().getVersion());
-        compoundTag.putBoolean("Snapshot", !SharedConstants.getCurrentVersion().isStable());
-        compoundTag.putString("Series", SharedConstants.getCurrentVersion().getDataVersion().getSeries());
-        levelNbt.put("Version", compoundTag);
+        CompoundTag nbttagcompound2 = new CompoundTag();
+
+        nbttagcompound2.putString("Name", SharedConstants.getCurrentVersion().getName());
+        nbttagcompound2.putInt("Id", SharedConstants.getCurrentVersion().getDataVersion().getVersion());
+        nbttagcompound2.putBoolean("Snapshot", !SharedConstants.getCurrentVersion().isStable());
+        nbttagcompound2.putString("Series", SharedConstants.getCurrentVersion().getDataVersion().getSeries());
+        levelNbt.put("Version", nbttagcompound2);
         NbtUtils.addCurrentDataVersion(levelNbt);
-        DynamicOps<Tag> dynamicOps = RegistryOps.create(NbtOps.INSTANCE, registryManager);
-        WorldGenSettings.encode(dynamicOps, this.worldOptions, registryManager).resultOrPartial(Util.prefix("WorldGenSettings: ", LOGGER::error)).ifPresent((tag) -> {
-            levelNbt.put("WorldGenSettings", tag);
+        DynamicOps<Tag> dynamicops = RegistryOps.create(NbtOps.INSTANCE, (HolderLookup.Provider) registryManager);
+        DataResult<Tag> dataresult = WorldGenSettings.encode(dynamicops, this.worldOptions, new WorldDimensions(this.customDimensions != null ? this.customDimensions : registryManager.registryOrThrow(Registries.LEVEL_STEM))); // CraftBukkit
+        Logger logger = PrimaryLevelData.LOGGER;
+
+        Objects.requireNonNull(logger);
+        dataresult.resultOrPartial(Util.prefix("WorldGenSettings: ", logger::error)).ifPresent((nbtbase) -> {
+            levelNbt.put("WorldGenSettings", nbtbase);
         });
         levelNbt.putInt("GameType", this.settings.gameType().getId());
         levelNbt.putInt("SpawnX", this.xSpawn);
@@ -179,19 +240,20 @@
         levelNbt.putBoolean("allowCommands", this.settings.allowCommands());
         levelNbt.putBoolean("initialized", this.initialized);
         this.worldBorder.write(levelNbt);
-        levelNbt.putByte("Difficulty", (byte)this.settings.difficulty().getId());
+        levelNbt.putByte("Difficulty", (byte) this.settings.difficulty().getId());
         levelNbt.putBoolean("DifficultyLocked", this.difficultyLocked);
         levelNbt.put("GameRules", this.settings.gameRules().createTag());
-        levelNbt.put("DragonFight", Util.getOrThrow(EndDragonFight.Data.CODEC.encodeStart(NbtOps.INSTANCE, this.endDragonFightData), IllegalStateException::new));
+        levelNbt.put("DragonFight", (Tag) Util.getOrThrow(EndDragonFight.Data.CODEC.encodeStart(NbtOps.INSTANCE, this.endDragonFightData), IllegalStateException::new));
         if (playerNbt != null) {
             levelNbt.put("Player", playerNbt);
         }
 
-        DataResult<Tag> dataResult = WorldDataConfiguration.CODEC.encodeStart(NbtOps.INSTANCE, this.settings.getDataConfiguration());
-        dataResult.get().ifLeft((dataConfiguration) -> {
-            levelNbt.merge((CompoundTag)dataConfiguration);
-        }).ifRight((result) -> {
-            LOGGER.warn("Failed to encode configuration {}", (Object)result.message());
+        DataResult<Tag> dataresult1 = WorldDataConfiguration.CODEC.encodeStart(NbtOps.INSTANCE, this.settings.getDataConfiguration());
+
+        dataresult1.get().ifLeft((nbtbase) -> {
+            levelNbt.merge((CompoundTag) nbtbase);
+        }).ifRight((partialresult) -> {
+            PrimaryLevelData.LOGGER.warn("Failed to encode configuration {}", partialresult.message());
         });
         if (this.customBossEvents != null) {
             levelNbt.put("CustomBossEvents", this.customBossEvents);
@@ -204,12 +266,17 @@
             levelNbt.putUUID("WanderingTraderId", this.wanderingTraderId);
         }
 
+        levelNbt.putString("Bukkit.Version", Bukkit.getName() + "/" + Bukkit.getVersion() + "/" + Bukkit.getBukkitVersion()); // CraftBukkit
+        this.world.getWorld().storeBukkitValues(levelNbt); // CraftBukkit - add pdc
     }
 
     private static ListTag stringCollectionToTag(Set<String> strings) {
-        ListTag listTag = new ListTag();
-        strings.stream().map(StringTag::valueOf).forEach(listTag::add);
-        return listTag;
+        ListTag nbttaglist = new ListTag();
+        Stream<StringTag> stream = strings.stream().map(StringTag::valueOf); // CraftBukkit - decompile error
+
+        Objects.requireNonNull(nbttaglist);
+        stream.forEach(nbttaglist::add);
+        return nbttaglist;
     }
 
     @Override
@@ -246,10 +313,10 @@
         if (!this.upgradedPlayerTag && this.loadedPlayerTag != null) {
             if (this.playerDataVersion < SharedConstants.getCurrentVersion().getDataVersion().getVersion()) {
                 if (this.fixerUpper == null) {
-                    throw (NullPointerException)Util.pauseInIde(new NullPointerException("Fixer Upper not set inside LevelData, and the player tag is not upgraded."));
+                    throw (NullPointerException) Util.pauseInIde(new NullPointerException("Fixer Upper not set inside LevelData, and the player tag is not upgraded."));
                 }
 
-                this.loadedPlayerTag = DataFixTypes.PLAYER.updateToCurrentVersion(this.fixerUpper, this.loadedPlayerTag, this.playerDataVersion);
+                this.loadedPlayerTag = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.PLAYER, this.loadedPlayerTag, version, SharedConstants.getCurrentVersion().getDataVersion().getVersion()); // Paper
             }
 
             this.upgradedPlayerTag = true;
@@ -327,6 +394,25 @@
 
     @Override
     public void setThundering(boolean thundering) {
+        // Paper start
+        this.setThundering(thundering, org.bukkit.event.weather.ThunderChangeEvent.Cause.UNKNOWN);
+    }
+    public void setThundering(boolean thundering, org.bukkit.event.weather.ThunderChangeEvent.Cause cause) {
+        // Paper end
+        // CraftBukkit start
+        if (this.thundering == thundering) {
+            return;
+        }
+
+        org.bukkit.World world = Bukkit.getWorld(this.getLevelName());
+        if (world != null) {
+            ThunderChangeEvent thunder = new ThunderChangeEvent(world, thundering, cause); // Paper
+            Bukkit.getServer().getPluginManager().callEvent(thunder);
+            if (thunder.isCancelled()) {
+                return;
+            }
+        }
+        // CraftBukkit end
         this.thundering = thundering;
     }
 
@@ -347,6 +433,26 @@
 
     @Override
     public void setRaining(boolean raining) {
+        // Paper start
+        this.setRaining(raining, org.bukkit.event.weather.WeatherChangeEvent.Cause.UNKNOWN);
+    }
+
+    public void setRaining(boolean raining, org.bukkit.event.weather.WeatherChangeEvent.Cause cause) {
+        // Paper end
+        // CraftBukkit start
+        if (this.raining == raining) {
+            return;
+        }
+
+        org.bukkit.World world = Bukkit.getWorld(this.getLevelName());
+        if (world != null) {
+            WeatherChangeEvent weather = new WeatherChangeEvent(world, raining, cause); // Paper
+            Bukkit.getServer().getPluginManager().callEvent(weather);
+            if (weather.isCancelled()) {
+                return;
+            }
+        }
+        // CraftBukkit end
         this.raining = raining;
     }
 
@@ -413,6 +519,12 @@
     @Override
     public void setDifficulty(Difficulty difficulty) {
         this.settings = this.settings.withDifficulty(difficulty);
+        // CraftBukkit start
+        ClientboundChangeDifficultyPacket packet = new ClientboundChangeDifficultyPacket(this.getDifficulty(), this.isDifficultyLocked());
+        for (ServerPlayer player : (java.util.List<ServerPlayer>) (java.util.List) this.world.players()) {
+            player.connection.send(packet);
+        }
+        // CraftBukkit end
     }
 
     @Override
@@ -549,11 +661,20 @@
         return this.settings.copy();
     }
 
+    // CraftBukkit start - Check if the name stored in NBT is the correct one
+    public void checkName(String name) {
+        if (!this.settings.levelName.equals(name)) {
+            this.settings.levelName = name;
+        }
+    }
+    // CraftBukkit end
+
     /** @deprecated */
     @Deprecated
     public static enum SpecialWorldProperty {
-        NONE,
-        FLAT,
-        DEBUG;
+
+        NONE, FLAT, DEBUG;
+
+        private SpecialWorldProperty() {}
     }
 }
