Paper/patches/server/0631-Improve-and-expand-AsyncCatcher.patch
Owen 89d51d5f29
Allow enabling sand duping (#10191)
Because this exploit has been widely known for years and has not been fixed by Mojang, we decided that it was worth allowing people to toggle it on/off due to how easy it is to make it configurable.

It should be noted that this decision does not promise all future exploits will be configurable.
2024-03-03 17:05:34 -05:00

228 lines
13 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Wed, 25 Aug 2021 20:17:12 -0700
Subject: [PATCH] Improve and expand AsyncCatcher
Log when the async catcher is tripped
The chunk system can swallow the exception given it's all
built with completablefuture, so ensure it is at least printed.
Add/move several async catchers
Async catch modifications to critical entity state
These used to be here from Spigot, but were dropped with 1.17.
Now in 1.17, this state is _even more_ critical than it was before,
so these must exist to catch stupid plugins.
Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 2941445ea453e2fb88ea8de38c24bf755b93936a..666c6cb6f600973e1d84839aed715606279d4630 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1570,6 +1570,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
}
public void internalTeleport(double d0, double d1, double d2, float f, float f1, Set<RelativeMovement> set) { // Paper
+ org.spigotmc.AsyncCatcher.catchOp("teleport"); // Paper
// Paper start - Prevent teleporting dead entities
if (player.isRemoved()) {
LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName());
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index a2edccc840f2ba76e236ea7441f3e4909b1a1898..18804ea960f8423d172d9119c0739226a18d4d56 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -1126,7 +1126,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
}
public boolean addEffect(MobEffectInstance mobeffect, @Nullable Entity entity, EntityPotionEffectEvent.Cause cause) {
- org.spigotmc.AsyncCatcher.catchOp("effect add"); // Spigot
+ // org.spigotmc.AsyncCatcher.catchOp("effect add"); // Spigot // Paper - move to API
if (this.isTickingEffects) {
this.effectsToProcess.add(new ProcessableEffect(mobeffect, cause));
return true;
diff --git a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
index 8f23b45dce35617bb56b21fb4f7a09baf36d40c5..cbdb2a710a7217b750de3e782cad5b5a45c814b3 100644
--- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
+++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
@@ -78,6 +78,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
private boolean addEntityUuid(T entity) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity add by UUID"); // Paper
if (!this.knownUuids.add(entity.getUUID())) {
PersistentEntitySectionManager.LOGGER.warn("UUID of added entity already exists: {}", entity);
return false;
@@ -91,6 +92,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
private boolean addEntity(T entity, boolean existing) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity add"); // Paper
// Paper start - chunk system hooks
if (existing) {
// I don't want to know why this is a generic type.
@@ -146,19 +148,23 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
void startTicking(T entity) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity start ticking"); // Paper
this.callbacks.onTickingStart(entity);
}
void stopTicking(T entity) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity stop ticking"); // Paper
this.callbacks.onTickingEnd(entity);
}
void startTracking(T entity) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity start tracking"); // Paper
this.visibleEntityStorage.add(entity);
this.callbacks.onTrackingStart(entity);
}
void stopTracking(T entity) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity stop tracking"); // Paper
this.callbacks.onTrackingEnd(entity);
this.visibleEntityStorage.remove(entity);
}
@@ -170,6 +176,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
public void updateChunkStatus(ChunkPos chunkPos, Visibility trackingStatus) {
+ org.spigotmc.AsyncCatcher.catchOp("Update chunk status"); // Paper
long i = chunkPos.toLong();
if (trackingStatus == Visibility.HIDDEN) {
@@ -214,6 +221,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
public void ensureChunkQueuedForLoad(long chunkPos) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity chunk save"); // Paper
PersistentEntitySectionManager.ChunkLoadStatus persistententitysectionmanager_b = (PersistentEntitySectionManager.ChunkLoadStatus) this.chunkLoadStatuses.get(chunkPos);
if (persistententitysectionmanager_b == PersistentEntitySectionManager.ChunkLoadStatus.FRESH) {
@@ -258,6 +266,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
private void requestChunkLoad(long chunkPos) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity chunk load request"); // Paper
this.chunkLoadStatuses.put(chunkPos, PersistentEntitySectionManager.ChunkLoadStatus.PENDING);
ChunkPos chunkcoordintpair = new ChunkPos(chunkPos);
CompletableFuture completablefuture = this.permanentStorage.loadEntities(chunkcoordintpair);
@@ -271,6 +280,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
private boolean processChunkUnload(long chunkPos) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity chunk unload process"); // Paper
boolean flag = this.storeChunkSections(chunkPos, (entityaccess) -> {
entityaccess.getPassengersAndSelf().forEach(this::unloadEntity);
}, true); // CraftBukkit - add boolean for event call
@@ -295,6 +305,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
private void processPendingLoads() {
+ org.spigotmc.AsyncCatcher.catchOp("Entity chunk process pending loads"); // Paper
ChunkEntities<T> chunkentities; // CraftBukkit - decompile error
while ((chunkentities = (ChunkEntities) this.loadingInbox.poll()) != null) {
@@ -311,6 +322,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
public void tick() {
+ org.spigotmc.AsyncCatcher.catchOp("Entity manager tick"); // Paper
this.processPendingLoads();
this.processUnloads();
}
@@ -331,6 +343,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
public void autoSave() {
+ org.spigotmc.AsyncCatcher.catchOp("Entity manager autosave"); // Paper
this.getAllChunksToSave().forEach((java.util.function.LongConsumer) (i) -> { // CraftBukkit - decompile error
boolean flag = this.chunkVisibility.get(i) == Visibility.HIDDEN;
@@ -345,6 +358,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
public void saveAll() {
+ org.spigotmc.AsyncCatcher.catchOp("Entity manager save"); // Paper
LongSet longset = this.getAllChunksToSave();
while (!longset.isEmpty()) {
@@ -452,6 +466,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
long i = SectionPos.asLong(blockposition);
if (i != this.currentSectionKey) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity move"); // Paper
Visibility visibility = this.currentSection.getStatus();
if (!this.currentSection.remove(this.entity)) {
@@ -506,6 +521,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
@Override
public void onRemove(Entity.RemovalReason reason) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity remove"); // Paper
if (!this.currentSection.remove(this.entity)) {
PersistentEntitySectionManager.LOGGER.warn("Entity {} wasn't found in section {} (destroying due to {})", new Object[]{this.entity, SectionPos.of(this.currentSectionKey), reason});
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index ca68faef4232859e833adfd86a0ce13f7c2ad00e..df57157317fc6c84f69751fd8a120761837429af 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -1737,6 +1737,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public void playSound(Location loc, Sound sound, org.bukkit.SoundCategory category, float volume, float pitch, long seed) {
+ org.spigotmc.AsyncCatcher.catchOp("play sound"); // Paper
if (loc == null || sound == null || category == null) return;
double x = loc.getX();
@@ -1748,6 +1749,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public void playSound(Location loc, String sound, org.bukkit.SoundCategory category, float volume, float pitch, long seed) {
+ org.spigotmc.AsyncCatcher.catchOp("play sound"); // Paper
if (loc == null || sound == null || category == null) return;
double x = loc.getX();
@@ -1780,6 +1782,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public void playSound(Entity entity, Sound sound, org.bukkit.SoundCategory category, float volume, float pitch, long seed) {
+ org.spigotmc.AsyncCatcher.catchOp("play sound"); // Paper
if (!(entity instanceof CraftEntity craftEntity) || entity.getWorld() != this || sound == null || category == null) return;
ClientboundSoundEntityPacket packet = new ClientboundSoundEntityPacket(CraftSound.bukkitToMinecraftHolder(sound), net.minecraft.sounds.SoundSource.valueOf(category.name()), craftEntity.getHandle(), volume, pitch, seed);
@@ -1791,6 +1794,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public void playSound(Entity entity, String sound, org.bukkit.SoundCategory category, float volume, float pitch, long seed) {
+ org.spigotmc.AsyncCatcher.catchOp("play sound"); // Paper
if (!(entity instanceof CraftEntity craftEntity) || entity.getWorld() != this || sound == null || category == null) return;
ClientboundSoundEntityPacket packet = new ClientboundSoundEntityPacket(Holder.direct(SoundEvent.createVariableRangeEvent(new ResourceLocation(sound))), net.minecraft.sounds.SoundSource.valueOf(category.name()), craftEntity.getHandle(), volume, pitch, seed);
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
index e1092bb514e01e978a8d624bd5a7cba5b4b5fe81..1170cf9b3b61fae099ed8574888187cf4f20ffd1 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
@@ -501,6 +501,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
@Override
public boolean addPotionEffect(PotionEffect effect, boolean force) {
+ org.spigotmc.AsyncCatcher.catchOp("effect add"); // Paper
this.getHandle().addEffect(org.bukkit.craftbukkit.potion.CraftPotionUtil.fromBukkit(effect), EntityPotionEffectEvent.Cause.PLUGIN); // Paper - Don't ignore icon
return true;
}
diff --git a/src/main/java/org/spigotmc/AsyncCatcher.java b/src/main/java/org/spigotmc/AsyncCatcher.java
index 78669fa035b7537ff7e533cf32aaf2995625424f..e8e3cc48cf1c58bd8151d1f28df28781859cd0e3 100644
--- a/src/main/java/org/spigotmc/AsyncCatcher.java
+++ b/src/main/java/org/spigotmc/AsyncCatcher.java
@@ -11,6 +11,7 @@ public class AsyncCatcher
{
if ( (AsyncCatcher.enabled || io.papermc.paper.util.TickThread.STRICT_THREAD_CHECKS) && Thread.currentThread() != MinecraftServer.getServer().serverThread ) // Paper
{
+ MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable()); // Paper
throw new IllegalStateException( "Asynchronous " + reason + "!" );
}
}