--- a/net/minecraft/world/entity/animal/horse/AbstractHorse.java
+++ b/net/minecraft/world/entity/animal/horse/AbstractHorse.java
@@ -123,6 +_,7 @@
     protected int gallopSoundCounter;
     @Nullable
     public EntityReference<LivingEntity> owner;
+    public int maxDomestication = 100; // CraftBukkit - store max domestication value
 
     protected AbstractHorse(EntityType<? extends AbstractHorse> entityType, Level level) {
         super(entityType, level);
@@ -252,7 +_,7 @@
     }
 
     @Override
-    public boolean isPushable() {
+    public boolean isCollidable(boolean ignoreClimbing) { // Paper - Climbing should not bypass cramming gamerule
         return !this.isVehicle();
     }
 
@@ -303,7 +_,7 @@
 
     public void createInventory() {
         SimpleContainer simpleContainer = this.inventory;
-        this.inventory = new SimpleContainer(this.getInventorySize());
+        this.inventory = new SimpleContainer(this.getInventorySize(), (org.bukkit.entity.AbstractHorse) this.getBukkitEntity()); // CraftBukkit
         if (simpleContainer != null) {
             int min = Math.min(simpleContainer.getContainerSize(), this.inventory.getContainerSize());
 
@@ -397,7 +_,7 @@
     }
 
     public int getMaxTemper() {
-        return 100;
+        return this.maxDomestication; // CraftBukkit - return stored max domestication instead
     }
 
     @Override
@@ -456,7 +_,7 @@
             i1 = 5;
             if (!this.level().isClientSide && this.isTamed() && this.getAge() == 0 && !this.isInLove()) {
                 flag = true;
-                this.setInLove(player);
+                this.setInLove(player, stack.copy()); // Paper - Fix EntityBreedEvent copying
             }
         } else if (stack.is(Items.GOLDEN_APPLE) || stack.is(Items.ENCHANTED_GOLDEN_APPLE)) {
             f = 10.0F;
@@ -464,12 +_,12 @@
             i1 = 10;
             if (!this.level().isClientSide && this.isTamed() && this.getAge() == 0 && !this.isInLove()) {
                 flag = true;
-                this.setInLove(player);
+                this.setInLove(player, stack.copy()); // Paper - Fix EntityBreedEvent copying
             }
         }
 
         if (this.getHealth() < this.getMaxHealth() && f > 0.0F) {
-            this.heal(f);
+            this.heal(f, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.EATING); // CraftBukkit
             flag = true;
         }
 
@@ -540,7 +_,7 @@
         super.aiStep();
         if (this.level() instanceof ServerLevel serverLevel && this.isAlive()) {
             if (this.random.nextInt(900) == 0 && this.deathTime == 0) {
-                this.heal(1.0F);
+                this.heal(1.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.REGEN); // CraftBukkit
             }
 
             if (this.canEatGrass()) {
@@ -642,6 +_,16 @@
         }
     }
 
+    // Paper start - Horse API
+    public void setMouthOpen(boolean open) {
+        this.setFlag(FLAG_OPEN_MOUTH, open);
+    }
+
+    public boolean isMouthOpen() {
+        return this.getFlag(FLAG_OPEN_MOUTH);
+    }
+    // Paper end - Horse API
+
     @Override
     public InteractionResult mobInteract(Player player, InteractionHand hand) {
         if (this.isVehicle() || this.isBaby()) {
@@ -788,6 +_,7 @@
         output.putInt("Temper", this.getTemper());
         output.putBoolean("Tame", this.isTamed());
         EntityReference.store(this.owner, output, "Owner");
+        output.putInt("Bukkit.MaxDomestication", this.maxDomestication); // Paper - max domestication
     }
 
     @Override
@@ -798,6 +_,7 @@
         this.setTemper(input.getIntOr("Temper", 0));
         this.setTamed(input.getBooleanOr("Tame", false));
         this.owner = EntityReference.readWithOldOwnerConversion(input, "Owner", this.level());
+        this.maxDomestication = input.getIntOr("Bukkit.MaxDomestication", this instanceof Llama ? 30 : 100); // Paper - max domestication
     }
 
     @Override
@@ -886,6 +_,17 @@
 
     @Override
     public void handleStartJump(int jumpPower) {
+        // CraftBukkit start
+        float power;
+        if (jumpPower >= 90) {
+            power = 1.0F;
+        } else {
+            power = 0.4F + 0.4F * (float) jumpPower / 90.0F;
+        }
+        if (!org.bukkit.craftbukkit.event.CraftEventFactory.callHorseJumpEvent(this, power)) {
+            return;
+        }
+        // CraftBukkit end
         this.allowStandSliding = true;
         this.standIfPossible();
         this.playJumpSound();
