more patches (#5811)
This commit is contained in:
parent
0c2d1544ac
commit
2ba2c29b2e
53 changed files with 711 additions and 1074 deletions
|
@ -0,0 +1,28 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Mon, 23 Jul 2018 22:18:31 -0400
|
||||
Subject: [PATCH] Mark chunk dirty anytime entities change to guarantee it
|
||||
saves
|
||||
|
||||
Useless in 1.17 - leaf
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
index d69ccb1f31f31ebeee477df20ce1410f9e485eb7..bd9b19d988ecf72e099efeff6ec3483a352174ec 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
@@ -559,6 +559,7 @@ public class LevelChunk implements ChunkAccess {
|
||||
entity.zChunk = this.chunkPos.z;
|
||||
this.entities.add(entity); // Paper - per chunk entity list
|
||||
this.entitySlices[k].add(entity);
|
||||
+ this.markUnsaved(); // Paper
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -587,6 +588,7 @@ public class LevelChunk implements ChunkAccess {
|
||||
return;
|
||||
}
|
||||
entityCounts.decrement(entity.getMinecraftKeyString());
|
||||
+ this.markUnsaved(); // Paper
|
||||
// Paper end
|
||||
this.entities.remove(entity); // Paper
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Mon, 23 Jul 2018 22:44:23 -0400
|
||||
Subject: [PATCH] Add some Debug to Chunk Entity slices
|
||||
|
||||
If we detect unexpected state, log and try to recover
|
||||
|
||||
This should hopefully avoid duplicate entities ever being created
|
||||
if the entity was to end up in 2 different chunk slices
|
||||
|
||||
Useless in 1.17
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index a2cc3e58d59ed3d9f443b77c44d8200cc09b4da9..7847078c54154e28ab066ea8a329f929df1e1a37 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -156,6 +156,7 @@ public abstract class Entity implements Nameable, CommandSource, net.minecraft.s
|
||||
}
|
||||
}
|
||||
};
|
||||
+ public List<Entity> entitySlice = null;
|
||||
// Paper end
|
||||
|
||||
public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData; // Paper
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
index bd9b19d988ecf72e099efeff6ec3483a352174ec..09aa608bd303b618ae2c0ebd237bcbdba60a37a8 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
@@ -26,7 +26,9 @@ import net.minecraft.ReportedException;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
+import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
+import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
@@ -550,6 +552,25 @@ public class LevelChunk implements ChunkAccess {
|
||||
if (k >= this.entitySlices.length) {
|
||||
k = this.entitySlices.length - 1;
|
||||
}
|
||||
+ // Paper - remove from any old list if its in one
|
||||
+ List<Entity> nextSlice = this.entitySlices[k]; // the next list to be added to
|
||||
+ List<Entity> currentSlice = entity.entitySlice;
|
||||
+ if (nextSlice == currentSlice) {
|
||||
+ if (Level.DEBUG_ENTITIES) MinecraftServer.LOGGER.warn("Entity was already in this chunk!" + entity, new Throwable());
|
||||
+ return; // ??? silly plugins
|
||||
+ }
|
||||
+ if (currentSlice != null && currentSlice.contains(entity)) {
|
||||
+ // Still in an old chunk...
|
||||
+ if (Level.DEBUG_ENTITIES) MinecraftServer.LOGGER.warn("Entity is still in another chunk!" + entity, new Throwable());
|
||||
+ LevelChunk chunk = entity.getCurrentChunk();
|
||||
+ if (chunk != null) {
|
||||
+ chunk.removeEntity(entity);
|
||||
+ } else {
|
||||
+ removeEntity(entity);
|
||||
+ }
|
||||
+ currentSlice.remove(entity); // Just incase the above did not remove from the previous slice
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
if (!entity.inChunk || entity.getCurrentChunk() != this) entityCounts.increment(entity.getMinecraftKeyString()); // Paper
|
||||
entity.inChunk = true;
|
||||
@@ -559,6 +580,7 @@ public class LevelChunk implements ChunkAccess {
|
||||
entity.zChunk = this.chunkPos.z;
|
||||
this.entities.add(entity); // Paper - per chunk entity list
|
||||
this.entitySlices[k].add(entity);
|
||||
+ entity.entitySlice = this.entitySlices[k]; // Paper
|
||||
this.markUnsaved(); // Paper
|
||||
}
|
||||
|
||||
@@ -584,6 +606,10 @@ public class LevelChunk implements ChunkAccess {
|
||||
|
||||
// Paper start
|
||||
if (entity.currentChunk != null && entity.currentChunk.get() == this) entity.setCurrentChunk(null);
|
||||
+ if (entitySlices[section] == entity.entitySlice) {
|
||||
+ entity.entitySlice = null;
|
||||
+ entity.inChunk = false;
|
||||
+ }
|
||||
if (!this.entitySlices[section].remove(entity)) {
|
||||
return;
|
||||
}
|
||||
@@ -742,7 +768,7 @@ public class LevelChunk implements ChunkAccess {
|
||||
// Paper start - neighbour cache
|
||||
int chunkX = this.chunkPos.x;
|
||||
int chunkZ = this.chunkPos.z;
|
||||
- ChunkProviderServer chunkProvider = ((ServerLevel)this.world).getChunkSource();
|
||||
+ ServerChunkCache chunkProvider = ((ServerLevel)this.world).getChunkSource();
|
||||
for (int dx = -NEIGHBOUR_CACHE_RADIUS; dx <= NEIGHBOUR_CACHE_RADIUS; ++dx) {
|
||||
for (int dz = -NEIGHBOUR_CACHE_RADIUS; dz <= NEIGHBOUR_CACHE_RADIUS; ++dz) {
|
||||
LevelChunk neighbour = chunkProvider.getChunkAtIfLoadedMainThreadNoCache(chunkX + dx, chunkZ + dz);
|
||||
@@ -802,7 +828,7 @@ public class LevelChunk implements ChunkAccess {
|
||||
// Paper start - neighbour cache
|
||||
int chunkX = this.chunkPos.x;
|
||||
int chunkZ = this.chunkPos.z;
|
||||
- ChunkProviderServer chunkProvider = ((ServerLevel)this.world).getChunkSource();
|
||||
+ ServerChunkCache chunkProvider = ((ServerLevel)this.world).getChunkSource();
|
||||
for (int dx = -NEIGHBOUR_CACHE_RADIUS; dx <= NEIGHBOUR_CACHE_RADIUS; ++dx) {
|
||||
for (int dz = -NEIGHBOUR_CACHE_RADIUS; dz <= NEIGHBOUR_CACHE_RADIUS; ++dz) {
|
||||
LevelChunk neighbour = chunkProvider.getChunkAtIfLoadedMainThreadNoCache(chunkX + dx, chunkZ + dz);
|
|
@ -0,0 +1,129 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Thu, 26 Jul 2018 00:11:12 -0400
|
||||
Subject: [PATCH] Prevent Saving Bad entities to chunks
|
||||
|
||||
See https://github.com/PaperMC/Paper/issues/1223
|
||||
|
||||
Minecraft is saving invalid entities to the chunk files.
|
||||
|
||||
Avoid saving bad data, and also make improvements to handle
|
||||
loading these chunks. Any invalid entity will be instant killed,
|
||||
so lets avoid adding it to the world...
|
||||
|
||||
This lets us be safer about the dupe UUID resolver too, as now
|
||||
we can ignore instant killed entities and avoid risk of duplicating
|
||||
an invalid entity.
|
||||
|
||||
This should reduce log occurrences of dupe uuid messages.
|
||||
|
||||
1.17, not a concern anymore
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
index a5d7781b13a6d61238d026f064512f7162e1e868..8e8e5f30c512ed7d8ee987550c22d3e9df845043 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -1151,6 +1151,7 @@ public class ServerLevel extends net.minecraft.world.level.Level implements Worl
|
||||
List[] aentityslice = chunk.getEntitySlices(); // Spigot
|
||||
int i = aentityslice.length;
|
||||
|
||||
+ java.util.List<Entity> toMoveChunks = new java.util.ArrayList<>(); // Paper
|
||||
for (int j = 0; j < i; ++j) {
|
||||
List<Entity> entityslice = aentityslice[j]; // Spigot
|
||||
Iterator iterator = entityslice.iterator();
|
||||
@@ -1163,11 +1164,25 @@ public class ServerLevel extends net.minecraft.world.level.Level implements Worl
|
||||
throw (IllegalStateException) Util.pauseInIde((Throwable) (new IllegalStateException("Removing entity while ticking!")));
|
||||
}
|
||||
|
||||
+ // Paper start - move out entities that shouldn't be in this chunk before it unloads
|
||||
+ if (!entity.removed && (int) Math.floor(entity.getX()) >> 4 != chunk.getPos().x || (int) Math.floor(entity.getZ()) >> 4 != chunk.getPos().z) {
|
||||
+ toMoveChunks.add(entity);
|
||||
+ continue;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
this.entitiesById.remove(entity.getId());
|
||||
this.onEntityRemoved(entity);
|
||||
+
|
||||
+ if (entity.removed) iterator.remove(); // Paper - don't save dead entities during unload
|
||||
}
|
||||
}
|
||||
}
|
||||
+ // Paper start - move out entities that shouldn't be in this chunk before it unloads
|
||||
+ for (Entity entity : toMoveChunks) {
|
||||
+ this.updateChunkPos(entity);
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
index 0efaf4d0f58bcf38b427e76bf09b96e354294159..542d6f322df5f44ad9f504c8e14c88e3fa540657 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
@@ -27,6 +27,7 @@ import net.minecraft.nbt.LongArrayTag;
|
||||
import net.minecraft.nbt.ShortTag;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
+import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
@@ -349,6 +350,7 @@ public class ChunkSerializer {
|
||||
nbttagcompound1.put("TileEntities", nbttaglist1);
|
||||
ListTag nbttaglist2 = new ListTag();
|
||||
|
||||
+ java.util.List<Entity> toUpdate = new java.util.ArrayList<>(); // Paper
|
||||
if (chunk.getStatus().getChunkType() == ChunkStatus.ChunkType.LEVELCHUNK) {
|
||||
LevelChunk chunk1 = (LevelChunk) chunk;
|
||||
|
||||
@@ -366,13 +368,28 @@ public class ChunkSerializer {
|
||||
while (iterator1.hasNext()) {
|
||||
Entity entity = (Entity) iterator1.next();
|
||||
CompoundTag nbttagcompound4 = new CompoundTag();
|
||||
-
|
||||
+ // Paper start
|
||||
+ if ((int) Math.floor(entity.getX()) >> 4 != chunk1.getPos().x || (int) Math.floor(entity.getZ()) >> 4 != chunk1.getPos().z) {
|
||||
+ toUpdate.add(entity);
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (entity.removed || hasPlayerPassenger(entity)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ // Paper end
|
||||
if (entity.save(nbttagcompound4)) {
|
||||
chunk1.setLastSaveHadEntities(true);
|
||||
nbttaglist2.add(nbttagcompound4);
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // Paper start - move entities to the correct chunk
|
||||
+ for (Entity entity : toUpdate) {
|
||||
+ world.updateChunkPos(entity);
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
} else {
|
||||
ProtoChunk protochunk = (ProtoChunk) chunk;
|
||||
|
||||
@@ -431,6 +448,19 @@ public class ChunkSerializer {
|
||||
nbttagcompound1.put("Structures", packStructureData(chunkcoordintpair, chunk.getAllStarts(), chunk.getAllReferences()));
|
||||
return nbttagcompound;
|
||||
}
|
||||
+ // Paper start - this is saved with the player
|
||||
+ private static boolean hasPlayerPassenger(Entity entity) {
|
||||
+ for (Entity passenger : entity.passengers) {
|
||||
+ if (passenger instanceof ServerPlayer) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ if (hasPlayerPassenger(passenger)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
public static ChunkStatus.ChunkType getChunkTypeFromTag(@Nullable CompoundTag tag) {
|
||||
if (tag != null) {
|
|
@ -0,0 +1,122 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Sat, 28 Jul 2018 12:18:27 -0400
|
||||
Subject: [PATCH] Ignore Dead Entities in entityList iteration
|
||||
|
||||
A spigot change delays removal of entities from the entity list.
|
||||
This causes a change in behavior from Vanilla where getEntities type
|
||||
methods will return dead entities that they shouldn't otherwise be doing.
|
||||
|
||||
This will ensure that dead entities are skipped from iteration since
|
||||
they shouldn't of been in the list in the first place.
|
||||
|
||||
Not relevant in 1.17
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
index e95b91cefb0374bd5bb57cc090f5ecd566d7a618..8fd716bf2e1402694798b8be03fd85821153be44 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
@@ -209,6 +209,7 @@ public class PaperCommand extends Command {
|
||||
Collection<Entity> entities = world.entitiesById.values();
|
||||
entities.forEach(e -> {
|
||||
ResourceLocation key = e.getMinecraftKey();
|
||||
+ if (e.shouldBeRemoved) return; // Paper
|
||||
|
||||
MutablePair<Integer, Map<ChunkPos, Integer>> info = list.computeIfAbsent(key, k -> MutablePair.of(0, Maps.newHashMap()));
|
||||
ChunkPos chunk = new ChunkPos(e.xChunk, e.zChunk);
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
index 8e8e5f30c512ed7d8ee987550c22d3e9df845043..84b2cd661697545186677ab7966556d9288c650b 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -1303,6 +1303,7 @@ public class ServerLevel extends net.minecraft.world.level.Level implements Worl
|
||||
entity.origin = entity.getBukkitEntity().getLocation();
|
||||
}
|
||||
// Paper end
|
||||
+ entity.shouldBeRemoved = false; // Paper - shouldn't be removed after being re-added
|
||||
new com.destroystokyo.paper.event.entity.EntityAddToWorldEvent(entity.getBukkitEntity()).callEvent(); // Paper - fire while valid
|
||||
}
|
||||
|
||||
@@ -1315,6 +1316,7 @@ public class ServerLevel extends net.minecraft.world.level.Level implements Worl
|
||||
this.removeFromChunk(entity);
|
||||
this.entitiesById.remove(entity.getId());
|
||||
this.onEntityRemoved(entity);
|
||||
+ entity.shouldBeRemoved = true; // Paper
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index 7847078c54154e28ab066ea8a329f929df1e1a37..5bf6bc6a01ccde8a4d67b49293bb326cb09248d8 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -275,6 +275,7 @@ public abstract class Entity implements Nameable, CommandSource, net.minecraft.s
|
||||
protected int numCollisions = 0; // Paper
|
||||
public void inactiveTick() { }
|
||||
// Spigot end
|
||||
+ public boolean shouldBeRemoved; // Paper
|
||||
|
||||
public float getBukkitYaw() {
|
||||
return this.yRot;
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
index 09aa608bd303b618ae2c0ebd237bcbdba60a37a8..db28bfe95c885cdefa855c7aaa3bcf92bc52df26 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
@@ -862,6 +862,7 @@ public class LevelChunk implements ChunkAccess {
|
||||
|
||||
for (int i1 = 0; i1 < l; ++i1) {
|
||||
Entity entity1 = (Entity) list1.get(i1);
|
||||
+ if (entity1.shouldBeRemoved) continue; // Paper
|
||||
|
||||
if (entity1.getBoundingBox().intersects(box) && entity1 != except) {
|
||||
if (predicate == null || predicate.test(entity1)) {
|
||||
@@ -899,6 +900,7 @@ public class LevelChunk implements ChunkAccess {
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
T entity = (T) iterator.next(); // CraftBukkit - decompile error
|
||||
+ if (entity.shouldBeRemoved) continue; // Paper
|
||||
|
||||
if ((type == null || entity.getType() == type) && entity.getBoundingBox().intersects(box) && predicate.test(entity)) {
|
||||
result.add(entity);
|
||||
@@ -921,6 +923,7 @@ public class LevelChunk implements ChunkAccess {
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
T t0 = (T) iterator.next(); // CraftBukkit - decompile error
|
||||
+ if (t0.shouldBeRemoved) continue; // Paper
|
||||
|
||||
if (entityClass.isInstance(t0) && t0.getBoundingBox().intersects(box) && (predicate == null || predicate.test(t0))) { // Spigot - instance check
|
||||
result.add(t0);
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
index 3a3466cd9bbd34dbc0b79567f5579e84a81d6009..9807612aed6c4393cbe1f4b6078e45bf1ba3deb2 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
@@ -1022,6 +1022,7 @@ public class CraftWorld implements World {
|
||||
for (Object o : world.entitiesById.values()) {
|
||||
if (o instanceof net.minecraft.world.entity.Entity) {
|
||||
net.minecraft.world.entity.Entity mcEnt = (net.minecraft.world.entity.Entity) o;
|
||||
+ if (mcEnt.shouldBeRemoved) continue; // Paper
|
||||
Entity bukkitEntity = mcEnt.getBukkitEntity();
|
||||
|
||||
// Assuming that bukkitEntity isn't null
|
||||
@@ -1041,6 +1042,7 @@ public class CraftWorld implements World {
|
||||
for (Object o : world.entitiesById.values()) {
|
||||
if (o instanceof net.minecraft.world.entity.Entity) {
|
||||
net.minecraft.world.entity.Entity mcEnt = (net.minecraft.world.entity.Entity) o;
|
||||
+ if (mcEnt.shouldBeRemoved) continue; // Paper
|
||||
Entity bukkitEntity = mcEnt.getBukkitEntity();
|
||||
|
||||
// Assuming that bukkitEntity isn't null
|
||||
@@ -1067,6 +1069,7 @@ public class CraftWorld implements World {
|
||||
|
||||
for (Object entity: world.entitiesById.values()) {
|
||||
if (entity instanceof net.minecraft.world.entity.Entity) {
|
||||
+ if (((net.minecraft.world.entity.Entity) entity).shouldBeRemoved) continue; // Paper
|
||||
Entity bukkitEntity = ((net.minecraft.world.entity.Entity) entity).getBukkitEntity();
|
||||
|
||||
if (bukkitEntity == null) {
|
||||
@@ -1090,6 +1093,7 @@ public class CraftWorld implements World {
|
||||
|
||||
for (Object entity: world.entitiesById.values()) {
|
||||
if (entity instanceof net.minecraft.world.entity.Entity) {
|
||||
+ if (((net.minecraft.world.entity.Entity) entity).shouldBeRemoved) continue; // Paper
|
||||
Entity bukkitEntity = ((net.minecraft.world.entity.Entity) entity).getBukkitEntity();
|
||||
|
||||
if (bukkitEntity == null) {
|
Loading…
Add table
Add a link
Reference in a new issue