--- a/net/minecraft/world/entity/projectile/ThrownEnderpearl.java
+++ b/net/minecraft/world/entity/projectile/ThrownEnderpearl.java
@@ -58,15 +_,11 @@
     }
 
     private void deregisterFromCurrentOwner() {
-        if (this.getOwner() instanceof ServerPlayer serverPlayer) {
-            serverPlayer.deregisterEnderPearl(this);
-        }
+        // Folia - region threading - we remove the registration logic, we do not need to fetch the owner
     }
 
     private void registerToCurrentOwner() {
-        if (this.getOwner() instanceof ServerPlayer serverPlayer) {
-            serverPlayer.registerEnderPearl(this);
-        }
+        // Folia - region threading - we remove the registration logic, we do not need to fetch the owner
     }
 
     @Nullable
@@ -99,6 +_,81 @@
         result.getEntity().hurt(this.damageSources().thrown(this, this.getOwner()), 0.0F);
     }
 
+    // Folia start - region threading
+    private static void attemptTeleport(Entity source, ServerLevel checkWorld, net.minecraft.world.phys.Vec3 to) {
+        final boolean onPortalCooldown = source.isOnPortalCooldown();
+        // ignore retired callback, in those cases we do not want to teleport
+        source.getBukkitEntity().taskScheduler.schedule(
+            (Entity entity) -> {
+                if (!isAllowedToTeleportOwner(entity, checkWorld)) {
+                    return;
+                }
+                // source is now an invalid reference, do not use it, use the entity parameter
+                net.minecraft.world.phys.Vec3 endermitePos = entity.position();
+
+                // dismount from any vehicles, so we can teleport and to prevent desync
+                if (entity.isPassenger()) {
+                    entity.unRide();
+                }
+
+                if (onPortalCooldown) {
+                    entity.setPortalCooldown();
+                }
+
+                entity.teleportAsync(
+                    checkWorld, to, null, null, null,
+                    org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.ENDER_PEARL,
+                    // chunk could have been unloaded
+                    Entity.TELEPORT_FLAG_TELEPORT_PASSENGERS | Entity.TELEPORT_FLAG_LOAD_CHUNK,
+                    (Entity teleported) -> {
+                        // entity is now an invalid reference, do not use it, instead use teleported
+                        if (teleported instanceof ServerPlayer player) {
+                            // connection teleport is already done
+                            ServerLevel world = player.serverLevel();
+
+                            // endermite spawn chance
+                            if (world.random.nextFloat() < 0.05F && world.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) {
+                                Endermite entityendermite = (Endermite) EntityType.ENDERMITE.create(world, EntitySpawnReason.TRIGGERED);
+
+                                if (entityendermite != null) {
+                                    float yRot = teleported.getYRot();
+                                    float xRot = teleported.getXRot();
+                                    Runnable spawn = () -> {
+                                        entityendermite.moveTo(endermitePos.x, endermitePos.y, endermitePos.z, yRot, xRot);
+                                        world.addFreshEntity(entityendermite, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.ENDER_PEARL);
+                                    };
+
+                                    if (ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(world, endermitePos, net.minecraft.world.phys.Vec3.ZERO, 1)) {
+                                        spawn.run();
+                                    } else {
+                                        io.papermc.paper.threadedregions.RegionizedServer.getInstance().taskQueue.queueTickTaskQueue(
+                                            world,
+                                            ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkCoordinate(endermitePos.x),
+                                            ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkCoordinate(endermitePos.z),
+                                            spawn
+                                        );
+                                    }
+                                }
+                            }
+
+                            // damage player
+                            teleported.resetFallDistance();
+                            player.resetCurrentImpulseContext();
+                            player.hurtServer(player.serverLevel(), player.damageSources().enderPearl().customEventDamager(player), 5.0F); // CraftBukkit // Paper - fix DamageSource API
+                            playSound(teleported.level(), to);
+                        } else {
+                            // reset fall damage so that if the entity was falling they do not instantly die
+                            teleported.resetFallDistance();
+                            playSound(teleported.level(), to);
+                        }
+                    }
+                );
+            },
+            null, 1L
+        );
+    }
+    // Folia end - region threading
+
     @Override
     protected void onHit(HitResult result) {
         super.onHit(result);
@@ -117,6 +_,20 @@
         }
 
         if (this.level() instanceof ServerLevel serverLevel && !this.isRemoved()) {
+            // Folia start - region threading
+            if (true) {
+                // we can't fire events, because we do not actually know where the other entity is located
+                if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this)) {
+                    throw new IllegalStateException("Must be on tick thread for ticking entity: " + this);
+                }
+                Entity entity = this.getOwnerRaw();
+                if (entity != null) {
+                    attemptTeleport(entity, (ServerLevel)this.level(), this.position());
+                }
+                this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.HIT);
+                return;
+            }
+            // Folia end - region threading
             Entity owner = this.getOwner();
             if (owner != null && isAllowedToTeleportOwner(owner, serverLevel)) {
                 if (owner.isPassenger()) {
@@ -212,7 +_,15 @@
         }
     }
 
-    private void playSound(Level level, Vec3 pos) {
+    // Folia start - region threading
+    @Override
+    public void preChangeDimension() {
+        super.preChangeDimension();
+        // Don't change the owner here, since the tick logic will consider it anyways.
+    }
+    // Folia end - region threading
+
+    private static void playSound(Level level, Vec3 pos) { // Folia - region threading - static
         level.playSound(null, pos.x, pos.y, pos.z, SoundEvents.PLAYER_TELEPORT, SoundSource.PLAYERS);
     }
 
