diff --git a/nms-patches/PersistentRaid.patch b/nms-patches/PersistentRaid.patch new file mode 100644 index 0000000000..0de163c2a6 --- /dev/null +++ b/nms-patches/PersistentRaid.patch @@ -0,0 +1,46 @@ +--- a/net/minecraft/server/PersistentRaid.java ++++ b/net/minecraft/server/PersistentRaid.java +@@ -9,7 +9,7 @@ + + public class PersistentRaid extends PersistentBase { + +- private final Map a = Maps.newHashMap(); ++ public final Map a = Maps.newHashMap(); // PAIL rename raids, private -> public + private final WorldServer b; + private int c; + private int d; +@@ -92,19 +92,33 @@ + boolean flag = false; + + if (!raid.j()) { ++ /* CraftBukkit - moved down + if (!this.a.containsKey(raid.u())) { + this.a.put(raid.u(), raid); + } ++ */ + + flag = true; +- } else if (raid.m() < raid.l()) { ++ // CraftBukkit start - fixed a bug with raid: players could add up Bad Omen level even when the raid had finished ++ } else if (raid.isInProgress() && raid.m() < raid.l()) { + flag = true; ++ // CraftBukkit end + } else { + entityplayer.removeEffect(MobEffects.BAD_OMEN); + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityStatus(entityplayer, (byte) 43)); + } + + if (flag) { ++ // CraftBukkit start ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callRaidTriggerEvent(raid, entityplayer)) { ++ entityplayer.removeEffect(MobEffects.BAD_OMEN); ++ return null; ++ } ++ ++ if (!this.a.containsKey(raid.u())) { ++ this.a.put(raid.u(), raid); ++ } ++ // CraftBukkit end + raid.a((EntityHuman) entityplayer); + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityStatus(entityplayer, (byte) 43)); + if (!raid.c()) { diff --git a/nms-patches/Raid.patch b/nms-patches/Raid.patch new file mode 100644 index 0000000000..0e2c920d6f --- /dev/null +++ b/nms-patches/Raid.patch @@ -0,0 +1,155 @@ +--- a/net/minecraft/server/Raid.java ++++ b/net/minecraft/server/Raid.java +@@ -26,21 +26,21 @@ + private static final IChatBaseComponent e = Raid.a.g().a(" - ").addSibling(Raid.c); + private final Map f = Maps.newHashMap(); + private final Map> g = Maps.newHashMap(); +- private final Set h = Sets.newHashSet(); +- private long i; ++ public final Set h = Sets.newHashSet(); // PAIL rename heroes, private -> public ++ public long i; // PAIL rename activeTicks, private -> public + private BlockPosition j; + private final WorldServer k; + private boolean l; + private final int m; +- private float n; +- private int o; ++ public float n; // PAIL rename originTotalHealth, private -> public ++ public int o; // PAIL rename badOmenLevel, private -> public + private boolean p; + private int q; + private final BossBattleServer r; + private int s; + private int t; + private final Random u; +- private final int v; ++ public final int v; // PAIL rename totalWaves, private -> public + private Raid.Status w; + private int x; + private Optional y; +@@ -114,6 +114,12 @@ + return this.w == Raid.Status.LOSS; + } + ++ // CraftBukkit start ++ public boolean isInProgress() { ++ return this.w == Status.ONGOING; ++ } ++ // CraftBukkit end ++ + public World i() { + return this.k; + } +@@ -189,6 +195,7 @@ + + this.p = this.k.isLoaded(this.j); + if (this.k.getDifficulty() == EnumDifficulty.PEACEFUL) { ++ org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(this, org.bukkit.event.raid.RaidStopEvent.Reason.PEACE); // CraftBukkit + this.n(); + return; + } +@@ -208,13 +215,16 @@ + if (!this.k.b_(this.j)) { + if (this.q > 0) { + this.w = Raid.Status.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.n(); + } + } + + ++this.i; + if (this.i >= 48000L) { ++ org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(this, org.bukkit.event.raid.RaidStopEvent.Reason.TIMEOUT); // CraftBukkit + this.n(); + return; + } +@@ -288,6 +298,7 @@ + } + + if (j > 3) { ++ org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(this, org.bukkit.event.raid.RaidStopEvent.Reason.UNSPAWNABLE); // CraftBukkit + this.n(); + break; + } +@@ -300,6 +311,7 @@ + this.w = Raid.Status.VICTORY; + Iterator iterator = this.h.iterator(); + ++ List winners = new java.util.ArrayList<>(); // CraftBukkit + while (iterator.hasNext()) { + UUID uuid = (UUID) iterator.next(); + Entity entity = this.k.getEntity(uuid); +@@ -313,9 +325,11 @@ + + entityplayer.a(StatisticList.RAID_WIN); + CriterionTriggers.H.a(entityplayer); ++ winners.add(entityplayer.getBukkitEntity()); // CraftBukkit + } + } + } ++ org.bukkit.craftbukkit.event.CraftEventFactory.callRaidFinishEvent(this, winners); // CraftBukkit + } + } + +@@ -323,6 +337,7 @@ + } else if (this.a()) { + ++this.x; + if (this.x >= 600) { ++ org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(this, org.bukkit.event.raid.RaidStopEvent.Reason.FINISHED); // CraftBukkit + this.n(); + return; + } +@@ -456,6 +471,10 @@ + Raid.Wave[] araid_wave = Raid.Wave.f; + int j = araid_wave.length; + ++ // CraftBukkit start ++ EntityRaider leader = null; ++ List raiders = new java.util.ArrayList<>(); ++ // CraftBukkit end + for (int k = 0; k < j; ++k) { + Raid.Wave raid_wave = araid_wave[k]; + int l = this.a(raid_wave, i, flag1) + this.a(raid_wave, this.u, i, difficultydamagescaler, flag1); +@@ -468,9 +487,11 @@ + entityraider.setPatrolLeader(true); + this.a(i, entityraider); + flag = true; ++ leader = entityraider; // CraftBukkit + } + + this.a(i, entityraider, blockposition, false); ++ raiders.add(entityraider); // CraftBukkit + if (raid_wave.g == EntityTypes.RAVAGER) { + EntityRaider entityraider1 = null; + +@@ -489,6 +510,7 @@ + this.a(i, entityraider1, blockposition, false); + entityraider1.setPositionRotation(blockposition, 0.0F, 0.0F); + entityraider1.startRiding(entityraider); ++ raiders.add(entityraider); // CraftBukkit + } + } + } +@@ -498,6 +520,7 @@ + ++this.q; + this.p(); + this.H(); ++ org.bukkit.craftbukkit.event.CraftEventFactory.callRaidSpawnWaveEvent(this, leader, raiders); // CraftBukkit + } + + public void a(int i, EntityRaider entityraider, @Nullable BlockPosition blockposition, boolean flag) { +@@ -759,6 +782,12 @@ + this.h.add(entity.getUniqueID()); + } + ++ // CraftBukkit start - a method to get all raiders ++ public java.util.Collection getRaiders() { ++ return this.g.values().stream().flatMap(Set::stream).collect(java.util.stream.Collectors.toSet()); ++ } ++ // CraftBukkit end ++ + static enum Wave { + + VINDICATOR(EntityTypes.VINDICATOR, new int[]{0, 0, 2, 0, 1, 4, 2, 5}), EVOKER(EntityTypes.EVOKER, new int[]{0, 0, 0, 0, 0, 1, 1, 2}), PILLAGER(EntityTypes.PILLAGER, new int[]{0, 4, 3, 3, 4, 4, 4, 2}), WITCH(EntityTypes.WITCH, new int[]{0, 0, 0, 0, 3, 0, 0, 1}), RAVAGER(EntityTypes.RAVAGER, new int[]{0, 0, 0, 1, 0, 1, 0, 2}); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRaid.java b/src/main/java/org/bukkit/craftbukkit/CraftRaid.java new file mode 100644 index 0000000000..d17cdcb1d3 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftRaid.java @@ -0,0 +1,101 @@ +package org.bukkit.craftbukkit; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.function.Function; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.EntityRaider; +import net.minecraft.server.World; +import org.bukkit.Location; +import org.bukkit.Raid; +import org.bukkit.entity.Raider; + +public final class CraftRaid implements Raid { + + private final net.minecraft.server.Raid handle; + + public CraftRaid(net.minecraft.server.Raid handle) { + this.handle = handle; + } + + @Override + public boolean isStarted() { + return handle.j(); // PAIL rename isStarted + } + + @Override + public long getActiveTicks() { + return handle.i; + } + + @Override + public int getBadOmenLevel() { + return handle.o; + } + + @Override + public void setBadOmenLevel(int badOmenLevel) { + int max = handle.l(); // PAIL rename getMaxBadOmenLevel + Preconditions.checkArgument(0 <= badOmenLevel && badOmenLevel <= max, "Bad Omen level must be between 0 and %s", max); + handle.o = badOmenLevel; + } + + @Override + public Location getLocation() { + BlockPosition pos = handle.t(); // PAIL rename getCenterLocation + World world = handle.i(); // PAIL rename getWorld + return new Location(world.getWorld(), pos.getX(), pos.getY(), pos.getZ()); + } + + @Override + public RaidStatus getStatus() { + if (handle.d()) { // PAIL rename isStopped + return RaidStatus.STOPPED; + } else if (handle.e()) { // PAIL rename isVictory + return RaidStatus.VICTORY; + } else if (handle.f()) { // PAIL rename isLoss + return RaidStatus.LOSS; + } else { + return RaidStatus.ONGOING; + } + } + + @Override + public int getSpawnedGroups() { + return handle.k(); // PAIL rename countSpawnedGroups + } + + @Override + public int getTotalGroups() { + return handle.v + (handle.o > 1 ? 1 : 0); + } + + @Override + public int getTotalWaves() { + return handle.v; + } + + @Override + public float getTotalHealth() { + return handle.q(); // PAIL rename sumMobHealth + } + + @Override + public Set getHeroes() { + return Collections.unmodifiableSet(handle.h); + } + + @Override + public List getRaiders() { + return handle.getRaiders().stream().map(new Function() { + @Override + public Raider apply(EntityRaider entityRaider) { + return (Raider) entityRaider.getBukkitEntity(); + } + }).collect(ImmutableList.toImmutableList()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index a880fc85d4..7c895609dc 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -19,6 +19,7 @@ import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.function.Predicate; +import java.util.stream.Collectors; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.objects.ObjectSortedSet; import net.minecraft.server.AxisAlignedBB; @@ -77,6 +78,7 @@ import net.minecraft.server.MovingObjectPosition; import net.minecraft.server.PacketPlayOutCustomSoundEffect; import net.minecraft.server.PacketPlayOutUpdateTime; import net.minecraft.server.PacketPlayOutWorldEvent; +import net.minecraft.server.PersistentRaid; import net.minecraft.server.PlayerChunk; import net.minecraft.server.ProtoChunkExtension; import net.minecraft.server.RayTrace; @@ -88,7 +90,6 @@ import net.minecraft.server.Vec3D; import net.minecraft.server.WorldGenFeatureEmptyConfiguration; import net.minecraft.server.WorldGenHugeMushroomConfiguration; import net.minecraft.server.WorldGenerator; -import net.minecraft.server.WorldNBTStorage; import net.minecraft.server.WorldServer; import org.apache.commons.lang.Validate; import org.bukkit.BlockChangeDelegate; @@ -104,6 +105,7 @@ import org.bukkit.Particle; import org.bukkit.Sound; import org.bukkit.StructureType; import org.bukkit.TreeType; +import org.bukkit.Raid; import org.bukkit.World; import org.bukkit.WorldBorder; import org.bukkit.block.Biome; @@ -2145,4 +2147,20 @@ public class CraftWorld implements World { BlockPosition nearest = getHandle().getChunkProvider().getChunkGenerator().findNearestMapFeature(getHandle(), structureType.getName(), originPos, radius, findUnexplored); return (nearest == null) ? null : new Location(this, nearest.getX(), nearest.getY(), nearest.getZ()); } + + @Override + public Raid locateNearestRaid(Location location, int radius) { + Validate.notNull(location, "Location cannot be null"); + Validate.isTrue(radius >= 0, "Radius cannot be negative"); + + PersistentRaid persistentRaid = world.C(); // PAIL rename getPersistentRaid + net.minecraft.server.Raid raid = persistentRaid.a(new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ()), radius * radius); // PAIL rename getNearbyRaid + return (raid == null) ? null : new CraftRaid(raid); + } + + @Override + public List getRaids() { + PersistentRaid persistentRaid = world.C(); // PAIL rename getPersistentRaid + return persistentRaid.a.values().stream().map(CraftRaid::new).collect(Collectors.toList()); + } } diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java index 9331952385..3502cac6d9 100644 --- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java @@ -39,6 +39,7 @@ import net.minecraft.server.EntityPlayer; import net.minecraft.server.EntityPotion; import net.minecraft.server.EntitySheep; import net.minecraft.server.EntitySlime; +import net.minecraft.server.EntityRaider; import net.minecraft.server.EntityTypes; import net.minecraft.server.EntityVillager; import net.minecraft.server.EntityWaterAnimal; @@ -60,6 +61,7 @@ import net.minecraft.server.MovingObjectPositionBlock; import net.minecraft.server.MovingObjectPositionEntity; import net.minecraft.server.NPC; import net.minecraft.server.PacketPlayInCloseWindow; +import net.minecraft.server.Raid; import net.minecraft.server.Unit; import net.minecraft.server.World; import net.minecraft.server.WorldServer; @@ -70,6 +72,7 @@ import org.bukkit.Statistic.Type; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.CraftRaid; import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.CraftStatistic; import org.bukkit.craftbukkit.CraftWorld; @@ -78,6 +81,7 @@ import org.bukkit.craftbukkit.block.CraftBlockState; import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.craftbukkit.entity.CraftEntity; import org.bukkit.craftbukkit.entity.CraftLivingEntity; +import org.bukkit.craftbukkit.entity.CraftRaider; import org.bukkit.craftbukkit.entity.CraftPlayer; import org.bukkit.craftbukkit.inventory.CraftInventoryCrafting; import org.bukkit.craftbukkit.inventory.CraftItemStack; @@ -100,6 +104,7 @@ import org.bukkit.entity.Pig; import org.bukkit.entity.PigZombie; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; +import org.bukkit.entity.Raider; import org.bukkit.entity.Sheep; import org.bukkit.entity.ThrownExpBottle; import org.bukkit.entity.ThrownPotion; @@ -188,6 +193,10 @@ import org.bukkit.event.player.PlayerStatisticIncrementEvent; import org.bukkit.event.player.PlayerUnleashEntityEvent; import org.bukkit.event.server.ServerListPingEvent; import org.bukkit.event.vehicle.VehicleCreateEvent; +import org.bukkit.event.raid.RaidTriggerEvent; +import org.bukkit.event.raid.RaidFinishEvent; +import org.bukkit.event.raid.RaidStopEvent; +import org.bukkit.event.raid.RaidSpawnWaveEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.meta.BookMeta; @@ -1441,4 +1450,33 @@ public class CraftEventFactory { Bukkit.getPluginManager().callEvent(event); return event; } + + /** + * Raid events + */ + public static boolean callRaidTriggerEvent(Raid raid, EntityPlayer player) { + RaidTriggerEvent event = new RaidTriggerEvent(new CraftRaid(raid), raid.i().getWorld(), player.getBukkitEntity()); + Bukkit.getPluginManager().callEvent(event); + return !event.isCancelled(); + } + + public static void callRaidFinishEvent(Raid raid, List players) { + RaidFinishEvent event = new RaidFinishEvent(new CraftRaid(raid), raid.i().getWorld(), players); + Bukkit.getPluginManager().callEvent(event); + } + + public static void callRaidStopEvent(Raid raid, RaidStopEvent.Reason reason) { + RaidStopEvent event = new RaidStopEvent(new CraftRaid(raid), raid.i().getWorld(), reason); + Bukkit.getPluginManager().callEvent(event); + } + + public static void callRaidSpawnWaveEvent(Raid raid, EntityRaider leader, List raiders) { + Raider craftLeader = (CraftRaider) leader.getBukkitEntity(); + List craftRaiders = new ArrayList<>(); + for (EntityRaider entityRaider : raiders) { + craftRaiders.add((Raider) entityRaider.getBukkitEntity()); + } + RaidSpawnWaveEvent event = new RaidSpawnWaveEvent(new CraftRaid(raid), raid.i().getWorld(), craftLeader, craftRaiders); + Bukkit.getPluginManager().callEvent(event); + } }