--- a/net/minecraft/world/level/storage/PrimaryLevelData.java
+++ b/net/minecraft/world/level/storage/PrimaryLevelData.java
@@ -3,27 +3,29 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import com.mojang.logging.LogUtils;
+import com.mojang.serialization.Codec;
+import com.mojang.serialization.DataResult;
 import com.mojang.serialization.Dynamic;
 import com.mojang.serialization.DynamicOps;
 import com.mojang.serialization.Lifecycle;
+import com.mojang.serialization.OptionalDynamic;
 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.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.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.ServerPlayer;
 import net.minecraft.world.Difficulty;
 import net.minecraft.world.level.GameRules;
 import net.minecraft.world.level.GameType;
@@ -31,14 +33,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();
     public static final String LEVEL_NAME = "LevelName";
     protected static final String PLAYER = "Player";
@@ -73,36 +90,22 @@
     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;
 
-    private PrimaryLevelData(
-        @Nullable CompoundTag playerData,
-        boolean modded,
-        BlockPos spawnPos,
-        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
-    ) {
+    public void setWorld(ServerLevel world) {
+        if (this.world != null) {
+            return;
+        }
+        this.world = world;
+        world.getWorld().readBukkitValues(this.pdc);
+        this.pdc = null;
+    }
+    // CraftBukkit end
+
+    private PrimaryLevelData(@Nullable CompoundTag playerData, boolean modded, BlockPos spawnPos, 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.wasModded = modded;
         this.spawnPos = spawnPos;
         this.spawnAngle = spawnAngle;
@@ -133,73 +136,45 @@
     }
 
     public PrimaryLevelData(LevelSettings levelInfo, WorldOptions generatorOptions, PrimaryLevelData.SpecialWorldProperty specialProperty, Lifecycle lifecycle) {
-        this(
-            null,
-            false,
-            BlockPos.ZERO,
-            0.0F,
-            0L,
-            0L,
-            19133,
-            0,
-            0,
-            false,
-            0,
-            false,
-            false,
-            false,
-            WorldBorder.DEFAULT_SETTINGS,
-            0,
-            0,
-            null,
-            Sets.newLinkedHashSet(),
-            new HashSet<>(),
-            new TimerQueue<>(TimerCallbacks.SERVER_CALLBACKS),
-            null,
-            EndDragonFight.Data.DEFAULT,
-            levelInfo.copy(),
-            generatorOptions,
-            specialProperty,
-            lifecycle
-        );
+        this((CompoundTag) null, false, BlockPos.ZERO, 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, LevelSettings info, PrimaryLevelData.SpecialWorldProperty specialProperty, WorldOptions generatorOptions, Lifecycle lifecycle
-    ) {
-        long l = dynamic.get("Time").asLong(0L);
-        return new PrimaryLevelData(
-            dynamic.get("Player").flatMap(CompoundTag.CODEC::parse).result().orElse(null),
-            dynamic.get("WasModded").asBoolean(false),
-            new BlockPos(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),
-            LevelVersion.parse(dynamic).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(null),
-            dynamic.get("ServerBrands")
-                .asStream()
-                .flatMap(dynamicx -> dynamicx.asString().result().stream())
-                .collect(Collectors.toCollection(Sets::newLinkedHashSet)),
-            dynamic.get("removed_features").asStream().flatMap(dynamicx -> 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),
-            info,
-            generatorOptions,
-            specialProperty,
-            lifecycle
-        );
+    public static <T> PrimaryLevelData parse(Dynamic<T> dynamic, LevelSettings info, PrimaryLevelData.SpecialWorldProperty specialProperty, WorldOptions generatorOptions, Lifecycle lifecycle) {
+        long i = dynamic.get("Time").asLong(0L);
+        OptionalDynamic<T> optionaldynamic = dynamic.get("Player"); // CraftBukkit - decompile error
+        Codec codec = CompoundTag.CODEC;
+
+        Objects.requireNonNull(codec);
+        CompoundTag nbttagcompound = (CompoundTag) optionaldynamic.flatMap(codec::parse).result().orElse((Object) null);
+        boolean flag = dynamic.get("WasModded").asBoolean(false);
+        BlockPos blockposition = new BlockPos(dynamic.get("SpawnX").asInt(0), dynamic.get("SpawnY").asInt(0), dynamic.get("SpawnZ").asInt(0));
+        float f = dynamic.get("SpawnAngle").asFloat(0.0F);
+        long j = dynamic.get("DayTime").asLong(i);
+        int k = LevelVersion.parse(dynamic).levelDataVersion();
+        int l = dynamic.get("clearWeatherTime").asInt(0);
+        int i1 = dynamic.get("rainTime").asInt(0);
+        boolean flag1 = dynamic.get("raining").asBoolean(false);
+        int j1 = 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 k1 = dynamic.get("WanderingTraderSpawnDelay").asInt(0);
+        int l1 = 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(nbttagcompound, flag, blockposition, f, i, j, k, l, i1, flag1, j1, flag2, flag3, flag4, worldborder_c, k1, l1, uuid, set, set1, customfunctioncallbacktimerqueue, nbttagcompound1, (EndDragonFight.Data) dataresult.resultOrPartial(logger::error).orElse(EndDragonFight.Data.DEFAULT), info, generatorOptions, specialProperty, lifecycle);
     }
 
     @Override
@@ -208,29 +183,35 @@
             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 = registryManager.createSerializationContext(NbtOps.INSTANCE);
-        WorldGenSettings.encode(dynamicOps, this.worldOptions, registryManager)
-            .resultOrPartial(Util.prefix("WorldGenSettings: ", LOGGER::error))
-            .ifPresent(tag -> levelNbt.put("WorldGenSettings", tag));
+        DynamicOps<Tag> dynamicops = registryManager.createSerializationContext(NbtOps.INSTANCE);
+        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.spawnPos.getX());
         levelNbt.putInt("SpawnY", this.spawnPos.getY());
@@ -250,18 +231,19 @@
         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", EndDragonFight.Data.CODEC.encodeStart(NbtOps.INSTANCE, this.endDragonFightData).getOrThrow());
+        levelNbt.put("DragonFight", (Tag) EndDragonFight.Data.CODEC.encodeStart(NbtOps.INSTANCE, this.endDragonFightData).getOrThrow());
         if (playerNbt != null) {
             levelNbt.put("Player", playerNbt);
         }
 
-        WorldDataConfiguration.CODEC
-            .encodeStart(NbtOps.INSTANCE, this.settings.getDataConfiguration())
-            .ifSuccess(dataConfiguration -> levelNbt.merge((CompoundTag)dataConfiguration))
-            .ifError(error -> LOGGER.warn("Failed to encode configuration {}", error.message()));
+        WorldDataConfiguration.CODEC.encodeStart(NbtOps.INSTANCE, this.settings.getDataConfiguration()).ifSuccess((nbtbase) -> {
+            levelNbt.merge((CompoundTag) nbtbase);
+        }).ifError((error) -> {
+            PrimaryLevelData.LOGGER.warn("Failed to encode configuration {}", error.message());
+        });
         if (this.customBossEvents != null) {
             levelNbt.put("CustomBossEvents", this.customBossEvents);
         }
@@ -272,12 +254,18 @@
         if (this.wanderingTraderId != null) {
             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
@@ -349,6 +337,25 @@
 
     @Override
     public void setThundering(boolean thundering) {
+        // Paper start - Add cause to Weather/ThunderChangeEvents
+        this.setThundering(thundering, org.bukkit.event.weather.ThunderChangeEvent.Cause.UNKNOWN);
+    }
+    public void setThundering(boolean thundering, org.bukkit.event.weather.ThunderChangeEvent.Cause cause) {
+        // Paper end - Add cause to Weather/ThunderChangeEvents
+        // 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 - Add cause to Weather/ThunderChangeEvents
+            Bukkit.getServer().getPluginManager().callEvent(thunder);
+            if (thunder.isCancelled()) {
+                return;
+            }
+        }
+        // CraftBukkit end
         this.thundering = thundering;
     }
 
@@ -369,6 +376,26 @@
 
     @Override
     public void setRaining(boolean raining) {
+        // Paper start - Add cause to Weather/ThunderChangeEvents
+        this.setRaining(raining, org.bukkit.event.weather.WeatherChangeEvent.Cause.UNKNOWN);
+    }
+
+    public void setRaining(boolean raining, org.bukkit.event.weather.WeatherChangeEvent.Cause cause) {
+        // Paper end - Add cause to Weather/ThunderChangeEvents
+        // 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 - Add cause to Weather/ThunderChangeEvents
+            Bukkit.getServer().getPluginManager().callEvent(weather);
+            if (weather.isCancelled()) {
+                return;
+            }
+        }
+        // CraftBukkit end
         this.raining = raining;
     }
 
@@ -435,6 +462,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
@@ -571,10 +604,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() {}
     }
 }
