From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Byteflux <byte@byteflux.net>
Date: Tue, 30 Jun 2015 20:45:24 -0700
Subject: [PATCH] Force load chunks for specific entities that fly through


diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
@@ -0,0 +0,0 @@ public class ChunkProviderServer implements IChunkProvider {
             return;
         }
         // PaperSpigot end
+        // PaperSpigot start - Don't unload chunk if it contains an entity that loads chunks
+        if (chunk != null) {
+            for (List<Entity> entities : chunk.entitySlices) {
+                for (Entity entity : entities) {
+                    if (entity.loadChunks) {
+                        return;
+                    }
+                }
+            }
+        }
+        // PaperSpigot end
         if (this.world.worldProvider.e()) {
             if (!this.world.c(i, j)) {
                 // CraftBukkit start
diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/Entity.java
+++ b/src/main/java/net/minecraft/server/Entity.java
@@ -0,0 +0,0 @@ public abstract class Entity implements ICommandListener {
     public org.bukkit.projectiles.ProjectileSource projectileSource; // CraftBukkit - For projectiles only
     public boolean forceExplosionKnockback; // CraftBukkit - SPIGOT-949
     public boolean inUnloadedChunk = false; // PaperSpigot - Remove entities in unloaded chunks
+    public boolean loadChunks = false; // PaperSpigot - Entities can load chunks they move through and keep them loaded
 
     // Spigot start
     public CustomTimingsHandler tickTimer = org.bukkit.craftbukkit.SpigotTimings.getEntityTimings(this); // Spigot
@@ -0,0 +0,0 @@ public abstract class Entity implements ICommandListener {
         return this.world.getCubes(this, axisalignedbb).isEmpty() && !this.world.containsLiquid(axisalignedbb);
     }
 
+    /**
+     * PaperSpigot - Load surrounding chunks the entity is moving through
+     */
+    public void loadChunks() {
+        for (int cx = (int) locX >> 4; cx <= (int) (locX + motX) >> 4; ++cx) {
+            for (int cz = (int) locZ >> 4; cz <= (int) (locZ + motZ) >> 4; ++cz) {
+                ((ChunkProviderServer) world.chunkProvider).getChunkAt(cx, cz);
+            }
+        }
+    }
+
+
     public void move(double d0, double d1, double d2) {
         org.bukkit.craftbukkit.SpigotTimings.entityMoveTimer.startTiming(); // Spigot
+        if (this.loadChunks) loadChunks(); // PaperSpigot - Load chunks
         if (this.noclip) {
             this.a(this.getBoundingBox().c(d0, d1, d2));
             this.recalcPosition();
diff --git a/src/main/java/net/minecraft/server/EntityEnderPearl.java b/src/main/java/net/minecraft/server/EntityEnderPearl.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/EntityEnderPearl.java
+++ b/src/main/java/net/minecraft/server/EntityEnderPearl.java
@@ -0,0 +0,0 @@ public class EntityEnderPearl extends EntityProjectile {
 
     public EntityEnderPearl(World world) {
         super(world);
+        this.loadChunks = world.paperSpigotConfig.loadUnloadedEnderPearls; // PaperSpigot
     }
 
     public EntityEnderPearl(World world, EntityLiving entityliving) {
         super(world, entityliving);
         this.c = entityliving;
+        this.loadChunks = world.paperSpigotConfig.loadUnloadedEnderPearls; // PaperSpigot
     }
 
     protected void a(MovingObjectPosition movingobjectposition) {
diff --git a/src/main/java/net/minecraft/server/EntityFallingBlock.java b/src/main/java/net/minecraft/server/EntityFallingBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/EntityFallingBlock.java
+++ b/src/main/java/net/minecraft/server/EntityFallingBlock.java
@@ -0,0 +0,0 @@ public class EntityFallingBlock extends Entity {
     public EntityFallingBlock(org.bukkit.Location loc, World world) {
         super(world);
         sourceLoc = loc;
+        this.loadChunks = world.paperSpigotConfig.loadUnloadedFallingBlocks; // PaperSpigot
     }
 
     public EntityFallingBlock(org.bukkit.Location loc, World world, double d0, double d1, double d2, IBlockData iblockdata) {
@@ -0,0 +0,0 @@ public class EntityFallingBlock extends Entity {
         this.lastX = d0;
         this.lastY = d1;
         this.lastZ = d2;
+        this.loadChunks = world.paperSpigotConfig.loadUnloadedFallingBlocks; // PaperSpigot
     }
 
     protected boolean s_() {
diff --git a/src/main/java/net/minecraft/server/EntityTNTPrimed.java b/src/main/java/net/minecraft/server/EntityTNTPrimed.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/EntityTNTPrimed.java
+++ b/src/main/java/net/minecraft/server/EntityTNTPrimed.java
@@ -0,0 +0,0 @@ public class EntityTNTPrimed extends Entity {
     // PaperSpigot end
         this.k = true;
         this.setSize(0.98F, 0.98F);
+        this.loadChunks = world.paperSpigotConfig.loadUnloadedTNTEntities; // PaperSpigot
     }
 
     public EntityTNTPrimed(org.bukkit.Location loc, World world, double d0, double d1, double d2, EntityLiving entityliving) {
@@ -0,0 +0,0 @@ public class EntityTNTPrimed extends Entity {
     private void explode() {
         // CraftBukkit start
         // float f = 4.0F;
-        
+
+        // PaperSpigot start - Force load chunks during TNT explosions
+        ChunkProviderServer chunkProviderServer = ((ChunkProviderServer) world.chunkProvider);
+        boolean forceChunkLoad = chunkProviderServer.forceChunkLoad;
+        if (world.paperSpigotConfig.loadUnloadedTNTEntities) {
+            chunkProviderServer.forceChunkLoad = true;
+        }
+        // PaperSpigot end
+
         org.bukkit.craftbukkit.CraftServer server = this.world.getServer();
 
         ExplosionPrimeEvent event = new ExplosionPrimeEvent((org.bukkit.entity.Explosive) org.bukkit.craftbukkit.entity.CraftEntity.getEntity(server, this));
@@ -0,0 +0,0 @@ public class EntityTNTPrimed extends Entity {
             this.world.createExplosion(this, this.locX, this.locY + (double) (this.length / 2.0F), this.locZ, event.getRadius(), event.getFire(), true);
         }
         // CraftBukkit end
+
+        // PaperSpigot start - Force load chunks during TNT explosions
+        if (world.paperSpigotConfig.loadUnloadedTNTEntities) {
+            chunkProviderServer.forceChunkLoad = forceChunkLoad;
+        }
+        // PaperSpigot end
     }
 
     protected void b(NBTTagCompound nbttagcompound) {
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/World.java
+++ b/src/main/java/net/minecraft/server/World.java
@@ -0,0 +0,0 @@ public abstract class World implements IBlockAccess {
             {
                 if ( !this.isChunkLoaded( chunkx, chunkz, true ) )
                 {
-                    entity.inUnloadedChunk = true; // PaperSpigot - Remove entities in unloaded chunks
-                    continue;
+                    // PaperSpigot start
+                    if (entity.loadChunks) {
+                        ((ChunkProviderServer) entity.world.chunkProvider).getChunkAt(chunkx, chunkz);
+                    } else {
+                        entity.inUnloadedChunk = true; // PaperSpigot - Remove entities in unloaded chunks
+                        continue;
+                    }
+                    // PaperSpigot end
                 }
                 int cz = chunkz << 4;
                 Chunk chunk = this.getChunkAt( chunkx, chunkz );
@@ -0,0 +0,0 @@ public abstract class World implements IBlockAccess {
             int i1 = MathHelper.floor(entity.locZ / 16.0D);
 
             if (!entity.ad || entity.ae != k || entity.af != l || entity.ag != i1) {
+                if (entity.loadChunks) entity.loadChunks(); // PaperSpigot - Force load chunks
                 if (entity.ad && this.isChunkLoaded(entity.ae, entity.ag, true)) {
                     this.getChunkAt(entity.ae, entity.ag).a(entity, entity.af);
                 }
diff --git a/src/main/java/org/github/paperspigot/PaperSpigotWorldConfig.java b/src/main/java/org/github/paperspigot/PaperSpigotWorldConfig.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/github/paperspigot/PaperSpigotWorldConfig.java
+++ b/src/main/java/org/github/paperspigot/PaperSpigotWorldConfig.java
@@ -0,0 +0,0 @@ public class PaperSpigotWorldConfig
     {
         disableEndCredits = getBoolean( "game-mechanics.disable-end-credits", false );
     }
+
+    public boolean loadUnloadedEnderPearls;
+    public boolean loadUnloadedTNTEntities;
+    public boolean loadUnloadedFallingBlocks;
+    private void loadUnloaded()
+    {
+        loadUnloadedEnderPearls = getBoolean( "load-chunks.enderpearls", false );
+        loadUnloadedTNTEntities = getBoolean( "load-chunks.tnt-entities", false );
+        loadUnloadedFallingBlocks = getBoolean( "load-chunks.falling-blocks", false );
+    }
 }
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/spigotmc/ActivationRange.java
+++ b/src/main/java/org/spigotmc/ActivationRange.java
@@ -0,0 +0,0 @@ public class ActivationRange
     {
         SpigotTimings.checkIfActiveTimer.startTiming();
         // Never safe to skip fireworks or entities not yet added to chunk
-        if ( !entity.isAddedToChunk() || entity instanceof EntityFireworks ) {
+        if ( !entity.isAddedToChunk() || entity instanceof EntityFireworks || entity.loadChunks ) { // PaperSpigot
             SpigotTimings.checkIfActiveTimer.stopTiming();
             return true;
         }
--