From 4e52cd39203c6de10c2cf702c0ddc48ac06dd2c0 Mon Sep 17 00:00:00 2001 From: William Blake Galbreath Date: Mon, 6 Jul 2020 01:50:16 -0500 Subject: [PATCH] Start bringing back ridables (cows only for now) --- patches/api/0029-Ridables.patch | 89 ++++ patches/server/0103-Ridables.patch | 667 +++++++++++++++++++++++++++++ 2 files changed, 756 insertions(+) create mode 100644 patches/api/0029-Ridables.patch create mode 100644 patches/server/0103-Ridables.patch diff --git a/patches/api/0029-Ridables.patch b/patches/api/0029-Ridables.patch new file mode 100644 index 000000000..608b9629b --- /dev/null +++ b/patches/api/0029-Ridables.patch @@ -0,0 +1,89 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Sat, 4 May 2019 00:57:16 -0500 +Subject: [PATCH] Ridables + + +diff --git a/src/main/java/net/pl3x/purpur/event/entity/RidableSpacebarEvent.java b/src/main/java/net/pl3x/purpur/event/entity/RidableSpacebarEvent.java +new file mode 100644 +index 000000000..c0ec5a130 +--- /dev/null ++++ b/src/main/java/net/pl3x/purpur/event/entity/RidableSpacebarEvent.java +@@ -0,0 +1,37 @@ ++package net.pl3x.purpur.event.entity; ++ ++import org.bukkit.entity.Entity; ++import org.bukkit.event.Cancellable; ++import org.bukkit.event.HandlerList; ++import org.bukkit.event.entity.EntityEvent; ++import org.jetbrains.annotations.NotNull; ++ ++public class RidableSpacebarEvent extends EntityEvent implements Cancellable { ++ private static final HandlerList handlers = new HandlerList(); ++ private boolean cancelled; ++ ++ public RidableSpacebarEvent(@NotNull Entity entity) { ++ super(entity); ++ } ++ ++ @Override ++ public boolean isCancelled() { ++ return cancelled; ++ } ++ ++ @Override ++ public void setCancelled(boolean cancel) { ++ cancelled = cancel; ++ } ++ ++ @Override ++ @NotNull ++ public HandlerList getHandlers() { ++ return handlers; ++ } ++ ++ @NotNull ++ public static HandlerList getHandlerList() { ++ return handlers; ++ } ++} +diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java +index 7808ade92..5abf3eaf3 100644 +--- a/src/main/java/org/bukkit/entity/Entity.java ++++ b/src/main/java/org/bukkit/entity/Entity.java +@@ -695,4 +695,35 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent + */ + public boolean isInLava(); + // Paper end ++ ++ // Purpur start ++ /** ++ * Get the riding player ++ * ++ * @return Riding player ++ */ ++ @Nullable ++ Player getRider(); ++ ++ /** ++ * Check if entity is being ridden ++ * ++ * @return True if being ridden ++ */ ++ boolean hasRider(); ++ ++ /** ++ * Check if entity is ridable ++ * ++ * @return True if ridable ++ */ ++ boolean isRidable(); ++ ++ /** ++ * Check if entity is ridable in water ++ * ++ * @return True if ridable in water ++ */ ++ boolean isRidableInWater(); ++ // Purpur end + } diff --git a/patches/server/0103-Ridables.patch b/patches/server/0103-Ridables.patch new file mode 100644 index 000000000..bc3049896 --- /dev/null +++ b/patches/server/0103-Ridables.patch @@ -0,0 +1,667 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Sun, 5 Jul 2020 22:19:49 -0500 +Subject: [PATCH] Ridables + + +diff --git a/src/main/java/net/minecraft/server/ControllerMove.java b/src/main/java/net/minecraft/server/ControllerMove.java +index ac6f9d9e52..d874b177b4 100644 +--- a/src/main/java/net/minecraft/server/ControllerMove.java ++++ b/src/main/java/net/minecraft/server/ControllerMove.java +@@ -6,9 +6,9 @@ public class ControllerMove { + protected double b; + protected double c; + protected double d; +- protected double e; +- protected float f; +- protected float g; ++ protected double e; public double getSpeed() { return e; } public void setSpeed(double speed) { this.e = speed; } // Purpur - OBFHELPER ++ protected float f; public float getForward() { return f; } public void setForward(float forward) { this.f = forward; } // Purpur - OBFHELPER ++ protected float g; public float getStrafe() { return g; } public void setStrafe(float strafe) { this.g = strafe; } // Purpur - OBFHELPER + protected ControllerMove.Operation h; + + public ControllerMove(EntityInsentient entityinsentient) { +diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java +index fe1c6f0c1b..fcafc9e31e 100644 +--- a/src/main/java/net/minecraft/server/Entity.java ++++ b/src/main/java/net/minecraft/server/Entity.java +@@ -80,7 +80,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke + public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData; // Paper + private CraftEntity bukkitEntity; + +- PlayerChunkMap.EntityTracker tracker; // Paper ++ PlayerChunkMap.EntityTracker tracker; public PlayerChunkMap.EntityTracker getTracker() { return tracker; } // Paper // Purpur + boolean collisionLoadChunks = false; // Paper + Throwable addedToWorldStack; // Paper - entity debug + public CraftEntity getBukkitEntity() { +@@ -121,7 +121,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke + public float lastYaw; + public float lastPitch; + private AxisAlignedBB boundingBox; +- protected boolean onGround; ++ public boolean onGround; // Purpur - protected -> public + public boolean positionChanged; + public boolean v; + public boolean velocityChanged; +@@ -2213,6 +2213,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke + return this.a(entity, false); + } + ++ public boolean startRiding(Entity entity, boolean flag) { return a(entity, flag); } // Purpur - OBFHELPER + public boolean a(Entity entity, boolean flag) { + for (Entity entity1 = entity; entity1.vehicle != null; entity1 = entity1.vehicle) { + if (entity1.vehicle == this) { +@@ -2307,7 +2308,13 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke + } else { + this.passengers.add(entity); + } +- ++ // Purpur start ++ if (isRidable() && passengers.get(0) == entity && entity instanceof EntityHuman) { ++ EntityHuman entityhuman = (EntityHuman) entity; ++ onMount(entityhuman); ++ this.rider = entityhuman; ++ } ++ // Purpur end + } + return true; // CraftBukkit + } +@@ -2348,6 +2355,12 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke + return false; + } + // Spigot end ++ // Purpur start ++ if (rider != null && passengers.get(0) == rider) { ++ onDismount(rider); ++ this.rider = null; ++ } ++ // Purpur end + this.passengers.remove(entity); + entity.j = 60; + } +@@ -2739,6 +2752,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke + + public void setHeadRotation(float f) {} + ++ public void setBodyYaw(float yaw) { l(yaw); } // Purpur - OBFHELPER + public void l(float f) {} + + public boolean bH() { +@@ -3152,6 +3166,18 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke + return false; + } + ++ // Purpur Start ++ public void sendMessage(String str) { ++ sendMessage(org.bukkit.craftbukkit.util.CraftChatMessage.fromStringOrNull(str)); ++ } ++ ++ public void sendMessage(IChatBaseComponent ichatbasecomponent) { ++ if (ichatbasecomponent != null) { ++ sendMessage(ichatbasecomponent, SystemUtils.getNullUUID()); ++ } ++ } ++ // Purpur end ++ + @Override + public void sendMessage(IChatBaseComponent ichatbasecomponent, UUID uuid) {} + +@@ -3591,4 +3617,39 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke + + void accept(Entity entity, double d0, double d1, double d2); + } ++ ++ // Purpur start ++ private EntityHuman rider; ++ ++ public EntityHuman getRider() { ++ return rider; ++ } ++ ++ public boolean hasRider() { ++ return rider != null; ++ } ++ ++ public boolean isRidable() { ++ return false; ++ } ++ ++ public boolean isRidableInWater() { ++ return false; ++ } ++ ++ public void onMount(EntityHuman entityhuman) { ++ if (this instanceof EntityInsentient) { ++ ((EntityInsentient) this).setGoalTarget(null, null, false); ++ ((EntityInsentient) this).getNavigation().stopPathfinding(); ++ } ++ entityhuman.setJumping(false); // fixes jump on mount ++ } ++ ++ public void onDismount(EntityHuman entityhuman) { ++ } ++ ++ public boolean onSpacebar() { ++ return false; ++ } ++ // Purpur end + } +diff --git a/src/main/java/net/minecraft/server/EntityComplexPart.java b/src/main/java/net/minecraft/server/EntityComplexPart.java +index 920f4c7671..7fec83387a 100644 +--- a/src/main/java/net/minecraft/server/EntityComplexPart.java ++++ b/src/main/java/net/minecraft/server/EntityComplexPart.java +@@ -47,4 +47,11 @@ public class EntityComplexPart extends Entity { + public EntitySize a(EntityPose entitypose) { + return this.d; + } ++ ++ // Purpur start ++ @Override ++ public EnumInteractionResult a(EntityHuman entityhuman, EnumHand enumhand) { ++ return owner.isAlive() ? owner.tryRide(entityhuman, enumhand) : EnumInteractionResult.PASS; ++ } ++ // Purpur end + } +diff --git a/src/main/java/net/minecraft/server/EntityCow.java b/src/main/java/net/minecraft/server/EntityCow.java +index d6baddb9d3..404c548d26 100644 +--- a/src/main/java/net/minecraft/server/EntityCow.java ++++ b/src/main/java/net/minecraft/server/EntityCow.java +@@ -11,9 +11,22 @@ public class EntityCow extends EntityAnimal { + super(entitytypes, world); + } + ++ // Purpur start ++ @Override ++ public boolean isRidable() { ++ return world.purpurConfig.cowRidable; ++ } ++ ++ @Override ++ public boolean isRidableInWater() { ++ return world.purpurConfig.cowRidableInWater; ++ } ++ // Purpur end ++ + @Override + protected void initPathfinder() { + this.goalSelector.a(0, new PathfinderGoalFloat(this)); ++ this.goalSelector.a(0, new net.pl3x.purpur.pathfinder.PathfinderGoalHasRider(this)); // Purpur + this.goalSelector.a(1, new PathfinderGoalPanic(this, 2.0D)); + this.goalSelector.a(2, new PathfinderGoalBreed(this, 1.0D)); + if (world.purpurConfig.cowFeedMushrooms > 0) this.goalSelector.a(3, new PathfinderGoalTempt(this, 1.25D, RecipeItemStack.a(Items.WHEAT, Blocks.RED_MUSHROOM.getItem(), Blocks.BROWN_MUSHROOM.getItem()), false)); else // Purpur +@@ -62,7 +75,7 @@ public class EntityCow extends EntityAnimal { + org.bukkit.event.player.PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent((WorldServer) entityhuman.world, entityhuman, this.getChunkCoordinates(), this.getChunkCoordinates(), null, itemstack, Items.MILK_BUCKET, enumhand); // Paper - add enumHand + + if (event.isCancelled()) { +- return EnumInteractionResult.PASS; ++ return super.b(entityhuman, enumhand); + } + // CraftBukkit end + +@@ -73,7 +86,7 @@ public class EntityCow extends EntityAnimal { + return EnumInteractionResult.a(this.world.isClientSide); + // Purpur start - feed mushroom to change to mooshroom + } else if (world.purpurConfig.cowFeedMushrooms > 0 && getEntityType() != EntityTypes.MOOSHROOM && isMushroom(itemstack)) { +- return feedMushroom(entityhuman, itemstack); ++ return feedMushroom(entityhuman, enumhand, itemstack); + // Purpur end + } else { + return super.b(entityhuman, enumhand); +@@ -96,7 +109,7 @@ public class EntityCow extends EntityAnimal { + } + } + +- private EnumInteractionResult feedMushroom(EntityHuman entityhuman, ItemStack itemstack) { ++ private EnumInteractionResult feedMushroom(EntityHuman entityhuman, EnumHand enumhand, ItemStack itemstack) { + world.broadcastEntityEffect(this, (byte) 18); // hearts + playSound(SoundEffects.ENTITY_COW_MILK, 1.0F, 1.0F); + if (incrementFeedCount(itemstack) < world.purpurConfig.cowFeedMushrooms) { +@@ -107,7 +120,7 @@ public class EntityCow extends EntityAnimal { + } + EntityMushroomCow mooshroom = EntityTypes.MOOSHROOM.create(world); + if (mooshroom == null) { +- return EnumInteractionResult.PASS; ++ return super.b(entityhuman, enumhand); + } + if (itemstack.getItem() == Blocks.BROWN_MUSHROOM.getItem()) { + mooshroom.setVariant(EntityMushroomCow.Type.BROWN); +@@ -126,10 +139,10 @@ public class EntityCow extends EntityAnimal { + mooshroom.setCustomName(this.getCustomName()); + } + if (CraftEventFactory.callEntityTransformEvent(this, mooshroom, org.bukkit.event.entity.EntityTransformEvent.TransformReason.INFECTION).isCancelled()) { +- return EnumInteractionResult.PASS; ++ return super.b(entityhuman, enumhand); + } + if (!new com.destroystokyo.paper.event.entity.EntityTransformedEvent(this.getBukkitEntity(), mooshroom.getBukkitEntity(), com.destroystokyo.paper.event.entity.EntityTransformedEvent.TransformedReason.INFECTED).callEvent()) { +- return EnumInteractionResult.PASS; ++ return super.b(entityhuman, enumhand); + } + this.world.addEntity(mooshroom); + this.die(); +diff --git a/src/main/java/net/minecraft/server/EntityInsentient.java b/src/main/java/net/minecraft/server/EntityInsentient.java +index 8def5fbfb4..a64de2248b 100644 +--- a/src/main/java/net/minecraft/server/EntityInsentient.java ++++ b/src/main/java/net/minecraft/server/EntityInsentient.java +@@ -67,8 +67,8 @@ public abstract class EntityInsentient extends EntityLiving { + this.bH = -1.0F; + this.goalSelector = new PathfinderGoalSelector(world.getMethodProfilerSupplier()); + this.targetSelector = new PathfinderGoalSelector(world.getMethodProfilerSupplier()); +- this.lookController = new ControllerLook(this); +- this.moveController = new ControllerMove(this); ++ this.moveController = new net.pl3x.purpur.controller.ControllerMoveWASD(this); // Purpur ++ this.lookController = new net.pl3x.purpur.controller.ControllerLookWASD(this); // Purpur + this.bp = new ControllerJump(this); + this.c = this.r(); + this.navigation = this.b(world); +@@ -532,6 +532,7 @@ public abstract class EntityInsentient extends EntityLiving { + this.aY = f; + } + ++ public void setSpeed(float speed) { n(speed); } // Purpur - OBFHELPER + @Override + public void n(float f) { + super.n(f); +@@ -1200,7 +1201,7 @@ public abstract class EntityInsentient extends EntityLiving { + protected void a(EntityHuman entityhuman, EntityInsentient entityinsentient) {} + + protected EnumInteractionResult b(EntityHuman entityhuman, EnumHand enumhand) { +- return EnumInteractionResult.PASS; ++ return tryRide(entityhuman, enumhand); // Purpur + } + + public boolean ew() { +@@ -1559,4 +1560,42 @@ public abstract class EntityInsentient extends EntityLiving { + this.world.getServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), UnleashReason.UNKNOWN)); // CraftBukkit + this.unleash(true, false); + } ++ ++ // Purpur start ++ public double getMaxY() { ++ return world.getHeight(); ++ } ++ ++ public EnumInteractionResult tryRide(EntityHuman entityhuman, EnumHand enumhand) { ++ if (!isRidable()) { ++ return EnumInteractionResult.PASS; ++ } ++ if (enumhand != EnumHand.MAIN_HAND) { ++ return EnumInteractionResult.PASS; ++ } ++ if (entityhuman.isSneaking()) { ++ return EnumInteractionResult.PASS; ++ } ++ if (!passengers.isEmpty() || entityhuman.isPassenger()) { ++ return EnumInteractionResult.PASS; ++ } ++ if (this instanceof EntityTameableAnimal) { ++ EntityTameableAnimal tameable = (EntityTameableAnimal) this; ++ if (tameable.isTamed() && !tameable.isOwner(entityhuman)) { ++ return EnumInteractionResult.PASS; ++ } ++ } ++ if (!entityhuman.getBukkitEntity().hasPermission("allow.ride." + getEntityType().getName())) { ++ entityhuman.sendMessage(net.pl3x.purpur.PurpurConfig.cannotRideMob); ++ return EnumInteractionResult.PASS; ++ } ++ entityhuman.yaw = this.yaw; ++ entityhuman.pitch = this.pitch; ++ if (entityhuman.startRiding(this)) { ++ return EnumInteractionResult.SUCCESS; ++ } else { ++ return EnumInteractionResult.PASS; ++ } ++ } ++ // Purpur end + } +diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java +index cb9fdfd114..22b13896d0 100644 +--- a/src/main/java/net/minecraft/server/EntityLiving.java ++++ b/src/main/java/net/minecraft/server/EntityLiving.java +@@ -458,7 +458,7 @@ public abstract class EntityLiving extends Entity { + + @Override + public boolean bp() { +- return false; ++ return isRidableInWater(); // Purpur + } + + protected void cT() { +@@ -2156,7 +2156,7 @@ public abstract class EntityLiving extends Entity { + return 0.42F * this.getBlockJumpFactor(); + } + +- protected void jump() { ++ public void jump() { // Purpur - protected -> public + float f = this.dI(); + + if (this.hasEffect(MobEffects.JUMP)) { +diff --git a/src/main/java/net/minecraft/server/EntityRabbit.java b/src/main/java/net/minecraft/server/EntityRabbit.java +index 1be0f943b0..8a8e745846 100644 +--- a/src/main/java/net/minecraft/server/EntityRabbit.java ++++ b/src/main/java/net/minecraft/server/EntityRabbit.java +@@ -60,7 +60,7 @@ public class EntityRabbit extends EntityAnimal { + } + + @Override +- protected void jump() { ++ public void jump() { // Purpur - protected -> public + super.jump(); + double d0 = this.moveController.c(); + +diff --git a/src/main/java/net/minecraft/server/EntitySlime.java b/src/main/java/net/minecraft/server/EntitySlime.java +index a4642cc739..ecf2059b00 100644 +--- a/src/main/java/net/minecraft/server/EntitySlime.java ++++ b/src/main/java/net/minecraft/server/EntitySlime.java +@@ -324,7 +324,7 @@ public class EntitySlime extends EntityInsentient implements IMonster { + } + + @Override +- protected void jump() { ++ public void jump() { // Purpur - protected -> public + Vec3D vec3d = this.getMot(); + + this.setMot(vec3d.x, (double) this.dI(), vec3d.z); +diff --git a/src/main/java/net/minecraft/server/EntityTameableAnimal.java b/src/main/java/net/minecraft/server/EntityTameableAnimal.java +index 9b0d937f06..0b0cca4e35 100644 +--- a/src/main/java/net/minecraft/server/EntityTameableAnimal.java ++++ b/src/main/java/net/minecraft/server/EntityTameableAnimal.java +@@ -130,6 +130,7 @@ public abstract class EntityTameableAnimal extends EntityAnimal { + return this.j(entityliving) ? false : super.d(entityliving); + } + ++ public boolean isOwner(EntityLiving entityLiving) { return j(entityLiving); } // Purpur - OBFHELPER + public boolean j(EntityLiving entityliving) { + return entityliving == this.getOwner(); + } +diff --git a/src/main/java/net/pl3x/purpur/PurpurConfig.java b/src/main/java/net/pl3x/purpur/PurpurConfig.java +index 494d36085e..9cde3693c5 100644 +--- a/src/main/java/net/pl3x/purpur/PurpurConfig.java ++++ b/src/main/java/net/pl3x/purpur/PurpurConfig.java +@@ -132,10 +132,12 @@ public class PurpurConfig { + public static String afkBroadcastAway = "§e§o%s is now AFK"; + public static String afkBroadcastBack = "§e§o%s is no longer AFK"; + public static String pingCommandOutput = "§a%s's ping is %sms"; ++ public static String cannotRideMob = "§cYou cannot mount that mob"; + private static void messages() { + afkBroadcastAway = getString("settings.messages.afk-broadcast-away", afkBroadcastAway); + afkBroadcastBack = getString("settings.messages.afk-broadcast-back", afkBroadcastBack); + pingCommandOutput = getString("settings.messages.ping-command-output", pingCommandOutput); ++ cannotRideMob = getString("settings.messages.cannot-ride-mob", cannotRideMob); + } + + public static String serverModName = "Purpur"; +diff --git a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java +index 2f5bc77e03..ebc9a4f344 100644 +--- a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java ++++ b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java +@@ -281,8 +281,12 @@ public class PurpurWorldConfig { + } + + public int cowFeedMushrooms = 0; ++ public boolean cowRidable = false; ++ public boolean cowRidableInWater = false; + private void cowSettings() { + cowFeedMushrooms = getInt("mobs.cow.feed-mushrooms-for-mooshroom", cowFeedMushrooms); ++ cowRidable = getBoolean("mobs.cow.ridable", cowRidable); ++ cowRidableInWater = getBoolean("mobs.cow.ridable-in-water", cowRidableInWater); + } + + public boolean creeperAllowGriefing = true; +diff --git a/src/main/java/net/pl3x/purpur/controller/ControllerLookWASD.java b/src/main/java/net/pl3x/purpur/controller/ControllerLookWASD.java +new file mode 100644 +index 0000000000..828e1b8730 +--- /dev/null ++++ b/src/main/java/net/pl3x/purpur/controller/ControllerLookWASD.java +@@ -0,0 +1,75 @@ ++package net.pl3x.purpur.controller; ++ ++import net.minecraft.server.ControllerLook; ++import net.minecraft.server.EntityHuman; ++import net.minecraft.server.EntityInsentient; ++import net.minecraft.server.MathHelper; ++import net.minecraft.server.PacketPlayOutEntity; ++ ++public class ControllerLookWASD extends ControllerLook { ++ protected final EntityInsentient entity; ++ private float yawOffset = 0; ++ private float pitchOffset = 0; ++ ++ public ControllerLookWASD(EntityInsentient entity) { ++ super(entity); ++ this.entity = entity; ++ } ++ ++ // tick ++ @Override ++ public void a() { ++ if (entity.hasRider()) { ++ tick(entity.getRider()); ++ } else { ++ tick(); ++ } ++ } ++ ++ protected void tick() { ++ super.a(); // tick ++ } ++ ++ protected void tick(EntityHuman rider) { ++ setYawPitch(rider.yaw, rider.pitch); ++ } ++ ++ public void setYawPitch(float yaw, float pitch) { ++ entity.yaw = normalizeYaw(yaw + yawOffset); ++ entity.lastYaw = entity.yaw; ++ entity.setBodyYaw(entity.yaw); ++ entity.setHeadRotation(entity.yaw); ++ entity.pitch = normalizePitch(pitch + pitchOffset); ++ ++ entity.getTracker().broadcast(new PacketPlayOutEntity ++ .PacketPlayOutRelEntityMoveLook(entity.getId(), ++ (short) 0, (short) 0, (short) 0, ++ (byte) MathHelper.d(entity.yaw * 256.0F / 360.0F), ++ (byte) MathHelper.d(entity.pitch * 256.0F / 360.0F), ++ entity.onGround)); ++ } ++ ++ public void setOffsets(float yaw, float pitch) { ++ yawOffset = yaw; ++ pitchOffset = pitch; ++ } ++ ++ public float normalizeYaw(float yaw) { ++ yaw %= 360.0f; ++ if (yaw >= 180.0f) { ++ yaw -= 360.0f; ++ } else if (yaw < -180.0f) { ++ yaw += 360.0f; ++ } ++ return yaw; ++ } ++ ++ public float normalizePitch(float pitch) { ++ if (pitch > 90.0f) { ++ pitch = 90.0f; ++ } else if (pitch < -90.0f) { ++ pitch = -90.0f; ++ } ++ return pitch; ++ } ++} +diff --git a/src/main/java/net/pl3x/purpur/controller/ControllerMoveWASD.java b/src/main/java/net/pl3x/purpur/controller/ControllerMoveWASD.java +new file mode 100644 +index 0000000000..6086f63119 +--- /dev/null ++++ b/src/main/java/net/pl3x/purpur/controller/ControllerMoveWASD.java +@@ -0,0 +1,86 @@ ++package net.pl3x.purpur.controller; ++ ++import net.minecraft.server.ControllerMove; ++import net.minecraft.server.Entity; ++import net.minecraft.server.EntityHuman; ++import net.minecraft.server.EntityInsentient; ++import net.minecraft.server.GenericAttributes; ++import net.pl3x.purpur.event.entity.RidableSpacebarEvent; ++ ++public class ControllerMoveWASD extends ControllerMove { ++ protected final EntityInsentient entity; ++ ++ public ControllerMoveWASD(EntityInsentient entity) { ++ super(entity); ++ this.entity = entity; ++ } ++ ++ // isUpdating ++ @Override ++ public boolean b() { ++ return entity.hasRider() ? getForward() != 0 || getStrafe() != 0 : super.b(); ++ } ++ ++ // tick ++ @Override ++ public void a() { ++ if (entity.hasRider()) { ++ tick(entity.getRider()); ++ } else { ++ tick(); ++ } ++ } ++ ++ protected void tick() { ++ super.a(); // tick ++ } ++ ++ protected void tick(EntityHuman rider) { ++ float forward = rider.getForward() * 0.5F; ++ float strafe = rider.getStrafe() * 0.25F; ++ ++ if (forward <= 0.0F) { ++ forward *= 0.5F; ++ } ++ ++ float yawOffset = 0; ++ if (strafe != 0) { ++ if (forward == 0) { ++ yawOffset += strafe > 0 ? -90 : 90; ++ forward = Math.abs(strafe * 2); ++ } else { ++ yawOffset += strafe > 0 ? -30 : 30; ++ strafe /= 2; ++ if (forward < 0) { ++ yawOffset += strafe > 0 ? -110 : 110; ++ forward *= -1; ++ } ++ } ++ } else if (forward < 0) { ++ yawOffset -= 180; ++ forward *= -1; ++ } ++ ++ ((ControllerLookWASD) entity.getControllerLook()).setOffsets(yawOffset, 0); ++ ++ if (rider.jumping && spacebarEvent(entity) && !entity.onSpacebar() && entity.onGround) { ++ entity.jump(); ++ } ++ ++ setSpeed(entity.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue()); ++ ++ entity.setSpeed((float) getSpeed()); ++ entity.setForward(forward); ++ ++ setForward(entity.getForward()); ++ setStrafe(entity.getStrafe()); ++ } ++ ++ public static boolean spacebarEvent(Entity entity) { ++ if (RidableSpacebarEvent.getHandlerList().getRegisteredListeners().length > 0) { ++ return new RidableSpacebarEvent(entity.getBukkitEntity()).callEvent(); ++ } else { ++ return true; ++ } ++ } ++} +diff --git a/src/main/java/net/pl3x/purpur/pathfinder/PathfinderGoalHasRider.java b/src/main/java/net/pl3x/purpur/pathfinder/PathfinderGoalHasRider.java +new file mode 100644 +index 0000000000..6e50344c07 +--- /dev/null ++++ b/src/main/java/net/pl3x/purpur/pathfinder/PathfinderGoalHasRider.java +@@ -0,0 +1,21 @@ ++package net.pl3x.purpur.pathfinder; ++ ++import net.minecraft.server.Entity; ++import net.minecraft.server.PathfinderGoal; ++ ++import java.util.EnumSet; ++ ++public class PathfinderGoalHasRider extends PathfinderGoal { ++ public final Entity entity; ++ ++ public PathfinderGoalHasRider(Entity entity) { ++ this.entity = entity; ++ setTypes(EnumSet.of(Type.JUMP, Type.MOVE, Type.LOOK, Type.TARGET)); ++ } ++ ++ // shouldExecute ++ @Override ++ public boolean a() { ++ return entity.hasRider(); ++ } ++} +diff --git a/src/main/java/net/pl3x/purpur/pathfinder/PathfinderGoalHorseHasRider.java b/src/main/java/net/pl3x/purpur/pathfinder/PathfinderGoalHorseHasRider.java +new file mode 100644 +index 0000000000..fb18f72a6a +--- /dev/null ++++ b/src/main/java/net/pl3x/purpur/pathfinder/PathfinderGoalHorseHasRider.java +@@ -0,0 +1,21 @@ ++package net.pl3x.purpur.pathfinder; ++ ++import net.minecraft.server.EntityHorseAbstract; ++import net.minecraft.server.PathfinderGoal; ++ ++import java.util.EnumSet; ++ ++public class PathfinderGoalHorseHasRider extends PathfinderGoal { ++ public final EntityHorseAbstract entity; ++ ++ public PathfinderGoalHorseHasRider(EntityHorseAbstract entity) { ++ this.entity = entity; ++ setTypes(EnumSet.of(Type.JUMP, Type.MOVE, Type.LOOK, Type.TARGET)); ++ } ++ ++ // shouldExecute ++ @Override ++ public boolean a() { ++ return super.a() && entity.hasSaddle(); ++ } ++} +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +index 309e7f6ee7..2d9320b059 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +@@ -1125,4 +1125,26 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { + return getHandle().isInLava(); + } + // Paper end ++ ++ // Purpur start ++ @Override ++ public org.bukkit.entity.Player getRider() { ++ return hasRider() ? (org.bukkit.entity.Player) getHandle().getRider().getBukkitEntity() : null; ++ } ++ ++ @Override ++ public boolean hasRider() { ++ return getHandle().hasRider(); ++ } ++ ++ @Override ++ public boolean isRidable() { ++ return getHandle().isRidable(); ++ } ++ ++ @Override ++ public boolean isRidableInWater() { ++ return getHandle().isRidableInWater(); ++ } ++ // Purpur end + }