--- a/net/minecraft/world/entity/raid/Raid.java
+++ b/net/minecraft/world/entity/raid/Raid.java
@@ -61,6 +_,12 @@
 import net.minecraft.world.phys.Vec3;
 
 public class Raid {
+    // 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;
+    public int idOrNegativeOne = -1;
+    // Paper end
     public static final SpawnPlacementType RAVAGER_SPAWN_PLACEMENT_TYPE = SpawnPlacements.getPlacementType(EntityType.RAVAGER);
     public static final MapCodec<Raid> MAP_CODEC = RecordCodecBuilder.mapCodec(
         instance -> instance.group(
@@ -76,6 +_,8 @@
                 Raid.RaidStatus.CODEC.fieldOf("status").forGetter(raid -> raid.status),
                 BlockPos.CODEC.fieldOf("center").forGetter(raid -> raid.center),
                 UUIDUtil.CODEC_SET.fieldOf("heroes_of_the_village").forGetter(raid -> raid.heroesOfTheVillage)
+                , org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer.createCodec(PDC_TYPE_REGISTRY).lenientOptionalFieldOf(PDC_NBT_KEY) // Paper - add persistent data container
+                    .forGetter(raid -> raid.persistentDataContainer.isEmpty() ? java.util.Optional.empty() : java.util.Optional.of(raid.persistentDataContainer)) // Paper - add persistent data container
             )
             .apply(instance, Raid::new)
     );
@@ -104,19 +_,19 @@
     public static final int RAID_REMOVAL_THRESHOLD_SQR = 12544;
     private final Map<Integer, Raider> groupToLeaderMap = Maps.newHashMap();
     private final Map<Integer, Set<Raider>> groupRaiderMap = Maps.newHashMap();
-    private final Set<UUID> heroesOfTheVillage = Sets.newHashSet();
-    private long ticksActive;
+    public final Set<UUID> heroesOfTheVillage = Sets.newHashSet();
+    public long ticksActive;
     private BlockPos center;
     private boolean started;
-    private float totalHealth;
-    private int raidOmenLevel;
+    public float totalHealth;
+    public int raidOmenLevel;
     private boolean active;
     private int groupsSpawned;
-    private final ServerBossEvent raidEvent = new ServerBossEvent(RAID_NAME_COMPONENT, BossEvent.BossBarColor.RED, BossEvent.BossBarOverlay.NOTCHED_10);
+    public final ServerBossEvent raidEvent = new ServerBossEvent(RAID_NAME_COMPONENT, BossEvent.BossBarColor.RED, BossEvent.BossBarOverlay.NOTCHED_10);
     private int postRaidTicks;
     private int raidCooldownTicks;
     private final RandomSource random = RandomSource.create();
-    private final int numGroups;
+    public final int numGroups;
     private Raid.RaidStatus status;
     private int celebrationTicks;
     private Optional<BlockPos> waveSpawnPos = Optional.empty();
@@ -128,6 +_,7 @@
         this.center = center;
         this.numGroups = this.getNumGroups(difficulty);
         this.status = Raid.RaidStatus.ONGOING;
+        this.persistentDataContainer = new org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer(PDC_TYPE_REGISTRY); // Paper - add persistent data container
     }
 
     private Raid(
@@ -143,6 +_,7 @@
         Raid.RaidStatus status,
         BlockPos center,
         Set<UUID> heroesOfTheVillage
+        , final Optional<org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer> persistentDataContainer // Paper - add persistent data container
     ) {
         this.started = started;
         this.active = active;
@@ -156,6 +_,7 @@
         this.numGroups = numGroups;
         this.status = status;
         this.heroesOfTheVillage.addAll(heroesOfTheVillage);
+        this.persistentDataContainer = persistentDataContainer.orElseGet(() -> new org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer(PDC_TYPE_REGISTRY)); // Paper - add persistent data container
     }
 
     public boolean isOver() {
@@ -182,6 +_,12 @@
         return this.status == Raid.RaidStatus.LOSS;
     }
 
+    // CraftBukkit start
+    public boolean isInProgress() {
+        return this.status == RaidStatus.ONGOING;
+    }
+    // CraftBukkit end
+
     public float getTotalHealth() {
         return this.totalHealth;
     }
@@ -268,6 +_,7 @@
                 boolean flag = this.active;
                 this.active = level.hasChunkAt(this.center);
                 if (level.getDifficulty() == Difficulty.PEACEFUL) {
+                    org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(level, this, org.bukkit.event.raid.RaidStopEvent.Reason.PEACE); // CraftBukkit
                     this.stop();
                     return;
                 }
@@ -287,13 +_,16 @@
                 if (!level.isVillage(this.center)) {
                     if (this.groupsSpawned > 0) {
                         this.status = Raid.RaidStatus.LOSS;
+                        org.bukkit.craftbukkit.event.CraftEventFactory.callRaidFinishEvent(level, this, new java.util.ArrayList<>()); // CraftBukkit
                     } else {
+                        org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(level, this, org.bukkit.event.raid.RaidStopEvent.Reason.NOT_IN_VILLAGE); // CraftBukkit
                         this.stop();
                     }
                 }
 
                 this.ticksActive++;
                 if (this.ticksActive >= 48000L) {
+                    org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(level, this, org.bukkit.event.raid.RaidStopEvent.Reason.TIMEOUT); // CraftBukkit
                     this.stop();
                     return;
                 }
@@ -382,6 +_,7 @@
                     }
 
                     if (i > 5) {
+                        org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(level, this, org.bukkit.event.raid.RaidStopEvent.Reason.UNSPAWNABLE); // CraftBukkit
                         this.stop();
                         break;
                     }
@@ -393,6 +_,7 @@
                     } else {
                         this.status = Raid.RaidStatus.VICTORY;
 
+                        List<org.bukkit.entity.Player> winners = new java.util.ArrayList<>(); // CraftBukkit
                         for (UUID uuid : this.heroesOfTheVillage) {
                             Entity entity = level.getEntity(uuid);
                             if (entity instanceof LivingEntity livingEntity && !entity.isSpectator()) {
@@ -400,9 +_,11 @@
                                 if (livingEntity instanceof ServerPlayer serverPlayer) {
                                     serverPlayer.awardStat(Stats.RAID_WIN);
                                     CriteriaTriggers.RAID_WIN.trigger(serverPlayer);
+                                    winners.add(serverPlayer.getBukkitEntity()); // CraftBukkit
                                 }
                             }
                         }
+                        org.bukkit.craftbukkit.event.CraftEventFactory.callRaidFinishEvent(level, this, winners); // CraftBukkit
                     }
                 }
 
@@ -410,6 +_,7 @@
             } else if (this.isOver()) {
                 this.celebrationTicks++;
                 if (this.celebrationTicks >= 600) {
+                    org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(level, this, org.bukkit.event.raid.RaidStopEvent.Reason.FINISHED); // CraftBukkit
                     this.stop();
                     return;
                 }
@@ -514,7 +_,7 @@
 
     private void spawnGroup(ServerLevel level, BlockPos pos) {
         boolean flag = false;
-        int i = this.groupsSpawned + 1;
+        int i = this.groupsSpawned + 1; final int wave = i; // Paper - OBFHELPER
         this.totalHealth = 0.0F;
         DifficultyInstance currentDifficultyAt = level.getCurrentDifficultyAt(pos);
         boolean shouldSpawnBonusGroup = this.shouldSpawnBonusGroup();
@@ -563,6 +_,7 @@
         this.groupsSpawned++;
         this.updateBossbar();
         this.setDirty(level);
+        org.bukkit.craftbukkit.event.CraftEventFactory.callRaidSpawnWaveEvent(level, this, java.util.Objects.requireNonNull(this.getLeader(wave)), this.groupRaiderMap.get(wave)); // CraftBukkit
     }
 
     public void joinRaid(ServerLevel level, int wave, Raider raider, @Nullable BlockPos pos, boolean isRecruited) {
@@ -577,7 +_,7 @@
                 raider.finalizeSpawn(level, level.getCurrentDifficultyAt(pos), EntitySpawnReason.EVENT, null);
                 raider.applyRaidBuffs(level, wave, false);
                 raider.setOnGround(true);
-                level.addFreshEntityWithPassengers(raider);
+                level.addFreshEntityWithPassengers(raider, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.RAID); // CraftBukkit
             }
         }
     }
@@ -796,6 +_,12 @@
     public void addHeroOfTheVillage(Entity player) {
         this.heroesOfTheVillage.add(player.getUUID());
     }
+
+    // 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
 
     static enum RaidStatus implements StringRepresentable {
         ONGOING("ongoing"),
