SPIGOT-4752: Fixed inconsistency between isChunkLoaded and chunk load/unload events

This commit is contained in:
blablubbabc 2019-05-16 01:11:20 +02:00 committed by md_5
parent 3f9f31c3ce
commit 8d0f37228d
8 changed files with 209 additions and 143 deletions

View File

@ -1,32 +1,18 @@
--- a/net/minecraft/server/Chunk.java
+++ b/net/minecraft/server/Chunk.java
@@ -22,6 +22,13 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+// CraftBukkit start
+import com.google.common.collect.Lists;
+import java.util.LinkedList;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.event.entity.CreatureSpawnEvent;
+// CraftBukkit end
+
public class Chunk implements IChunkAccess {
private static final Logger LOGGER = LogManager.getLogger();
@@ -95,8 +102,19 @@
@@ -95,8 +95,19 @@
}
}
+ // CraftBukkit start
+ this.bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this);
+ }
+
}
+ public org.bukkit.Chunk bukkitChunk;
+ public org.bukkit.Chunk getBukkitChunk() {
+ return bukkitChunk;
}
+ }
+
+ public boolean mustNotSave;
+ public boolean needsDecoration;
+ // CraftBukkit end
@ -34,7 +20,7 @@
public Chunk(World world, ProtoChunk protochunk) {
this(world, protochunk.getPos(), protochunk.getBiomeIndex(), protochunk.p(), protochunk.n(), protochunk.o(), protochunk.q(), protochunk.getSections(), (Consumer) null);
Iterator iterator = protochunk.y().iterator();
@@ -138,6 +156,7 @@
@@ -138,6 +149,7 @@
this.b(protochunk.r());
this.s = true;
@ -42,7 +28,7 @@
}
@Override
@@ -228,9 +247,16 @@
@@ -228,9 +240,16 @@
}
}
@ -59,7 +45,7 @@
int i = blockposition.getX() & 15;
int j = blockposition.getY();
int k = blockposition.getZ() & 15;
@@ -282,7 +308,8 @@
@@ -282,7 +301,8 @@
}
}
@ -69,7 +55,7 @@
iblockdata.onPlace(this.world, blockposition, iblockdata1, flag);
}
@@ -382,7 +409,12 @@
@@ -382,7 +402,12 @@
@Nullable
public TileEntity a(BlockPosition blockposition, Chunk.EnumTileEntityState chunk_enumtileentitystate) {
@ -83,7 +69,7 @@
if (tileentity == null) {
NBTTagCompound nbttagcompound = (NBTTagCompound) this.e.remove(blockposition);
@@ -429,6 +461,13 @@
@@ -429,6 +454,13 @@
tileentity1.m();
}
@ -97,7 +83,7 @@
}
}
@@ -457,6 +496,41 @@
@@ -457,6 +489,50 @@
}
@ -113,6 +99,7 @@
+ server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(this.bukkitChunk, this.needsDecoration));
+
+ if (this.needsDecoration) {
+ this.needsDecoration = false;
+ java.util.Random random = new java.util.Random();
+ random.setSeed(world.getSeed());
+ long xRand = random.nextLong() / 2L * 2L + 1L;
@ -134,12 +121,20 @@
+ }
+ }
+ }
+
+ public void unloadCallback() {
+ org.bukkit.Server server = this.world.getServer();
+ org.bukkit.event.world.ChunkUnloadEvent unloadEvent = new org.bukkit.event.world.ChunkUnloadEvent(this.bukkitChunk, this.isNeedsSaving());
+ server.getPluginManager().callEvent(unloadEvent);
+ // note: saving can be prevented, but not forced if no saving is actually required
+ this.mustNotSave = !unloadEvent.isSaveChunk();
+ }
+ // CraftBukkit end
+
public void markDirty() {
this.s = true;
}
@@ -531,7 +605,7 @@
@@ -531,7 +607,7 @@
Iterator iterator = this.entitySlices[k].a(oclass).iterator();
while (iterator.hasNext()) {
@ -148,7 +143,7 @@
if (t0.getBoundingBox().c(axisalignedbb) && (predicate == null || predicate.test(t0))) {
list.add(t0);
@@ -605,7 +679,7 @@
@@ -605,7 +681,7 @@
@Override
public boolean isNeedsSaving() {
@ -157,7 +152,7 @@
}
public void d(boolean flag) {
@@ -746,7 +820,7 @@
@@ -746,7 +822,7 @@
public void B() {
if (this.o instanceof ProtoChunkTickList) {
@ -166,7 +161,7 @@
return this.getType(blockposition).getBlock();
});
this.o = TickListEmpty.a();
@@ -756,7 +830,7 @@
@@ -756,7 +832,7 @@
}
if (this.p instanceof ProtoChunkTickList) {
@ -175,7 +170,7 @@
return this.getFluid(blockposition).getType();
});
this.p = TickListEmpty.a();
@@ -768,12 +842,12 @@
@@ -768,12 +844,12 @@
}
public void a(WorldServer worldserver) {

View File

@ -1,6 +1,49 @@
--- a/net/minecraft/server/ChunkProviderServer.java
+++ b/net/minecraft/server/ChunkProviderServer.java
@@ -241,6 +241,17 @@
@@ -81,7 +81,7 @@
for (int l = 0; l < 4; ++l) {
if (k == this.n[l] && chunkstatus == this.o[l]) {
ichunkaccess = this.p[l];
- if (ichunkaccess != null || !flag) {
+ if (ichunkaccess != null) { // CraftBukkit - the chunk can become accessible in the meantime TODO for non-null chunks it might also make sense to check that the chunk's state hasn't changed in the meantime
return ichunkaccess;
}
}
@@ -125,7 +125,15 @@
int l = 33 + ChunkStatus.a(chunkstatus);
PlayerChunk playerchunk = this.getChunk(k);
- if (flag) {
+ // CraftBukkit start - don't add new ticket for currently unloading chunk
+ boolean currentlyUnloading = false;
+ if (playerchunk != null) {
+ PlayerChunk.State oldChunkState = PlayerChunk.c(playerchunk.oldTicketLevel); // PAIL getChunkState
+ PlayerChunk.State currentChunkState = PlayerChunk.c(playerchunk.getTicketLevel()); // PAIL getChunkState
+ currentlyUnloading = (oldChunkState.a(PlayerChunk.State.BORDER) && !currentChunkState.a(PlayerChunk.State.BORDER)); // PAIL isAtLeast
+ }
+ if (flag && !currentlyUnloading) {
+ // CraftBukkit end
this.chunkMapDistance.a(TicketType.UNKNOWN, chunkcoordintpair, l, chunkcoordintpair);
if (this.a(playerchunk, l)) {
GameProfilerFiller gameprofilerfiller = this.world.getMethodProfiler();
@@ -144,14 +152,14 @@
}
private boolean a(@Nullable PlayerChunk playerchunk, int i) {
- return playerchunk == null || playerchunk.getTicketLevel() > i;
+ return playerchunk == null || playerchunk.oldTicketLevel > i; // CraftBukkit using oldTicketLevel for isLoaded checks
}
public boolean isLoaded(int i, int j) {
PlayerChunk playerchunk = this.getChunk((new ChunkCoordIntPair(i, j)).pair());
int k = 33 + ChunkStatus.a(ChunkStatus.FULL);
- return playerchunk != null && playerchunk.getTicketLevel() <= k ? ((Either) playerchunk.getStatusFuture(ChunkStatus.FULL).getNow(PlayerChunk.UNLOADED_CHUNK_ACCESS)).left().isPresent() : false;
+ return playerchunk != null && playerchunk.oldTicketLevel <= k ? ((Either) playerchunk.getStatusFuture(ChunkStatus.FULL).getNow(PlayerChunk.UNLOADED_CHUNK_ACCESS)).left().isPresent() : false; // CraftBukkit using oldTicketLevel for isLoaded checks
}
@Override
@@ -241,6 +249,18 @@
this.playerChunkMap.close();
}
@ -12,13 +55,14 @@
+ this.world.getMethodProfiler().exitEnter("unload");
+ this.playerChunkMap.unloadChunks(() -> true);
+ this.world.getMethodProfiler().exit();
+ this.l(); // PAIL clearCache
+ }
+ // CraftBukkit end
+
public void tick(BooleanSupplier booleansupplier) {
this.world.getMethodProfiler().enter("purge");
this.chunkMapDistance.purgeTickets();
@@ -260,13 +271,13 @@
@@ -260,13 +280,13 @@
this.lastTickTime = i;
WorldData worlddata = this.world.getWorldData();
boolean flag = worlddata.getType() == WorldType.DEBUG_ALL_BLOCK_STATES;
@ -34,7 +78,7 @@
this.world.getMethodProfiler().enter("naturalSpawnCount");
int l = this.chunkMapDistance.b();
@@ -299,8 +310,30 @@
@@ -299,8 +319,30 @@
for (int j1 = 0; j1 < i1; ++j1) {
EnumCreatureType enumcreaturetype = aenumcreaturetype1[j1];

View File

@ -58,7 +58,7 @@
protected static final Logger LOGGER = LogManager.getLogger();
private static final AtomicInteger entityCount = new AtomicInteger();
private static final List<ItemStack> c = Collections.emptyList();
@@ -106,6 +155,16 @@
@@ -106,6 +155,20 @@
private long aH;
private EntitySize size;
private float headHeight;
@ -71,11 +71,15 @@
+ public float getBukkitYaw() {
+ return this.yaw;
+ }
+
+ public boolean isChunkLoaded() {
+ return world.isChunkLoaded((int) Math.floor(this.locX) >> 4, (int) Math.floor(this.locZ) >> 4);
+ }
+ // CraftBukkit end
public Entity(EntityTypes<?> entitytypes, World world) {
this.id = Entity.entityCount.incrementAndGet();
@@ -204,6 +263,12 @@
@@ -204,6 +267,12 @@
}
protected void setPose(EntityPose entitypose) {
@ -88,7 +92,7 @@
this.datawatcher.set(Entity.POSE, entitypose);
}
@@ -212,6 +277,33 @@
@@ -212,6 +281,33 @@
}
protected void setYawPitch(float f, float f1) {
@ -122,7 +126,7 @@
this.yaw = f % 360.0F;
this.pitch = f1 % 360.0F;
}
@@ -224,6 +316,7 @@
@@ -224,6 +320,7 @@
float f1 = this.size.height;
this.a(new AxisAlignedBB(d0 - (double) f, d1, d2 - (double) f, d0 + (double) f, d1 + (double) f1, d2 + (double) f));
@ -130,7 +134,7 @@
}
public void tick() {
@@ -234,6 +327,15 @@
@@ -234,6 +331,15 @@
this.entityBaseTick();
}
@ -146,7 +150,7 @@
public void entityBaseTick() {
this.world.getMethodProfiler().enter("entityBaseTick");
if (this.isPassenger() && this.getVehicle().dead) {
@@ -250,7 +352,7 @@
@@ -250,7 +356,7 @@
this.lastZ = this.locZ;
this.lastPitch = this.pitch;
this.lastYaw = this.yaw;
@ -155,7 +159,7 @@
this.az();
this.m();
if (this.world.isClientSide) {
@@ -300,12 +402,44 @@
@@ -300,12 +406,44 @@
protected void burnFromLava() {
if (!this.isFireProof()) {
@ -201,7 +205,7 @@
int j = i * 20;
if (this instanceof EntityLiving) {
@@ -401,6 +535,28 @@
@@ -401,6 +539,28 @@
block1.a((IBlockAccess) this.world, this);
}
@ -230,7 +234,7 @@
if (this.playStepSound() && (!this.onGround || !this.isSneaking() || !(this instanceof EntityHuman)) && !this.isPassenger()) {
double d0 = vec3d1.x;
double d1 = vec3d1.y;
@@ -454,7 +610,14 @@
@@ -454,7 +614,14 @@
if (!flag) {
++this.fireTicks;
if (this.fireTicks == 0) {
@ -246,7 +250,7 @@
}
}
@@ -565,7 +728,7 @@
@@ -565,7 +732,7 @@
VoxelShape voxelshape = this.world.getWorldBorder().a();
Stream<VoxelShape> stream = VoxelShapes.c(voxelshape, VoxelShapes.a(axisalignedbb.shrink(1.0E-7D)), OperatorBoolean.AND) ? Stream.empty() : Stream.of(voxelshape);
AxisAlignedBB axisalignedbb1 = axisalignedbb.a(vec3d).g(1.0E-7D);
@ -255,7 +259,7 @@
return !this.x(entity);
}).flatMap((entity) -> {
return Stream.of(entity.ap(), this.j(entity));
@@ -649,6 +812,7 @@
@@ -649,6 +816,7 @@
this.locX = (axisalignedbb.minX + axisalignedbb.maxX) / 2.0D;
this.locY = axisalignedbb.minY;
this.locZ = (axisalignedbb.minZ + axisalignedbb.maxZ) / 2.0D;
@ -263,7 +267,7 @@
}
protected SoundEffect getSoundSwim() {
@@ -820,7 +984,7 @@
@@ -820,7 +988,7 @@
return null;
}
@ -272,7 +276,7 @@
if (!this.isFireProof()) {
this.damageEntity(DamageSource.FIRE, (float) i);
}
@@ -1053,6 +1217,13 @@
@@ -1053,6 +1221,13 @@
}
public void spawnIn(World world) {
@ -286,7 +290,7 @@
this.world = world;
}
@@ -1078,6 +1249,7 @@
@@ -1078,6 +1253,7 @@
this.lastYaw -= 360.0F;
}
@ -294,7 +298,7 @@
this.setPosition(this.locX, this.locY, this.locZ);
this.setYawPitch(f, f1);
}
@@ -1246,7 +1418,7 @@
@@ -1246,7 +1422,7 @@
public boolean c(NBTTagCompound nbttagcompound) {
String s = this.getSaveID();
@ -303,7 +307,7 @@
nbttagcompound.setString("id", s);
this.save(nbttagcompound);
return true;
@@ -1265,15 +1437,33 @@
@@ -1265,15 +1441,33 @@
Vec3D vec3d = this.getMot();
nbttagcompound.set("Motion", this.a(vec3d.x, vec3d.y, vec3d.z));
@ -338,7 +342,7 @@
IChatBaseComponent ichatbasecomponent = this.getCustomName();
if (ichatbasecomponent != null) {
@@ -1331,6 +1521,11 @@
@@ -1331,6 +1525,11 @@
}
}
@ -350,7 +354,7 @@
return nbttagcompound;
} catch (Throwable throwable) {
CrashReport crashreport = CrashReport.a(throwable, "Saving entity NBT");
@@ -1371,7 +1566,7 @@
@@ -1371,7 +1570,7 @@
this.setAirTicks(nbttagcompound.getShort("Air"));
this.onGround = nbttagcompound.getBoolean("OnGround");
if (nbttagcompound.hasKey("Dimension")) {
@ -359,7 +363,7 @@
}
this.invulnerable = nbttagcompound.getBoolean("Invulnerable");
@@ -1414,6 +1609,42 @@
@@ -1414,6 +1613,42 @@
} else {
throw new IllegalStateException("Entity has invalid position");
}
@ -402,7 +406,7 @@
} catch (Throwable throwable) {
CrashReport crashreport = CrashReport.a(throwable, "Loading entity NBT");
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being loaded");
@@ -1489,9 +1720,22 @@
@@ -1489,9 +1724,22 @@
} else if (this.world.isClientSide) {
return null;
} else {
@ -425,7 +429,7 @@
this.world.addEntity(entityitem);
return entityitem;
}
@@ -1595,7 +1839,7 @@
@@ -1595,7 +1843,7 @@
}
this.vehicle = entity;
@ -434,7 +438,7 @@
return true;
}
}
@@ -1620,15 +1864,36 @@
@@ -1620,15 +1868,36 @@
Entity entity = this.vehicle;
this.vehicle = null;
@ -473,7 +477,7 @@
if (!this.world.isClientSide && entity instanceof EntityHuman && !(this.getRidingPassenger() instanceof EntityHuman)) {
this.passengers.add(0, entity);
} else {
@@ -1636,15 +1901,33 @@
@@ -1636,15 +1905,33 @@
}
}
@ -508,7 +512,7 @@
}
protected boolean q(Entity entity) {
@@ -1687,11 +1970,17 @@
@@ -1687,11 +1974,17 @@
int i = this.ab();
if (this.ai) {
@ -528,7 +532,7 @@
this.world.getMethodProfiler().exit();
}
@@ -1771,6 +2060,13 @@
@@ -1771,6 +2064,13 @@
}
public void setSwimming(boolean flag) {
@ -542,7 +546,7 @@
this.setFlag(4, flag);
}
@@ -1831,16 +2127,56 @@
@@ -1831,16 +2131,56 @@
}
public void setAirTicks(int i) {
@ -577,8 +581,9 @@
+ this.setOnFire(entityCombustEvent.getDuration(), false);
+ }
+ // CraftBukkit end
+ }
+
}
- this.damageEntity(DamageSource.LIGHTNING, 5.0F);
+ // CraftBukkit start
+ if (thisBukkitEntity instanceof Hanging) {
+ HangingBreakByEntityEvent hangingEvent = new HangingBreakByEntityEvent((Hanging) thisBukkitEntity, stormBukkitEntity);
@ -587,9 +592,8 @@
+ if (hangingEvent.isCancelled()) {
+ return;
+ }
}
- this.damageEntity(DamageSource.LIGHTNING, 5.0F);
+ }
+
+ if (this.isFireProof()) {
+ return;
+ }
@ -602,7 +606,7 @@
}
public void j(boolean flag) {
@@ -1988,20 +2324,33 @@
@@ -1988,20 +2328,33 @@
@Nullable
public Entity a(DimensionManager dimensionmanager) {
@ -639,7 +643,7 @@
if (dimensionmanager1 == DimensionManager.THE_END && dimensionmanager == DimensionManager.OVERWORLD) {
blockposition = worldserver1.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES, worldserver1.getSpawn());
} else if (dimensionmanager == DimensionManager.THE_END) {
@@ -2039,6 +2388,25 @@
@@ -2039,6 +2392,25 @@
vec3d = shapedetector_c.b;
f = (float) shapedetector_c.c;
}
@ -665,7 +669,7 @@
this.world.getMethodProfiler().exitEnter("reloading");
Entity entity = this.getEntityType().a((World) worldserver1);
@@ -2048,6 +2416,14 @@
@@ -2048,6 +2420,14 @@
entity.setPositionRotation(blockposition, entity.yaw + f, entity.pitch);
entity.setMot(vec3d);
worldserver1.addEntityTeleport(entity);
@ -680,7 +684,7 @@
}
this.dead = true;
@@ -2239,7 +2615,26 @@
@@ -2239,7 +2619,26 @@
}
public void a(AxisAlignedBB axisalignedbb) {

View File

@ -1,5 +1,14 @@
--- a/net/minecraft/server/PlayerChunk.java
+++ b/net/minecraft/server/PlayerChunk.java
@@ -23,7 +23,7 @@
private volatile CompletableFuture<Either<Chunk, PlayerChunk.Failure>> tickingFuture;
private volatile CompletableFuture<Either<Chunk, PlayerChunk.Failure>> entityTickingFuture;
private CompletableFuture<IChunkAccess> chunkSave;
- private int oldTicketLevel;
+ public int oldTicketLevel; // CraftBukkit - public
private int ticketLevel;
private int n;
private final ChunkCoordIntPair location;
@@ -43,7 +43,7 @@
this.fullChunkFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE;
this.tickingFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE;
@ -9,7 +18,31 @@
this.dirtyBlocks = new short[64];
this.location = chunkcoordintpair;
this.lightEngine = lightengine;
@@ -76,9 +76,9 @@
@@ -55,6 +55,14 @@
this.a(i);
}
+ // CraftBukkit start
+ public Chunk getFullChunk() {
+ CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> statusFuture = this.getStatusFuture(ChunkStatus.FULL);
+ Either<IChunkAccess, PlayerChunk.Failure> either = (Either<IChunkAccess, PlayerChunk.Failure>) statusFuture.getNow(null);
+ return either == null ? null : (Chunk) either.left().orElse(null);
+ }
+ // CraftBukkit end
+
public CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> getStatusFutureUnchecked(ChunkStatus chunkstatus) {
CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> completablefuture = (CompletableFuture) this.statusFutures.get(chunkstatus.c());
@@ -62,7 +70,7 @@
}
public CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> getStatusFuture(ChunkStatus chunkstatus) {
- return b(this.ticketLevel).b(chunkstatus) ? this.getStatusFutureUnchecked(chunkstatus) : PlayerChunk.UNLOADED_CHUNK_ACCESS_FUTURE;
+ return b(this.oldTicketLevel).b(chunkstatus) ? this.getStatusFutureUnchecked(chunkstatus) : PlayerChunk.UNLOADED_CHUNK_ACCESS_FUTURE; // CraftBukkit using oldTicketLevel for isLoaded checks
}
public CompletableFuture<Either<Chunk, PlayerChunk.Failure>> a() {
@@ -76,9 +84,9 @@
@Nullable
public Chunk getChunk() {
CompletableFuture<Either<Chunk, PlayerChunk.Failure>> completablefuture = this.a();
@ -21,7 +54,7 @@
}
public CompletableFuture<IChunkAccess> getChunkSave() {
@@ -201,7 +201,7 @@
@@ -201,7 +209,7 @@
CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> completablefuture = (CompletableFuture) this.statusFutures.get(i);
if (completablefuture != null) {
@ -30,23 +63,29 @@
if (either == null || either.left().isPresent()) {
return completablefuture;
@@ -213,6 +213,15 @@
this.a(completablefuture1);
this.statusFutures.set(i, completablefuture1);
+ // CraftBukkit start
+ if (chunkstatus == ChunkStatus.FULL) {
+ completablefuture1.thenAccept((either) -> {
+ Chunk chunk = (Chunk) either.left().get();
+
+ chunk.loadCallback();
@@ -256,6 +264,21 @@
boolean flag1 = this.ticketLevel <= PlayerChunkMap.GOLDEN_TICKET;
PlayerChunk.State playerchunk_state = c(this.oldTicketLevel);
PlayerChunk.State playerchunk_state1 = c(this.ticketLevel);
+ // CraftBukkit start
+ // ChunkUnloadEvent: Called before the chunk is unloaded: isChunkLoaded is still true and chunk can still be modified by plugins.
+ if (playerchunk_state.a(PlayerChunk.State.BORDER) && !playerchunk_state1.a(PlayerChunk.State.BORDER)) { // PAIL oldChunkState, newChunkState, isAtLeast
+ this.getStatusFutureUnchecked(ChunkStatus.FULL).thenAccept((either) -> {
+ either.ifLeft((chunkAccess) -> {
+ Chunk chunk = (Chunk) chunkAccess;
+ // Minecraft will apply the chunks tick lists to the world once the chunk got loaded, and then store the tick
+ // lists again inside the chunk once the chunk becomes inaccessible and set the chunk's needsSaving flag.
+ // These actions may however happen deferred, so we manually set the needsSaving flag already here.
+ chunk.setNeedsSaving(true);
+ chunk.unloadCallback();
+ });
+ }
+ // CraftBukkit end
return completablefuture1;
} else {
return completablefuture == null ? PlayerChunk.UNLOADED_CHUNK_ACCESS_FUTURE : completablefuture;
@@ -294,7 +303,7 @@
+ });
+ }
+ // CraftBukkit end
if (flag1) {
for (int i = flag ? chunkstatus.c() + 1 : 0; i <= chunkstatus1.c(); ++i) {
@@ -294,7 +317,7 @@
if (flag2 && !flag3) {
completablefuture = this.fullChunkFuture;
this.fullChunkFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE;
@ -55,3 +94,21 @@
playerchunkmap.getClass();
return either1.ifLeft(playerchunkmap::a);
}));
@@ -332,6 +355,17 @@
this.w.a(this.location, this::j, this.ticketLevel, this::d);
this.oldTicketLevel = this.ticketLevel;
+ // CraftBukkit start
+ // ChunkLoadEvent: Called after the chunk is loaded: isChunkLoaded returns true and chunk is ready to be modified by plugins.
+ if (!playerchunk_state.a(PlayerChunk.State.BORDER) && playerchunk_state1.a(PlayerChunk.State.BORDER)) { // PAIL oldChunkState, newChunkState, isAtLeast
+ this.getStatusFutureUnchecked(ChunkStatus.FULL).thenAccept((either) -> {
+ either.ifLeft((chunkAccess) -> {
+ Chunk chunk = (Chunk) chunkAccess;
+ chunk.loadCallback();
+ });
+ });
+ }
+ // CraftBukkit end
}
public static ChunkStatus b(int i) {

View File

@ -1,17 +1,14 @@
--- a/net/minecraft/server/PlayerChunkMap.java
+++ b/net/minecraft/server/PlayerChunkMap.java
@@ -35,6 +35,10 @@
@@ -35,6 +35,7 @@
import org.apache.commons.lang3.mutable.MutableBoolean;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+// CraftBukkit start
+import org.bukkit.entity.Player;
+import org.bukkit.event.world.ChunkUnloadEvent;
+// CraftBukkit end
+import org.bukkit.entity.Player; // CraftBukkit
public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
@@ -181,9 +185,12 @@
@@ -181,9 +182,12 @@
return completablefuture1.thenApply((list1) -> {
List<IChunkAccess> list2 = Lists.newArrayList();
@ -26,7 +23,7 @@
final Either<IChunkAccess, PlayerChunk.Failure> either = (Either) iterator.next();
Optional<IChunkAccess> optional = either.left();
@@ -279,7 +286,7 @@
@@ -279,7 +283,7 @@
PlayerChunkMap.LOGGER.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", this.x.getName());
} else {
this.visibleChunks.values().stream().filter(PlayerChunk::hasBeenLoaded).forEach((playerchunk) -> {
@ -35,7 +32,7 @@
if (ichunkaccess instanceof ProtoChunkExtension || ichunkaccess instanceof Chunk) {
this.saveChunk(ichunkaccess);
@@ -290,7 +297,6 @@
@@ -290,7 +294,6 @@
}
}
@ -43,27 +40,7 @@
protected void unloadChunks(BooleanSupplier booleansupplier) {
GameProfilerFiller gameprofilerfiller = this.world.getMethodProfiler();
@@ -330,9 +336,19 @@
if (this.loadedChunks.remove(i) && ichunkaccess instanceof Chunk) {
Chunk chunk = (Chunk) ichunkaccess;
+ // CraftBukkit start
+ ChunkUnloadEvent event = new ChunkUnloadEvent(chunk.bukkitChunk, chunk.isNeedsSaving());
+ this.world.getServer().getPluginManager().callEvent(event);
+ this.saveChunk(ichunkaccess, event.isSaveChunk());
+ // CraftBukkit end
+
chunk.setLoaded(false);
this.world.unloadChunk(chunk);
+ // CraftBukkit start
+ } else {
+ this.saveChunk(ichunkaccess);
}
+ // CraftBukkit end
this.lightEngine.a(ichunkaccess.getPos());
this.lightEngine.queueUpdate();
@@ -416,7 +432,7 @@
@@ -416,7 +419,7 @@
return CompletableFuture.completedFuture(Either.right(playerchunk_failure));
});
}, (runnable) -> {
@ -72,7 +49,7 @@
});
}
}
@@ -498,7 +514,7 @@
@@ -498,7 +501,7 @@
long i = playerchunk.h().pair();
playerchunk.getClass();
@ -81,7 +58,7 @@
});
}
@@ -515,7 +531,7 @@
@@ -515,7 +518,7 @@
return Either.left(chunk);
});
}, (runnable) -> {
@ -90,7 +67,7 @@
});
completablefuture1.thenAcceptAsync((either) -> {
@@ -529,7 +545,7 @@
@@ -529,7 +532,7 @@
return Either.left(chunk);
});
}, (runnable) -> {
@ -99,7 +76,7 @@
});
return completablefuture1;
}
@@ -543,7 +559,7 @@
@@ -543,7 +546,7 @@
return chunk;
});
}, (runnable) -> {
@ -108,23 +85,7 @@
});
}
@@ -552,8 +568,14 @@
}
public boolean saveChunk(IChunkAccess ichunkaccess) {
+ // CraftBukkit start
+ return this.saveChunk(ichunkaccess, ichunkaccess.isNeedsSaving());
+ }
+
+ public boolean saveChunk(IChunkAccess ichunkaccess, boolean save) {
+ // CraftBukkit end
this.n.a(ichunkaccess.getPos());
- if (!ichunkaccess.isNeedsSaving()) {
+ if (!save) { // CraftBukkit
return false;
} else {
try {
@@ -607,9 +629,10 @@
@@ -607,9 +610,10 @@
ChunkCoordIntPair chunkcoordintpair = playerchunk.h();
Packet<?>[] apacket = new Packet[2];
@ -136,7 +97,7 @@
boolean flag1 = i1 <= this.viewDistance;
this.sendChunk(entityplayer, chunkcoordintpair, apacket, flag, flag1);
@@ -664,7 +687,7 @@
@@ -664,7 +668,7 @@
private NBTTagCompound readChunkData(ChunkCoordIntPair chunkcoordintpair) throws IOException {
NBTTagCompound nbttagcompound = this.read(chunkcoordintpair);
@ -145,7 +106,7 @@
}
boolean d(ChunkCoordIntPair chunkcoordintpair) {
@@ -984,7 +1007,7 @@
@@ -984,7 +988,7 @@
public final Set<EntityPlayer> trackedPlayers = Sets.newHashSet();
public EntityTracker(Entity entity, int i, int j, boolean flag) {
@ -154,7 +115,7 @@
this.tracker = entity;
this.trackingDistance = i;
this.e = SectionPosition.a(entity);
@@ -1053,6 +1076,17 @@
@@ -1053,6 +1057,17 @@
}
}

View File

@ -93,6 +93,9 @@ public class CraftChunk implements Chunk {
@Override
public Entity[] getEntities() {
if (!isLoaded()) {
getWorld().getChunkAt(x, z); // Transient load for this tick
}
int count = 0, index = 0;
net.minecraft.server.Chunk chunk = getHandle();
@ -118,6 +121,9 @@ public class CraftChunk implements Chunk {
@Override
public BlockState[] getTileEntities() {
if (!isLoaded()) {
getWorld().getChunkAt(x, z); // Transient load for this tick
}
int index = 0;
net.minecraft.server.Chunk chunk = getHandle();

View File

@ -330,7 +330,7 @@ public class CraftWorld implements World {
@Override
public boolean isChunkLoaded(int x, int z) {
net.minecraft.server.Chunk chunk = world.getChunkProvider().getChunkAt(x, z, false);
return chunk != null && chunk.loaded;
return chunk != null;
}
@Override
@ -345,8 +345,7 @@ public class CraftWorld implements World {
@Override
public Chunk[] getLoadedChunks() {
Long2ObjectLinkedOpenHashMap<PlayerChunk> chunks = world.getChunkProvider().playerChunkMap.visibleChunks;
return chunks.values().stream().map(PlayerChunk::getChunk).filter(Objects::nonNull).filter((chunk) -> chunk.loaded).map(net.minecraft.server.Chunk::getBukkitChunk).toArray(Chunk[]::new);
return chunks.values().stream().map(PlayerChunk::getFullChunk).filter(Objects::nonNull).map(net.minecraft.server.Chunk::getBukkitChunk).toArray(Chunk[]::new);
}
@Override

View File

@ -530,7 +530,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
@Override
public boolean isValid() {
return entity.isAlive() && entity.valid;
return entity.isAlive() && entity.valid && entity.isChunkLoaded();
}
@Override