--- a/net/minecraft/world/entity/raid/Raid.java
+++ b/net/minecraft/world/entity/raid/Raid.java
@@ -8,6 +8,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
@@ -48,6 +49,7 @@
 import net.minecraft.world.entity.EquipmentSlot;
 import net.minecraft.world.entity.LivingEntity;
 import net.minecraft.world.entity.MobSpawnType;
+import net.minecraft.world.entity.SpawnGroupData;
 import net.minecraft.world.entity.SpawnPlacementType;
 import net.minecraft.world.entity.SpawnPlacements;
 import net.minecraft.world.flag.FeatureFlags;
@@ -63,6 +65,7 @@
 import net.minecraft.world.phys.Vec3;
 
 public class Raid {
+
     private static final int SECTION_RADIUS_FOR_FINDING_NEW_VILLAGE_CENTER = 2;
     private static final int ATTEMPT_RAID_FARTHEST = 0;
     private static final int ATTEMPT_RAID_CLOSE = 1;
@@ -99,16 +102,24 @@
     public int raidOmenLevel;
     private boolean active;
     private int groupsSpawned;
-    public final ServerBossEvent raidEvent = new ServerBossEvent(RAID_NAME_COMPONENT, BossEvent.BossBarColor.RED, BossEvent.BossBarOverlay.NOTCHED_10);
+    public final ServerBossEvent raidEvent;
     private int postRaidTicks;
     private int raidCooldownTicks;
-    private final RandomSource random = RandomSource.create();
+    private final RandomSource random;
     public final int numGroups;
     private Raid.RaidStatus status;
     private int celebrationTicks;
-    private Optional<BlockPos> waveSpawnPos = Optional.empty();
+    private Optional<BlockPos> waveSpawnPos;
+    // Paper start
+    private static final String PDC_NBT_KEY = "BukkitValues";
+    private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry PDC_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry();
+    public final org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer persistentDataContainer = new org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer(PDC_TYPE_REGISTRY);
+    // Paper end
 
     public Raid(int id, ServerLevel world, BlockPos pos) {
+        this.raidEvent = new ServerBossEvent(Raid.RAID_NAME_COMPONENT, BossEvent.BossBarColor.RED, BossEvent.BossBarOverlay.NOTCHED_10);
+        this.random = RandomSource.create();
+        this.waveSpawnPos = Optional.empty();
         this.id = id;
         this.level = world;
         this.active = true;
@@ -120,6 +131,9 @@
     }
 
     public Raid(ServerLevel world, CompoundTag nbt) {
+        this.raidEvent = new ServerBossEvent(Raid.RAID_NAME_COMPONENT, BossEvent.BossBarColor.RED, BossEvent.BossBarOverlay.NOTCHED_10);
+        this.random = RandomSource.create();
+        this.waveSpawnPos = Optional.empty();
         this.level = world;
         this.id = nbt.getInt("Id");
         this.started = nbt.getBoolean("Started");
@@ -135,10 +149,21 @@
         this.status = Raid.RaidStatus.getByName(nbt.getString("Status"));
         this.heroesOfTheVillage.clear();
         if (nbt.contains("HeroesOfTheVillage", 9)) {
-            for (Tag tag : nbt.getList("HeroesOfTheVillage", 11)) {
-                this.heroesOfTheVillage.add(NbtUtils.loadUUID(tag));
+            ListTag nbttaglist = nbt.getList("HeroesOfTheVillage", 11);
+            Iterator iterator = nbttaglist.iterator();
+
+            while (iterator.hasNext()) {
+                Tag nbtbase = (Tag) iterator.next();
+
+                this.heroesOfTheVillage.add(NbtUtils.loadUUID(nbtbase));
             }
         }
+        // Paper start
+        if (nbt.contains(PDC_NBT_KEY, net.minecraft.nbt.Tag.TAG_COMPOUND)) {
+            this.persistentDataContainer.putAll(nbt.getCompound(PDC_NBT_KEY));
+        }
+        // Paper end
+
     }
 
     public boolean isOver() {
@@ -165,15 +190,24 @@
         return this.status == Raid.RaidStatus.LOSS;
     }
 
+    // CraftBukkit start
+    public boolean isInProgress() {
+        return this.status == RaidStatus.ONGOING;
+    }
+    // CraftBukkit end
+
     public float getTotalHealth() {
         return this.totalHealth;
     }
 
     public Set<Raider> getAllRaiders() {
         Set<Raider> set = Sets.newHashSet();
+        Iterator iterator = this.groupRaiderMap.values().iterator();
 
-        for (Set<Raider> set2 : this.groupRaiderMap.values()) {
-            set.addAll(set2);
+        while (iterator.hasNext()) {
+            Set<Raider> set1 = (Set) iterator.next();
+
+            set.addAll(set1);
         }
 
         return set;
@@ -192,27 +226,36 @@
     }
 
     private Predicate<ServerPlayer> validPlayer() {
-        return player -> {
-            BlockPos blockPos = player.blockPosition();
-            return player.isAlive() && this.level.getRaidAt(blockPos) == this;
+        return (entityplayer) -> {
+            BlockPos blockposition = entityplayer.blockPosition();
+
+            return entityplayer.isAlive() && this.level.getRaidAt(blockposition) == this;
         };
     }
 
     private void updatePlayers() {
         Set<ServerPlayer> set = Sets.newHashSet(this.raidEvent.getPlayers());
         List<ServerPlayer> list = this.level.getPlayers(this.validPlayer());
+        Iterator iterator = list.iterator();
 
-        for (ServerPlayer serverPlayer : list) {
-            if (!set.contains(serverPlayer)) {
-                this.raidEvent.addPlayer(serverPlayer);
+        ServerPlayer entityplayer;
+
+        while (iterator.hasNext()) {
+            entityplayer = (ServerPlayer) iterator.next();
+            if (!set.contains(entityplayer)) {
+                this.raidEvent.addPlayer(entityplayer);
             }
         }
 
-        for (ServerPlayer serverPlayer2 : set) {
-            if (!list.contains(serverPlayer2)) {
-                this.raidEvent.removePlayer(serverPlayer2);
+        iterator = set.iterator();
+
+        while (iterator.hasNext()) {
+            entityplayer = (ServerPlayer) iterator.next();
+            if (!list.contains(entityplayer)) {
+                this.raidEvent.removePlayer(entityplayer);
             }
         }
+
     }
 
     public int getMaxRaidOmenLevel() {
@@ -227,16 +270,17 @@
         this.raidOmenLevel = badOmenLevel;
     }
 
-    public boolean absorbRaidOmen(ServerPlayer serverPlayer) {
+    public boolean absorbRaidOmen(ServerPlayer entityplayer) {
         Holder<MobEffect> holder = this.level.enabledFeatures().contains(FeatureFlags.UPDATE_1_21) ? MobEffects.RAID_OMEN : MobEffects.BAD_OMEN;
-        if (!serverPlayer.hasEffect(holder)) {
+
+        if (!entityplayer.hasEffect(holder)) {
             return false;
         } else {
-            this.raidOmenLevel = this.raidOmenLevel + serverPlayer.getEffect(holder).getAmplifier() + 1;
+            this.raidOmenLevel += entityplayer.getEffect(holder).getAmplifier() + 1;
             this.raidOmenLevel = Mth.clamp(this.raidOmenLevel, 0, this.getMaxRaidOmenLevel());
             if (!this.hasFirstWaveSpawned()) {
-                serverPlayer.awardStat(Stats.RAID_TRIGGER);
-                CriteriaTriggers.RAID_OMEN.trigger(serverPlayer);
+                entityplayer.awardStat(Stats.RAID_TRIGGER);
+                CriteriaTriggers.RAID_OMEN.trigger(entityplayer);
             }
 
             return true;
@@ -252,14 +296,16 @@
     public void tick() {
         if (!this.isStopped()) {
             if (this.status == Raid.RaidStatus.ONGOING) {
-                boolean bl = this.active;
+                boolean flag = this.active;
+
                 this.active = this.level.hasChunkAt(this.center);
                 if (this.level.getDifficulty() == Difficulty.PEACEFUL) {
+                    org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(this, org.bukkit.event.raid.RaidStopEvent.Reason.PEACE); // CraftBukkit
                     this.stop();
                     return;
                 }
 
-                if (bl != this.active) {
+                if (flag != this.active) {
                     this.raidEvent.setVisible(this.active);
                 }
 
@@ -274,49 +320,54 @@
                 if (!this.level.isVillage(this.center)) {
                     if (this.groupsSpawned > 0) {
                         this.status = Raid.RaidStatus.LOSS;
+                        org.bukkit.craftbukkit.event.CraftEventFactory.callRaidFinishEvent(this, new java.util.ArrayList<>()); // CraftBukkit
                     } else {
+                        org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(this, org.bukkit.event.raid.RaidStopEvent.Reason.NOT_IN_VILLAGE); // CraftBukkit
                         this.stop();
                     }
                 }
 
-                this.ticksActive++;
+                ++this.ticksActive;
                 if (this.ticksActive >= 48000L) {
+                    org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(this, org.bukkit.event.raid.RaidStopEvent.Reason.TIMEOUT); // CraftBukkit
                     this.stop();
                     return;
                 }
 
                 int i = this.getTotalRaidersAlive();
+                boolean flag1;
+
                 if (i == 0 && this.hasMoreWaves()) {
-                    if (this.raidCooldownTicks <= 0) {
-                        if (this.raidCooldownTicks == 0 && this.groupsSpawned > 0) {
-                            this.raidCooldownTicks = 300;
-                            this.raidEvent.setName(RAID_NAME_COMPONENT);
-                            return;
-                        }
-                    } else {
-                        boolean bl2 = this.waveSpawnPos.isPresent();
-                        boolean bl3 = !bl2 && this.raidCooldownTicks % 5 == 0;
-                        if (bl2 && !this.level.isPositionEntityTicking(this.waveSpawnPos.get())) {
-                            bl3 = true;
+                    if (this.raidCooldownTicks > 0) {
+                        flag1 = this.waveSpawnPos.isPresent();
+                        boolean flag2 = !flag1 && this.raidCooldownTicks % 5 == 0;
+
+                        if (flag1 && !this.level.isPositionEntityTicking((BlockPos) this.waveSpawnPos.get())) {
+                            flag2 = true;
                         }
 
-                        if (bl3) {
-                            int j = 0;
+                        if (flag2) {
+                            byte b0 = 0;
+
                             if (this.raidCooldownTicks < 100) {
-                                j = 1;
+                                b0 = 1;
                             } else if (this.raidCooldownTicks < 40) {
-                                j = 2;
+                                b0 = 2;
                             }
 
-                            this.waveSpawnPos = this.getValidSpawnPos(j);
+                            this.waveSpawnPos = this.getValidSpawnPos(b0);
                         }
 
                         if (this.raidCooldownTicks == 300 || this.raidCooldownTicks % 20 == 0) {
                             this.updatePlayers();
                         }
 
-                        this.raidCooldownTicks--;
-                        this.raidEvent.setProgress(Mth.clamp((float)(300 - this.raidCooldownTicks) / 300.0F, 0.0F, 1.0F));
+                        --this.raidCooldownTicks;
+                        this.raidEvent.setProgress(Mth.clamp((float) (300 - this.raidCooldownTicks) / 300.0F, 0.0F, 1.0F));
+                    } else if (this.raidCooldownTicks == 0 && this.groupsSpawned > 0) {
+                        this.raidCooldownTicks = 300;
+                        this.raidEvent.setName(Raid.RAID_NAME_COMPONENT);
+                        return;
                     }
                 }
 
@@ -325,33 +376,34 @@
                     this.updateRaiders();
                     if (i > 0) {
                         if (i <= 2) {
-                            this.raidEvent
-                                .setName(RAID_NAME_COMPONENT.copy().append(" - ").append(Component.translatable("event.minecraft.raid.raiders_remaining", i)));
+                            this.raidEvent.setName(Raid.RAID_NAME_COMPONENT.copy().append(" - ").append((Component) Component.translatable("event.minecraft.raid.raiders_remaining", i)));
                         } else {
-                            this.raidEvent.setName(RAID_NAME_COMPONENT);
+                            this.raidEvent.setName(Raid.RAID_NAME_COMPONENT);
                         }
                     } else {
-                        this.raidEvent.setName(RAID_NAME_COMPONENT);
+                        this.raidEvent.setName(Raid.RAID_NAME_COMPONENT);
                     }
                 }
 
-                boolean bl4 = false;
-                int k = 0;
+                flag1 = false;
+                int j = 0;
 
                 while (this.shouldSpawnGroup()) {
-                    BlockPos blockPos = this.waveSpawnPos.isPresent() ? this.waveSpawnPos.get() : this.findRandomSpawnPos(k, 20);
-                    if (blockPos != null) {
+                    BlockPos blockposition = this.waveSpawnPos.isPresent() ? (BlockPos) this.waveSpawnPos.get() : this.findRandomSpawnPos(j, 20);
+
+                    if (blockposition != null) {
                         this.started = true;
-                        this.spawnGroup(blockPos);
-                        if (!bl4) {
-                            this.playSound(blockPos);
-                            bl4 = true;
+                        this.spawnGroup(blockposition);
+                        if (!flag1) {
+                            this.playSound(blockposition);
+                            flag1 = true;
                         }
                     } else {
-                        k++;
+                        ++j;
                     }
 
-                    if (k > 3) {
+                    if (j > 3) {
+                        org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(this, org.bukkit.event.raid.RaidStopEvent.Reason.UNSPAWNABLE);  // CraftBukkit
                         this.stop();
                         break;
                     }
@@ -359,32 +411,40 @@
 
                 if (this.isStarted() && !this.hasMoreWaves() && i == 0) {
                     if (this.postRaidTicks < 40) {
-                        this.postRaidTicks++;
+                        ++this.postRaidTicks;
                     } else {
                         this.status = Raid.RaidStatus.VICTORY;
+                        Iterator iterator = this.heroesOfTheVillage.iterator();
+
+                        List<org.bukkit.entity.Player> winners = new java.util.ArrayList<>(); // CraftBukkit
+                        while (iterator.hasNext()) {
+                            UUID uuid = (UUID) iterator.next();
+                            Entity entity = this.level.getEntity(uuid);
 
-                        for (UUID uUID : this.heroesOfTheVillage) {
-                            Entity entity = this.level.getEntity(uUID);
                             if (entity instanceof LivingEntity) {
-                                LivingEntity livingEntity = (LivingEntity)entity;
+                                LivingEntity entityliving = (LivingEntity) entity;
+
                                 if (!entity.isSpectator()) {
-                                    livingEntity.addEffect(
-                                        new MobEffectInstance(MobEffects.HERO_OF_THE_VILLAGE, 48000, this.raidOmenLevel - 1, false, false, true)
-                                    );
-                                    if (livingEntity instanceof ServerPlayer serverPlayer) {
-                                        serverPlayer.awardStat(Stats.RAID_WIN);
-                                        CriteriaTriggers.RAID_WIN.trigger(serverPlayer);
+                                    entityliving.addEffect(new MobEffectInstance(MobEffects.HERO_OF_THE_VILLAGE, 48000, this.raidOmenLevel - 1, false, false, true));
+                                    if (entityliving instanceof ServerPlayer) {
+                                        ServerPlayer entityplayer = (ServerPlayer) entityliving;
+
+                                        entityplayer.awardStat(Stats.RAID_WIN);
+                                        CriteriaTriggers.RAID_WIN.trigger(entityplayer);
+                                        winners.add(entityplayer.getBukkitEntity()); // CraftBukkit
                                     }
                                 }
                             }
                         }
+                        org.bukkit.craftbukkit.event.CraftEventFactory.callRaidFinishEvent(this, winners); // CraftBukkit
                     }
                 }
 
                 this.setDirty();
             } else if (this.isOver()) {
-                this.celebrationTicks++;
+                ++this.celebrationTicks;
                 if (this.celebrationTicks >= 600) {
+                    org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(this, org.bukkit.event.raid.RaidStopEvent.Reason.FINISHED); // CraftBukkit
                     this.stop();
                     return;
                 }
@@ -394,28 +454,32 @@
                     this.raidEvent.setVisible(true);
                     if (this.isVictory()) {
                         this.raidEvent.setProgress(0.0F);
-                        this.raidEvent.setName(RAID_BAR_VICTORY_COMPONENT);
+                        this.raidEvent.setName(Raid.RAID_BAR_VICTORY_COMPONENT);
                     } else {
-                        this.raidEvent.setName(RAID_BAR_DEFEAT_COMPONENT);
+                        this.raidEvent.setName(Raid.RAID_BAR_DEFEAT_COMPONENT);
                     }
                 }
             }
+
         }
     }
 
     private void moveRaidCenterToNearbyVillageSection() {
         Stream<SectionPos> stream = SectionPos.cube(SectionPos.of(this.center), 2);
-        stream.filter(this.level::isVillage)
-            .map(SectionPos::center)
-            .min(Comparator.comparingDouble(pos -> pos.distSqr(this.center)))
-            .ifPresent(this::setCenter);
+        ServerLevel worldserver = this.level;
+
+        Objects.requireNonNull(this.level);
+        stream.filter(worldserver::isVillage).map(SectionPos::center).min(Comparator.comparingDouble((blockposition) -> {
+            return blockposition.distSqr(this.center);
+        })).ifPresent(this::setCenter);
     }
 
     private Optional<BlockPos> getValidSpawnPos(int proximity) {
-        for (int i = 0; i < 3; i++) {
-            BlockPos blockPos = this.findRandomSpawnPos(proximity, 1);
-            if (blockPos != null) {
-                return Optional.of(blockPos);
+        for (int j = 0; j < 3; ++j) {
+            BlockPos blockposition = this.findRandomSpawnPos(proximity, 1);
+
+            if (blockposition != null) {
+                return Optional.of(blockposition);
             }
         }
 
@@ -447,118 +511,156 @@
         Set<Raider> set = Sets.newHashSet();
 
         while (iterator.hasNext()) {
-            Set<Raider> set2 = iterator.next();
+            Set<Raider> set1 = (Set) iterator.next();
+            Iterator iterator1 = set1.iterator();
 
-            for (Raider raider : set2) {
-                BlockPos blockPos = raider.blockPosition();
-                if (raider.isRemoved() || raider.level().dimension() != this.level.dimension() || this.center.distSqr(blockPos) >= 12544.0) {
-                    set.add(raider);
-                } else if (raider.tickCount > 600) {
-                    if (this.level.getEntity(raider.getUUID()) == null) {
-                        set.add(raider);
-                    }
+            while (iterator1.hasNext()) {
+                Raider entityraider = (Raider) iterator1.next();
+                BlockPos blockposition = entityraider.blockPosition();
 
-                    if (!this.level.isVillage(blockPos) && raider.getNoActionTime() > 2400) {
-                        raider.setTicksOutsideRaid(raider.getTicksOutsideRaid() + 1);
-                    }
+                if (!entityraider.isRemoved() && entityraider.level().dimension() == this.level.dimension() && this.center.distSqr(blockposition) < 12544.0D) {
+                    if (entityraider.tickCount > 600) {
+                        if (this.level.getEntity(entityraider.getUUID()) == null) {
+                            set.add(entityraider);
+                        }
 
-                    if (raider.getTicksOutsideRaid() >= 30) {
-                        set.add(raider);
+                        if (!this.level.isVillage(blockposition) && entityraider.getNoActionTime() > 2400) {
+                            entityraider.setTicksOutsideRaid(entityraider.getTicksOutsideRaid() + 1);
+                        }
+
+                        if (entityraider.getTicksOutsideRaid() >= 30) {
+                            set.add(entityraider);
+                        }
                     }
+                } else {
+                    set.add(entityraider);
                 }
             }
         }
 
-        for (Raider raider2 : set) {
-            this.removeFromRaid(raider2, true);
+        Iterator iterator2 = set.iterator();
+
+        while (iterator2.hasNext()) {
+            Raider entityraider1 = (Raider) iterator2.next();
+
+            this.removeFromRaid(entityraider1, true);
         }
+
     }
 
     private void playSound(BlockPos pos) {
         float f = 13.0F;
-        int i = 64;
+        boolean flag = true;
         Collection<ServerPlayer> collection = this.raidEvent.getPlayers();
-        long l = this.random.nextLong();
+        long i = this.random.nextLong();
+        Iterator iterator = this.level.players().iterator();
 
-        for (ServerPlayer serverPlayer : this.level.players()) {
-            Vec3 vec3 = serverPlayer.position();
-            Vec3 vec32 = Vec3.atCenterOf(pos);
-            double d = Math.sqrt((vec32.x - vec3.x) * (vec32.x - vec3.x) + (vec32.z - vec3.z) * (vec32.z - vec3.z));
-            double e = vec3.x + 13.0 / d * (vec32.x - vec3.x);
-            double g = vec3.z + 13.0 / d * (vec32.z - vec3.z);
-            if (d <= 64.0 || collection.contains(serverPlayer)) {
-                serverPlayer.connection.send(new ClientboundSoundPacket(SoundEvents.RAID_HORN, SoundSource.NEUTRAL, e, serverPlayer.getY(), g, 64.0F, 1.0F, l));
+        while (iterator.hasNext()) {
+            ServerPlayer entityplayer = (ServerPlayer) iterator.next();
+            Vec3 vec3d = entityplayer.position();
+            Vec3 vec3d1 = Vec3.atCenterOf(pos);
+            double d0 = Math.sqrt((vec3d1.x - vec3d.x) * (vec3d1.x - vec3d.x) + (vec3d1.z - vec3d.z) * (vec3d1.z - vec3d.z));
+            double d1 = vec3d.x + 13.0D / d0 * (vec3d1.x - vec3d.x);
+            double d2 = vec3d.z + 13.0D / d0 * (vec3d1.z - vec3d.z);
+
+            if (d0 <= 64.0D || collection.contains(entityplayer)) {
+                entityplayer.connection.send(new ClientboundSoundPacket(SoundEvents.RAID_HORN, SoundSource.NEUTRAL, d1, entityplayer.getY(), d2, 64.0F, 1.0F, i));
             }
         }
+
     }
 
     private void spawnGroup(BlockPos pos) {
-        boolean bl = false;
+        boolean flag = false;
         int i = this.groupsSpawned + 1;
+
         this.totalHealth = 0.0F;
-        DifficultyInstance difficultyInstance = this.level.getCurrentDifficultyAt(pos);
-        boolean bl2 = this.shouldSpawnBonusGroup();
+        DifficultyInstance difficultydamagescaler = this.level.getCurrentDifficultyAt(pos);
+        boolean flag1 = this.shouldSpawnBonusGroup();
+        Raid.RaiderType[] araid_wave = Raid.RaiderType.VALUES;
+        int j = araid_wave.length;
+        int k = 0;
 
-        for (Raid.RaiderType raiderType : Raid.RaiderType.VALUES) {
-            int j = this.getDefaultNumSpawns(raiderType, i, bl2) + this.getPotentialBonusSpawns(raiderType, this.random, i, difficultyInstance, bl2);
-            int k = 0;
+        // CraftBukkit start
+        Raider leader = null;
+        List<Raider> raiders = new java.util.ArrayList<>();
+        // CraftBukkit end
+        while (k < j) {
+            Raid.RaiderType raid_wave = araid_wave[k];
+            int l = this.getDefaultNumSpawns(raid_wave, i, flag1) + this.getPotentialBonusSpawns(raid_wave, this.random, i, difficultydamagescaler, flag1);
+            int i1 = 0;
+            int j1 = 0;
 
-            for (int l = 0; l < j; l++) {
-                Raider raider = raiderType.entityType.create(this.level);
-                if (raider == null) {
-                    break;
-                }
+            while (true) {
+                if (j1 < l) {
+                    Raider entityraider = (Raider) raid_wave.entityType.create(this.level);
 
-                if (!bl && raider.canBeLeader()) {
-                    raider.setPatrolLeader(true);
-                    this.setLeader(i, raider);
-                    bl = true;
-                }
+                    if (entityraider != null) {
+                        if (!flag && entityraider.canBeLeader()) {
+                            entityraider.setPatrolLeader(true);
+                            this.setLeader(i, entityraider);
+                            flag = true;
+                            leader = entityraider; // CraftBukkit
+                        }
 
-                this.joinRaid(i, raider, pos, false);
-                if (raiderType.entityType == EntityType.RAVAGER) {
-                    Raider raider2 = null;
-                    if (i == this.getNumGroups(Difficulty.NORMAL)) {
-                        raider2 = EntityType.PILLAGER.create(this.level);
-                    } else if (i >= this.getNumGroups(Difficulty.HARD)) {
-                        if (k == 0) {
-                            raider2 = EntityType.EVOKER.create(this.level);
-                        } else {
-                            raider2 = EntityType.VINDICATOR.create(this.level);
+                        this.joinRaid(i, entityraider, pos, false);
+                        raiders.add(entityraider); // CraftBukkit
+                        if (raid_wave.entityType == EntityType.RAVAGER) {
+                            Raider entityraider1 = null;
+
+                            if (i == this.getNumGroups(Difficulty.NORMAL)) {
+                                entityraider1 = (Raider) EntityType.PILLAGER.create(this.level);
+                            } else if (i >= this.getNumGroups(Difficulty.HARD)) {
+                                if (i1 == 0) {
+                                    entityraider1 = (Raider) EntityType.EVOKER.create(this.level);
+                                } else {
+                                    entityraider1 = (Raider) EntityType.VINDICATOR.create(this.level);
+                                }
+                            }
+
+                            ++i1;
+                            if (entityraider1 != null) {
+                                this.joinRaid(i, entityraider1, pos, false);
+                                entityraider1.moveTo(pos, 0.0F, 0.0F);
+                                entityraider1.startRiding(entityraider);
+                                raiders.add(entityraider); // CraftBukkit
+                            }
                         }
-                    }
 
-                    k++;
-                    if (raider2 != null) {
-                        this.joinRaid(i, raider2, pos, false);
-                        raider2.moveTo(pos, 0.0F, 0.0F);
-                        raider2.startRiding(raider);
+                        ++j1;
+                        continue;
                     }
                 }
+
+                ++k;
+                break;
             }
         }
 
         this.waveSpawnPos = Optional.empty();
-        this.groupsSpawned++;
+        ++this.groupsSpawned;
         this.updateBossbar();
         this.setDirty();
+        org.bukkit.craftbukkit.event.CraftEventFactory.callRaidSpawnWaveEvent(this, leader, raiders); // CraftBukkit
     }
 
     public void joinRaid(int wave, Raider raider, @Nullable BlockPos pos, boolean existing) {
-        boolean bl = this.addWaveMob(wave, raider);
-        if (bl) {
+        boolean flag1 = this.addWaveMob(wave, raider);
+
+        if (flag1) {
             raider.setCurrentRaid(this);
             raider.setWave(wave);
             raider.setCanJoinRaid(true);
             raider.setTicksOutsideRaid(0);
             if (!existing && pos != null) {
-                raider.setPos((double)pos.getX() + 0.5, (double)pos.getY() + 1.0, (double)pos.getZ() + 0.5);
-                raider.finalizeSpawn(this.level, this.level.getCurrentDifficultyAt(pos), MobSpawnType.EVENT, null);
+                raider.setPos((double) pos.getX() + 0.5D, (double) pos.getY() + 1.0D, (double) pos.getZ() + 0.5D);
+                raider.finalizeSpawn(this.level, this.level.getCurrentDifficultyAt(pos), MobSpawnType.EVENT, (SpawnGroupData) null);
                 raider.applyRaidBuffs(wave, false);
                 raider.setOnGround(true);
-                this.level.addFreshEntityWithPassengers(raider);
+                this.level.addFreshEntityWithPassengers(raider, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.RAID); // CraftBukkit
             }
         }
+
     }
 
     public void updateBossbar() {
@@ -567,10 +669,15 @@
 
     public float getHealthOfLivingRaiders() {
         float f = 0.0F;
+        Iterator iterator = this.groupRaiderMap.values().iterator();
 
-        for (Set<Raider> set : this.groupRaiderMap.values()) {
-            for (Raider raider : set) {
-                f += raider.getHealth();
+        while (iterator.hasNext()) {
+            Set<Raider> set = (Set) iterator.next();
+
+            Raider entityraider;
+
+            for (Iterator iterator1 = set.iterator(); iterator1.hasNext(); f += entityraider.getHealth()) {
+                entityraider = (Raider) iterator1.next();
             }
         }
 
@@ -586,19 +693,22 @@
     }
 
     public void removeFromRaid(Raider entity, boolean countHealth) {
-        Set<Raider> set = this.groupRaiderMap.get(entity.getWave());
+        Set<Raider> set = (Set) this.groupRaiderMap.get(entity.getWave());
+
         if (set != null) {
-            boolean bl = set.remove(entity);
-            if (bl) {
+            boolean flag1 = set.remove(entity);
+
+            if (flag1) {
                 if (countHealth) {
-                    this.totalHealth = this.totalHealth - entity.getHealth();
+                    this.totalHealth -= entity.getHealth();
                 }
 
-                entity.setCurrentRaid(null);
+                entity.setCurrentRaid((Raid) null);
                 this.updateBossbar();
                 this.setDirty();
             }
         }
+
     }
 
     private void setDirty() {
@@ -606,49 +716,38 @@
     }
 
     public static ItemStack getLeaderBannerInstance(HolderGetter<BannerPattern> bannerPatternLookup) {
-        ItemStack itemStack = new ItemStack(Items.WHITE_BANNER);
-        BannerPatternLayers bannerPatternLayers = new BannerPatternLayers.Builder()
-            .addIfRegistered(bannerPatternLookup, BannerPatterns.RHOMBUS_MIDDLE, DyeColor.CYAN)
-            .addIfRegistered(bannerPatternLookup, BannerPatterns.STRIPE_BOTTOM, DyeColor.LIGHT_GRAY)
-            .addIfRegistered(bannerPatternLookup, BannerPatterns.STRIPE_CENTER, DyeColor.GRAY)
-            .addIfRegistered(bannerPatternLookup, BannerPatterns.BORDER, DyeColor.LIGHT_GRAY)
-            .addIfRegistered(bannerPatternLookup, BannerPatterns.STRIPE_MIDDLE, DyeColor.BLACK)
-            .addIfRegistered(bannerPatternLookup, BannerPatterns.HALF_HORIZONTAL, DyeColor.LIGHT_GRAY)
-            .addIfRegistered(bannerPatternLookup, BannerPatterns.CIRCLE_MIDDLE, DyeColor.LIGHT_GRAY)
-            .addIfRegistered(bannerPatternLookup, BannerPatterns.BORDER, DyeColor.BLACK)
-            .build();
-        itemStack.set(DataComponents.BANNER_PATTERNS, bannerPatternLayers);
-        itemStack.set(DataComponents.HIDE_ADDITIONAL_TOOLTIP, Unit.INSTANCE);
-        itemStack.set(DataComponents.ITEM_NAME, OMINOUS_BANNER_PATTERN_NAME);
-        return itemStack;
+        ItemStack itemstack = new ItemStack(Items.WHITE_BANNER);
+        BannerPatternLayers bannerpatternlayers = (new BannerPatternLayers.Builder()).addIfRegistered(bannerPatternLookup, BannerPatterns.RHOMBUS_MIDDLE, DyeColor.CYAN).addIfRegistered(bannerPatternLookup, BannerPatterns.STRIPE_BOTTOM, DyeColor.LIGHT_GRAY).addIfRegistered(bannerPatternLookup, BannerPatterns.STRIPE_CENTER, DyeColor.GRAY).addIfRegistered(bannerPatternLookup, BannerPatterns.BORDER, DyeColor.LIGHT_GRAY).addIfRegistered(bannerPatternLookup, BannerPatterns.STRIPE_MIDDLE, DyeColor.BLACK).addIfRegistered(bannerPatternLookup, BannerPatterns.HALF_HORIZONTAL, DyeColor.LIGHT_GRAY).addIfRegistered(bannerPatternLookup, BannerPatterns.CIRCLE_MIDDLE, DyeColor.LIGHT_GRAY).addIfRegistered(bannerPatternLookup, BannerPatterns.BORDER, DyeColor.BLACK).build();
+
+        itemstack.set(DataComponents.BANNER_PATTERNS, bannerpatternlayers);
+        itemstack.set(DataComponents.HIDE_ADDITIONAL_TOOLTIP, Unit.INSTANCE);
+        itemstack.set(DataComponents.ITEM_NAME, Raid.OMINOUS_BANNER_PATTERN_NAME);
+        return itemstack;
     }
 
     @Nullable
     public Raider getLeader(int wave) {
-        return this.groupToLeaderMap.get(wave);
+        return (Raider) this.groupToLeaderMap.get(wave);
     }
 
     @Nullable
     private BlockPos findRandomSpawnPos(int proximity, int tries) {
-        int i = proximity == 0 ? 2 : 2 - proximity;
-        BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
-        SpawnPlacementType spawnPlacementType = SpawnPlacements.getPlacementType(EntityType.RAVAGER);
+        int k = proximity == 0 ? 2 : 2 - proximity;
+        BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
+        SpawnPlacementType spawnplacementtype = SpawnPlacements.getPlacementType(EntityType.RAVAGER);
 
-        for (int j = 0; j < tries; j++) {
-            float f = this.level.random.nextFloat() * (float) (Math.PI * 2);
-            int k = this.center.getX() + Mth.floor(Mth.cos(f) * 32.0F * (float)i) + this.level.random.nextInt(5);
-            int l = this.center.getZ() + Mth.floor(Mth.sin(f) * 32.0F * (float)i) + this.level.random.nextInt(5);
-            int m = this.level.getHeight(Heightmap.Types.WORLD_SURFACE, k, l);
-            mutableBlockPos.set(k, m, l);
-            if (!this.level.isVillage(mutableBlockPos) || proximity >= 2) {
-                int n = 10;
-                if (this.level.hasChunksAt(mutableBlockPos.getX() - 10, mutableBlockPos.getZ() - 10, mutableBlockPos.getX() + 10, mutableBlockPos.getZ() + 10)
-                    && this.level.isPositionEntityTicking(mutableBlockPos)
-                    && (
-                        spawnPlacementType.isSpawnPositionOk(this.level, mutableBlockPos, EntityType.RAVAGER)
-                            || this.level.getBlockState(mutableBlockPos.below()).is(Blocks.SNOW) && this.level.getBlockState(mutableBlockPos).isAir()
-                    )) {
-                    return mutableBlockPos;
+        for (int l = 0; l < tries; ++l) {
+            float f = this.level.random.nextFloat() * 6.2831855F;
+            int i1 = this.center.getX() + Mth.floor(Mth.cos(f) * 32.0F * (float) k) + this.level.random.nextInt(5);
+            int j1 = this.center.getZ() + Mth.floor(Mth.sin(f) * 32.0F * (float) k) + this.level.random.nextInt(5);
+            int k1 = this.level.getHeight(Heightmap.Types.WORLD_SURFACE, i1, j1);
+
+            blockposition_mutableblockposition.set(i1, k1, j1);
+            if (!this.level.isVillage((BlockPos) blockposition_mutableblockposition) || proximity >= 2) {
+                boolean flag = true;
+
+                if (this.level.hasChunksAt(blockposition_mutableblockposition.getX() - 10, blockposition_mutableblockposition.getZ() - 10, blockposition_mutableblockposition.getX() + 10, blockposition_mutableblockposition.getZ() + 10) && this.level.isPositionEntityTicking(blockposition_mutableblockposition) && (spawnplacementtype.isSpawnPositionOk(this.level, blockposition_mutableblockposition, EntityType.RAVAGER) || this.level.getBlockState(blockposition_mutableblockposition.below()).is(Blocks.SNOW) && this.level.getBlockState(blockposition_mutableblockposition).isAir())) {
+                    return blockposition_mutableblockposition;
                 }
             }
         }
@@ -661,25 +760,30 @@
     }
 
     public boolean addWaveMob(int wave, Raider entity, boolean countHealth) {
-        this.groupRaiderMap.computeIfAbsent(wave, wavex -> Sets.newHashSet());
-        Set<Raider> set = this.groupRaiderMap.get(wave);
-        Raider raider = null;
+        this.groupRaiderMap.computeIfAbsent(wave, (integer) -> {
+            return Sets.newHashSet();
+        });
+        Set<Raider> set = (Set) this.groupRaiderMap.get(wave);
+        Raider entityraider1 = null;
+        Iterator iterator = set.iterator();
 
-        for (Raider raider2 : set) {
-            if (raider2.getUUID().equals(entity.getUUID())) {
-                raider = raider2;
+        while (iterator.hasNext()) {
+            Raider entityraider2 = (Raider) iterator.next();
+
+            if (entityraider2.getUUID().equals(entity.getUUID())) {
+                entityraider1 = entityraider2;
                 break;
             }
         }
 
-        if (raider != null) {
-            set.remove(raider);
+        if (entityraider1 != null) {
+            set.remove(entityraider1);
             set.add(entity);
         }
 
         set.add(entity);
         if (countHealth) {
-            this.totalHealth = this.totalHealth + entity.getHealth();
+            this.totalHealth += entity.getHealth();
         }
 
         this.updateBossbar();
@@ -689,7 +793,7 @@
 
     public void setLeader(int wave, Raider entity) {
         this.groupToLeaderMap.put(wave, entity);
-        entity.setItemSlot(EquipmentSlot.HEAD, getLeaderBannerInstance(entity.registryAccess().lookupOrThrow(Registries.BANNER_PATTERN)));
+        entity.setItemSlot(EquipmentSlot.HEAD, Raid.getLeaderBannerInstance(entity.registryAccess().lookupOrThrow(Registries.BANNER_PATTERN)));
         entity.setDropChance(EquipmentSlot.HEAD, 2.0F);
     }
 
@@ -714,33 +818,34 @@
     }
 
     private int getPotentialBonusSpawns(Raid.RaiderType member, RandomSource random, int wave, DifficultyInstance localDifficulty, boolean extra) {
-        Difficulty difficulty = localDifficulty.getDifficulty();
-        boolean bl = difficulty == Difficulty.EASY;
-        boolean bl2 = difficulty == Difficulty.NORMAL;
+        Difficulty enumdifficulty = localDifficulty.getDifficulty();
+        boolean flag1 = enumdifficulty == Difficulty.EASY;
+        boolean flag2 = enumdifficulty == Difficulty.NORMAL;
         int j;
-        switch (member) {
-            case VINDICATOR:
-            case PILLAGER:
-                if (bl) {
+
+        switch (member.ordinal()) {
+            case 0:
+            case 2:
+                if (flag1) {
                     j = random.nextInt(2);
-                } else if (bl2) {
+                } else if (flag2) {
                     j = 1;
                 } else {
                     j = 2;
                 }
                 break;
-            case EVOKER:
+            case 1:
             default:
                 return 0;
-            case WITCH:
-                if (bl || wave <= 2 || wave == 4) {
+            case 3:
+                if (flag1 || wave <= 2 || wave == 4) {
                     return 0;
                 }
 
                 j = 1;
                 break;
-            case RAVAGER:
-                j = !bl && extra ? 1 : 0;
+            case 4:
+                j = !flag1 && extra ? 1 : 0;
         }
 
         return j > 0 ? random.nextInt(j + 1) : 0;
@@ -765,13 +870,21 @@
         nbt.putInt("CX", this.center.getX());
         nbt.putInt("CY", this.center.getY());
         nbt.putInt("CZ", this.center.getZ());
-        ListTag listTag = new ListTag();
+        ListTag nbttaglist = new ListTag();
+        Iterator iterator = this.heroesOfTheVillage.iterator();
 
-        for (UUID uUID : this.heroesOfTheVillage) {
-            listTag.add(NbtUtils.createUUID(uUID));
+        while (iterator.hasNext()) {
+            UUID uuid = (UUID) iterator.next();
+
+            nbttaglist.add(NbtUtils.createUUID(uuid));
         }
 
-        nbt.put("HeroesOfTheVillage", listTag);
+        nbt.put("HeroesOfTheVillage", nbttaglist);
+        // Paper start
+        if (!this.persistentDataContainer.isEmpty()) {
+            nbt.put(PDC_NBT_KEY, this.persistentDataContainer.toTagCompound());
+        }
+        // Paper end
         return nbt;
     }
 
@@ -790,37 +903,41 @@
 
     public float getEnchantOdds() {
         int i = this.getRaidOmenLevel();
-        if (i == 2) {
-            return 0.1F;
-        } else if (i == 3) {
-            return 0.25F;
-        } else if (i == 4) {
-            return 0.5F;
-        } else {
-            return i == 5 ? 0.75F : 0.0F;
-        }
+
+        return i == 2 ? 0.1F : (i == 3 ? 0.25F : (i == 4 ? 0.5F : (i == 5 ? 0.75F : 0.0F)));
     }
 
     public void addHeroOfTheVillage(Entity entity) {
         this.heroesOfTheVillage.add(entity.getUUID());
     }
 
-    static enum RaidStatus {
-        ONGOING,
-        VICTORY,
-        LOSS,
-        STOPPED;
+    // CraftBukkit start - a method to get all raiders
+    public java.util.Collection<Raider> getRaiders() {
+        return this.groupRaiderMap.values().stream().flatMap(Set::stream).collect(java.util.stream.Collectors.toSet());
+    }
+    // CraftBukkit end
+
+    private static enum RaidStatus {
+
+        ONGOING, VICTORY, LOSS, STOPPED;
 
         private static final Raid.RaidStatus[] VALUES = values();
 
+        private RaidStatus() {}
+
         static Raid.RaidStatus getByName(String name) {
-            for (Raid.RaidStatus raidStatus : VALUES) {
-                if (name.equalsIgnoreCase(raidStatus.name())) {
-                    return raidStatus;
+            Raid.RaidStatus[] araid_status = Raid.RaidStatus.VALUES;
+            int i = araid_status.length;
+
+            for (int j = 0; j < i; ++j) {
+                Raid.RaidStatus raid_status = araid_status[j];
+
+                if (name.equalsIgnoreCase(raid_status.name())) {
+                    return raid_status;
                 }
             }
 
-            return ONGOING;
+            return Raid.RaidStatus.ONGOING;
         }
 
         public String getName() {
@@ -828,20 +945,17 @@
         }
     }
 
-    static enum RaiderType {
-        VINDICATOR(EntityType.VINDICATOR, new int[]{0, 0, 2, 0, 1, 4, 2, 5}),
-        EVOKER(EntityType.EVOKER, new int[]{0, 0, 0, 0, 0, 1, 1, 2}),
-        PILLAGER(EntityType.PILLAGER, new int[]{0, 4, 3, 3, 4, 4, 4, 2}),
-        WITCH(EntityType.WITCH, new int[]{0, 0, 0, 0, 3, 0, 0, 1}),
-        RAVAGER(EntityType.RAVAGER, new int[]{0, 0, 0, 1, 0, 1, 0, 2});
+    private static enum RaiderType {
+
+        VINDICATOR(EntityType.VINDICATOR, new int[]{0, 0, 2, 0, 1, 4, 2, 5}), EVOKER(EntityType.EVOKER, new int[]{0, 0, 0, 0, 0, 1, 1, 2}), PILLAGER(EntityType.PILLAGER, new int[]{0, 4, 3, 3, 4, 4, 4, 2}), WITCH(EntityType.WITCH, new int[]{0, 0, 0, 0, 3, 0, 0, 1}), RAVAGER(EntityType.RAVAGER, new int[]{0, 0, 0, 1, 0, 1, 0, 2});
 
         static final Raid.RaiderType[] VALUES = values();
         final EntityType<? extends Raider> entityType;
         final int[] spawnsPerWaveBeforeBonus;
 
-        private RaiderType(final EntityType<? extends Raider> type, final int[] countInWave) {
-            this.entityType = type;
-            this.spawnsPerWaveBeforeBonus = countInWave;
+        private RaiderType(final EntityType entitytypes, final int[] aint) {
+            this.entityType = entitytypes;
+            this.spawnsPerWaveBeforeBonus = aint;
         }
     }
 }
