diff --git a/paper-server/patches/sources/net/minecraft/server/level/ChunkMap.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ChunkMap.java.patch index 745432acd172..019773d1a7c5 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/ChunkMap.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/ChunkMap.java.patch @@ -263,7 +263,7 @@ } protected void addEntity(final Entity entity) { -+ org.spigotmc.AsyncCatcher.catchOp("entity track"); // Spigot ++ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(entity, "Cannot track entity async"); // Paper - block async calls + // Paper start - ignore and warn about illegal addEntity calls instead of crashing server + if (!entity.valid || entity.level() != this.level || this.entityMap.containsKey(entity.getId())) { + LOGGER.error("Illegal ChunkMap::addEntity for world " + io.papermc.paper.util.MCUtil.getLevelName(this.level) @@ -283,7 +283,7 @@ } protected void removeEntity(final Entity entity) { -+ org.spigotmc.AsyncCatcher.catchOp("entity untrack"); // Spigot ++ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(entity, "Cannot untrack entity async"); // Paper - block async calls if (entity instanceof ServerPlayer player) { this.updatePlayerStatus(player, false); @@ -304,7 +304,7 @@ } public void removePlayer(final ServerPlayer player) { -+ org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot ++ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.entity, "Cannot remove tracking player async"); // Paper - block async calls if (this.seenBy.remove(player.connection)) { this.serverEntity.removePairing(player); if (this.seenBy.isEmpty()) { @@ -312,7 +312,7 @@ } public void updatePlayer(final ServerPlayer player) { -+ org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot ++ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.entity, "Cannot update tracking player async"); // Paper - block async calls if (player != this.entity) { - Vec3 deltaToPlayer = player.position().subtract(this.entity.position()); + // Paper start - remove allocation of Vec3D here diff --git a/paper-server/patches/sources/net/minecraft/server/level/ServerLevel.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ServerLevel.java.patch index 376be7a4b144..14800ca78b16 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/ServerLevel.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/ServerLevel.java.patch @@ -646,7 +646,7 @@ - private boolean addEntity(final Entity entity) { + // CraftBukkit start + private boolean addEntity(final Entity entity, final org.bukkit.event.entity.CreatureSpawnEvent.@Nullable SpawnReason spawnReason) { -+ org.spigotmc.AsyncCatcher.catchOp("entity add"); // Spigot ++ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(entity, "Cannot add entity async"); // Paper - block async calls + entity.generation = false; // Paper - Don't fire sync event during generation; Reset flag if it was added during a ServerLevel generation process + // Paper start - extra debug info + if (entity.valid) { @@ -1139,7 +1139,7 @@ @Override public void onTrackingStart(final Entity entity) { - ServerLevel.this.getChunkSource().addEntity(entity); -+ org.spigotmc.AsyncCatcher.catchOp("entity register"); // Spigot ++ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(entity, "Cannot start tracking entity async"); // Paper - block async calls + // ServerLevel.this.getChunkSource().addEntity(entity); // Paper - ignore and warn about illegal addEntity calls instead of crashing server; moved down below valid=true if (entity instanceof ServerPlayer player) { ServerLevel.this.players.add(player); diff --git a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch index fc4746f898f7..628c19a5be2c 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch @@ -210,6 +210,14 @@ if (enderPearl.isRemoved()) { LOGGER.warn("Trying to save removed ender pearl, skipping"); } else { +@@ -548,6 +_,7 @@ + } + + public void initMenu(final AbstractContainerMenu container) { ++ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this, "Cannot init menu async"); // Paper - block async calls + container.addSlotListener(this.containerListener); + container.setSynchronizer(this.containerSynchronizer); + } @@ -580,6 +_,11 @@ @Override @@ -432,7 +440,7 @@ this.connection .send( new ClientboundPlayerCombatKillPacket(this.getId(), deathMessage), -@@ -903,6 +_,65 @@ +@@ -903,6 +_,66 @@ } ) ); @@ -443,6 +451,7 @@ + // Paper end - Expand PlayerDeathEvent API + @Override + public void die(final DamageSource source) { ++ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this, "Cannot die async"); // Paper - block async calls + // this.gameEvent(GameEvent.ENTITY_DIE); // Paper - move below event cancellation check + boolean showDeathMessage = this.level().getGameRules().get(GameRules.SHOW_DEATH_MESSAGES); + // CraftBukkit start - fire PlayerDeathEvent @@ -963,7 +972,7 @@ } @Override -@@ -1323,8 +_,9 @@ +@@ -1323,22 +_,51 @@ this.connection.send(new ClientboundShowDialogPacket(dialog)); } @@ -974,7 +983,9 @@ } @Override -@@ -1333,12 +_,39 @@ + public OptionalInt openMenu(final @Nullable MenuProvider provider) { ++ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this, "Cannot open menu async"); // Paper - block async calls + if (provider == null) { return OptionalInt.empty(); } @@ -1087,7 +1098,7 @@ this.initMenu(this.containerMenu); } -@@ -1409,10 +_,30 @@ +@@ -1409,10 +_,31 @@ @Override public void closeContainer() { @@ -1096,6 +1107,7 @@ + } + @Override + public void closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) { ++ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this, "Cannot close container async"); // Paper - block async calls + org.bukkit.craftbukkit.event.CraftEventFactory.handleInventoryCloseEvent(this, reason); // CraftBukkit + // Paper end - Inventory close reason this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId)); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/state/BlockBehaviour.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/state/BlockBehaviour.java.patch index b74db39b6f37..f42b41242e1b 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/state/BlockBehaviour.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/state/BlockBehaviour.java.patch @@ -4,7 +4,7 @@ } protected void onPlace(final BlockState state, final Level level, final BlockPos pos, final BlockState oldState, final boolean movedByPiston) { -+ org.spigotmc.AsyncCatcher.catchOp("block onPlace"); // Spigot ++ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(level, pos, "Cannot place block async"); // Paper - block async calls } protected void affectNeighborsAfterRemoval(final BlockState state, final ServerLevel level, final BlockPos pos, final boolean movedByPiston) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index 335de83ba3e4..1ca4e450da31 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -2,6 +2,7 @@ import ca.spottedleaf.moonrise.common.list.ReferenceList; import ca.spottedleaf.moonrise.common.util.CoordinateUtils; +import ca.spottedleaf.moonrise.common.util.TickThread; import com.google.common.base.Preconditions; import com.google.common.base.Predicates; import com.google.common.collect.Lists; @@ -1550,7 +1551,7 @@ public void playSound(Location loc, String sound, org.bukkit.SoundCategory categ @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 + TickThread.ensureTickThread(this.getHandle(), CraftLocation.toBlockPos(loc), "Cannot play sound async"); if (loc == null || sound == null || category == null) return; double x = loc.getX(); @@ -1562,7 +1563,7 @@ public void playSound(Location loc, Sound sound, org.bukkit.SoundCategory catego @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 + TickThread.ensureTickThread(this.getHandle(), CraftLocation.toBlockPos(loc), "Cannot play sound async"); if (loc == null || sound == null || category == null) return; double x = loc.getX(); @@ -1585,8 +1586,8 @@ public void playSound(Entity entity, String sound, org.bukkit.SoundCategory cate @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; + TickThread.ensureTickThread(craftEntity.getHandleRaw(), "Cannot play sound async"); ClientboundSoundEntityPacket packet = new ClientboundSoundEntityPacket(CraftSound.bukkitToMinecraftHolder(sound), net.minecraft.sounds.SoundSource.valueOf(category.name()), craftEntity.getHandle(), volume, pitch, seed); ChunkMap.TrackedEntity entityTracker = this.getHandle().getChunkSource().chunkMap.entityMap.get(entity.getEntityId()); @@ -1606,8 +1607,8 @@ public void playSound(final net.kyori.adventure.sound.Sound sound) { @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; + TickThread.ensureTickThread(craftEntity.getHandleRaw(), "Cannot play sound async"); ClientboundSoundEntityPacket packet = new ClientboundSoundEntityPacket(Holder.direct(SoundEvent.createVariableRangeEvent(Identifier.parse(sound))), net.minecraft.sounds.SoundSource.valueOf(category.name()), craftEntity.getHandle(), volume, pitch, seed); ChunkMap.TrackedEntity entityTracker = this.getHandle().getChunkSource().chunkMap.entityMap.get(entity.getEntityId()); @@ -1618,7 +1619,7 @@ public void playSound(Entity entity, String sound, org.bukkit.SoundCategory cate @Override public void playSound(final net.kyori.adventure.sound.Sound sound, final double x, final double y, final double z) { - org.spigotmc.AsyncCatcher.catchOp("play sound"); // Paper + TickThread.ensureTickThread(this.getHandle(), Mth.floor(x) >> 4, Mth.floor(z) >> 4, "Cannot play sound async"); io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, x, y, z, sound.seed().orElseGet(this.world.getRandom()::nextLong), this.playSound0(x, y, z)); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java index c3074d51faec..785fe1705421 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.entity; +import ca.spottedleaf.moonrise.common.util.TickThread; import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.base.Predicates; @@ -371,7 +372,7 @@ public static LookAnchor toApiAnchor(net.minecraft.commands.arguments.EntityAnch @Override public List getNearbyEntities(double x, double y, double z) { Preconditions.checkState(!this.entity.generation, "Cannot get nearby entities during world generation"); - org.spigotmc.AsyncCatcher.catchOp("getNearbyEntities"); // Spigot + TickThread.ensureTickThread(this.entity, "Cannot get nearby entities async"); List entities = this.getHandle().level().getEntities(this.entity, this.entity.getBoundingBox().inflate(x, y, z), Predicates.alwaysTrue()); List result = new java.util.ArrayList<>(entities.size()); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java index 2aea80487ff1..d54deaa9f12d 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.entity; +import ca.spottedleaf.moonrise.common.util.TickThread; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import io.papermc.paper.adventure.PaperAdventure; @@ -326,6 +327,7 @@ public InventoryView getOpenInventory() { @Override public InventoryView openInventory(Inventory inventory) { if (!(this.getHandle() instanceof ServerPlayer)) return null; + TickThread.ensureTickThread(this.entity, "Cannot open inventory async"); ServerPlayer player = (ServerPlayer) this.getHandle(); AbstractContainerMenu formerContainer = this.getHandle().containerMenu; @@ -445,6 +447,7 @@ public void openInventory(InventoryView inventory) { Preconditions.checkArgument(this.equals(inventory.getPlayer()), "InventoryView must belong to the opening player"); if (!(this.getHandle() instanceof ServerPlayer)) return; // TODO: NPC support? if (((ServerPlayer) this.getHandle()).connection == null) return; + TickThread.ensureTickThread(this.entity, "Cannot open inventory view async"); if (this.getHandle().containerMenu != this.getHandle().inventoryMenu) { // fire INVENTORY_CLOSE if one already open ((ServerPlayer) this.getHandle()).connection.handleContainerClose(new ServerboundContainerClosePacket(this.getHandle().containerMenu.containerId), org.bukkit.event.inventory.InventoryCloseEvent.Reason.OPEN_NEW); // Paper - Inventory close reason @@ -501,6 +504,10 @@ private static int getMountInventoryColumns(final AbstractMountInventoryMenu mou @Override public InventoryView openMerchant(Merchant merchant, boolean force) { Preconditions.checkNotNull(merchant, "merchant cannot be null"); + if (merchant instanceof CraftEntity craftEntity) { + TickThread.ensureTickThread(craftEntity.entity, "Cannot open merchant screen async from merchant"); + } + TickThread.ensureTickThread(this.entity, "Cannot open merchant screen async"); if (!force && merchant.isTrading()) { return null; @@ -562,7 +569,7 @@ public InventoryView openStonecutter(Location location, boolean force) { } private InventoryView openInventory(Location location, boolean force, Material material) { - org.spigotmc.AsyncCatcher.catchOp("open" + material); + TickThread.ensureTickThread(this.entity, "Cannot open " + material + " inventory async"); if (location == null) { location = this.getLocation(); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java index fdde7295df23..aa915402a4c9 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.entity; +import ca.spottedleaf.moonrise.common.util.TickThread; import com.google.common.base.Preconditions; import com.google.common.collect.Sets; import io.papermc.paper.adventure.PaperAdventure; @@ -505,7 +506,7 @@ public void setKiller(Player killer) { @Override public boolean addPotionEffect(PotionEffect effect) { - org.spigotmc.AsyncCatcher.catchOp("effect add"); // Paper + TickThread.ensureTickThread(this.entity, "Cannot add potion effect async"); return this.getHandle().addEffect(org.bukkit.craftbukkit.potion.CraftPotionUtil.fromBukkit(effect), EntityPotionEffectEvent.Cause.PLUGIN); // Paper - Don't ignore icon } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index 41b77c94bad6..613baa1bf3f8 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.entity; +import ca.spottedleaf.moonrise.common.util.TickThread; import com.destroystokyo.paper.event.player.PlayerSetSpawnEvent; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; @@ -1340,6 +1341,7 @@ public void setSprinting(boolean sprinting) { @Override public void loadData() { + TickThread.ensureTickThread(this.entity, "Cannot load data async"); this.server.getHandle().playerIo.load(this.getHandle().nameAndId()) .map(tag -> TagValueInput.create(ProblemReporter.DISCARDING, this.server.getServer().registryAccess(), tag)) .ifPresent(this.getHandle()::load); @@ -1347,6 +1349,7 @@ public void loadData() { @Override public void saveData() { + TickThread.ensureTickThread(this.entity, "Cannot save data async"); this.server.getHandle().playerIo.save(this.getHandle()); } @@ -1663,6 +1666,7 @@ public void setGameMode(GameMode mode) { Preconditions.checkArgument(mode != null, "GameMode cannot be null"); if (this.getHandle().connection == null) return; + TickThread.ensureTickThread(this.entity, "Cannot set gamemode async"); this.getHandle().setGameMode(GameType.byId(mode.getValue()), org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.PLUGIN, null); // Paper - Expand PlayerGameModeChangeEvent }