diff --git a/patches/api-unmapped/Add-an-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch b/patches/api/Add-an-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch similarity index 100% rename from patches/api-unmapped/Add-an-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch rename to patches/api/Add-an-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch diff --git a/patches/api-unmapped/Add-ray-tracing-methods-to-LivingEntity.patch b/patches/api/Add-ray-tracing-methods-to-LivingEntity.patch similarity index 100% rename from patches/api-unmapped/Add-ray-tracing-methods-to-LivingEntity.patch rename to patches/api/Add-ray-tracing-methods-to-LivingEntity.patch diff --git a/patches/api-unmapped/Async-Chunks-API.patch b/patches/api/Async-Chunks-API.patch similarity index 99% rename from patches/api-unmapped/Async-Chunks-API.patch rename to patches/api/Async-Chunks-API.patch index 5626ed6096..df58dcaaf7 100644 --- a/patches/api-unmapped/Async-Chunks-API.patch +++ b/patches/api/Async-Chunks-API.patch @@ -12,8 +12,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/org/bukkit/World.java +++ b/src/main/java/org/bukkit/World.java @@ -0,0 +0,0 @@ public interface World extends PluginMessageRecipient, Metadatable, net.kyori.ad - public default Chunk getChunkAt(long chunkKey) { - return getChunkAt((int) chunkKey, (int) (chunkKey >> 32)); + } + return nearby; } + + /** diff --git a/patches/api-unmapped/Expose-attack-cooldown-methods-for-Player.patch b/patches/api/Expose-attack-cooldown-methods-for-Player.patch similarity index 100% rename from patches/api-unmapped/Expose-attack-cooldown-methods-for-Player.patch rename to patches/api/Expose-attack-cooldown-methods-for-Player.patch diff --git a/patches/api-unmapped/Improve-death-events.patch b/patches/api/Improve-death-events.patch similarity index 100% rename from patches/api-unmapped/Improve-death-events.patch rename to patches/api/Improve-death-events.patch diff --git a/patches/api-unmapped/Mob-Pathfinding-API.patch b/patches/api/Mob-Pathfinding-API.patch similarity index 100% rename from patches/api-unmapped/Mob-Pathfinding-API.patch rename to patches/api/Mob-Pathfinding-API.patch diff --git a/patches/server-remapped/Add-ray-tracing-methods-to-LivingEntity.patch b/patches/server-remapped/Add-ray-tracing-methods-to-LivingEntity.patch deleted file mode 100644 index 049e7280c1..0000000000 --- a/patches/server-remapped/Add-ray-tracing-methods-to-LivingEntity.patch +++ /dev/null @@ -1,99 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Mon, 3 Sep 2018 18:20:03 -0500 -Subject: [PATCH] Add ray tracing methods to LivingEntity - - -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity { - this.broadcastBreakEvent(hand == InteractionHand.MAIN_HAND ? EquipmentSlot.MAINHAND : EquipmentSlot.OFFHAND); - } - // Paper start -+ public HitResult getRayTrace(int maxDistance) { -+ return getRayTrace(maxDistance, ClipContext.Fluid.NONE); -+ } -+ -+ public HitResult getRayTrace(int maxDistance, ClipContext.Fluid fluidCollisionOption) { -+ if (maxDistance < 1 || maxDistance > 120) { -+ throw new IllegalArgumentException("maxDistance must be between 1-120"); -+ } -+ -+ Vec3 start = new Vec3(getX(), getY() + getEyeHeight(), getZ()); -+ org.bukkit.util.Vector dir = getBukkitEntity().getLocation().getDirection().multiply(maxDistance); -+ Vec3 end = new Vec3(start.x + dir.getX(), start.y + dir.getY(), start.z + dir.getZ()); -+ ClipContext raytrace = new ClipContext(start, end, ClipContext.Block.OUTLINE, fluidCollisionOption, this); -+ -+ return level.clip(raytrace); -+ } -+ - public int shieldBlockingDelay = level.paperConfig.shieldBlockingDelay; - - public int getShieldBlockingDelay() { -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -@@ -0,0 +0,0 @@ - package org.bukkit.craftbukkit.entity; - -+import com.destroystokyo.paper.block.TargetBlockInfo; - import com.google.common.base.Preconditions; - import com.google.common.collect.Sets; - import java.util.ArrayList; -@@ -0,0 +0,0 @@ import java.util.Iterator; - import java.util.List; - import java.util.Set; - import java.util.UUID; -+import net.minecraft.server.MCUtil; - import net.minecraft.server.level.ServerPlayer; - import net.minecraft.world.InteractionHand; - import net.minecraft.world.damagesource.DamageSource; -@@ -0,0 +0,0 @@ import net.minecraft.world.entity.projectile.ThrownEgg; - import net.minecraft.world.entity.projectile.ThrownEnderpearl; - import net.minecraft.world.entity.projectile.ThrownExperienceBottle; - import net.minecraft.world.entity.projectile.ThrownTrident; -+import net.minecraft.world.phys.BlockHitResult; -+import net.minecraft.world.phys.HitResult; - import org.apache.commons.lang.Validate; - import org.bukkit.FluidCollisionMode; - import org.bukkit.Location; -@@ -0,0 +0,0 @@ import org.bukkit.attribute.AttributeInstance; - import org.bukkit.block.Block; - import org.bukkit.craftbukkit.CraftServer; - import org.bukkit.craftbukkit.CraftWorld; -+import org.bukkit.craftbukkit.block.CraftBlock; - import org.bukkit.craftbukkit.entity.memory.CraftMemoryKey; - import org.bukkit.craftbukkit.entity.memory.CraftMemoryMapper; - import org.bukkit.craftbukkit.inventory.CraftEntityEquipment; -@@ -0,0 +0,0 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { - return blocks.get(0); - } - -+ // Paper start -+ @Override -+ public Block getTargetBlock(int maxDistance, TargetBlockInfo.FluidMode fluidMode) { -+ HitResult rayTrace = getHandle().getRayTrace(maxDistance, MCUtil.getNMSFluidCollisionOption(fluidMode)); -+ return !(rayTrace instanceof BlockHitResult) ? null : CraftBlock.at(getHandle().level, ((BlockHitResult)rayTrace).getBlockPos()); -+ } -+ -+ @Override -+ public org.bukkit.block.BlockFace getTargetBlockFace(int maxDistance, TargetBlockInfo.FluidMode fluidMode) { -+ HitResult rayTrace = getHandle().getRayTrace(maxDistance, MCUtil.getNMSFluidCollisionOption(fluidMode)); -+ return !(rayTrace instanceof BlockHitResult) ? null : MCUtil.toBukkitBlockFace(((BlockHitResult)rayTrace).getDirection()); -+ } -+ -+ @Override -+ public TargetBlockInfo getTargetBlockInfo(int maxDistance, TargetBlockInfo.FluidMode fluidMode) { -+ HitResult rayTrace = getHandle().getRayTrace(maxDistance, MCUtil.getNMSFluidCollisionOption(fluidMode)); -+ return !(rayTrace instanceof BlockHitResult) ? null : -+ new TargetBlockInfo(CraftBlock.at(getHandle().level, ((BlockHitResult)rayTrace).getBlockPos()), -+ MCUtil.toBukkitBlockFace(((BlockHitResult)rayTrace).getDirection())); -+ } -+ // Paper end -+ - @Override - public List getLastTwoTargetBlocks(Set transparent, int maxDistance) { - return getLineOfSight(transparent, maxDistance, 2); diff --git a/patches/server-remapped/Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch b/patches/server-remapped/Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch deleted file mode 100644 index f0f4e7f2e9..0000000000 --- a/patches/server-remapped/Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch +++ /dev/null @@ -1,97 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Wed, 12 Sep 2018 21:47:01 -0400 -Subject: [PATCH] Optimize Biome Mob Lookups for Mob Spawning - -Uses an EnumMap as well as a Set paired List for O(1) contains calls. - -diff --git a/src/main/java/net/minecraft/world/level/biome/MobSpawnSettings.java b/src/main/java/net/minecraft/world/level/biome/MobSpawnSettings.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/biome/MobSpawnSettings.java -+++ b/src/main/java/net/minecraft/world/level/biome/MobSpawnSettings.java -@@ -0,0 +0,0 @@ public class MobSpawnSettings { - }, (enumcreaturetype) -> { - return ImmutableList.of(); - })), ImmutableMap.of(), false); -+ // Paper start- decompile error workaround -+ private static class bProxy extends MobSpawnSettings.MobSpawnCost { -+ private bProxy(double gravityLimit, double mass) { -+ super(gravityLimit, mass); -+ } -+ } -+ private static class cProxy extends MobSpawnSettings.SpawnerData { -+ public cProxy(EntityType type, int weight, int minGroupSize, int maxGroupSize) { -+ super(type, weight, minGroupSize, maxGroupSize); -+ } -+ }; -+ // Paper end - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec((instance) -> { -- RecordCodecBuilder recordcodecbuilder = Codec.FLOAT.optionalFieldOf("creature_spawn_probability", 0.1F).forGetter((biomesettingsmobs) -> { -- return biomesettingsmobs.d; -+ RecordCodecBuilder recordcodecbuilder = Codec.FLOAT.optionalFieldOf("creature_spawn_probability", 0.1F).forGetter((biomesettingsmobs) -> { // Paper - add type to builder -+ return biomesettingsmobs.creatureGenerationProbability; - }); -- Codec codec = MobCategory.CODEC; -- Codec codec1 = BiomeSettingsMobs.c.b.listOf(); -- Logger logger = MobSpawnSettings.LOGGER; -- -- logger.getClass(); -- return instance.group(recordcodecbuilder, Codec.simpleMap(codec, codec1.promotePartial(Util.prefix("Spawn data: ", logger::error)), StringRepresentable.keys(MobCategory.values())).fieldOf("spawners").forGetter((biomesettingsmobs) -> { -- return biomesettingsmobs.e; -- }), Codec.simpleMap(Registry.ENTITY_TYPE, BiomeSettingsMobs.b.a, Registry.ENTITY_TYPE).fieldOf("spawn_costs").forGetter((biomesettingsmobs) -> { -- return biomesettingsmobs.f; -+ // Paper - remove unused vars -+ -+ return instance.group(recordcodecbuilder, Codec.simpleMap(MobCategory.CODEC, cProxy.CODEC.listOf().promotePartial(Util.prefix("Spawn data: ", MobSpawnSettings.LOGGER::error)), StringRepresentable.keys(MobCategory.values())).fieldOf("spawners").forGetter((biomesettingsmobs) -> { // Paper - inline codec, cProxy, LOGGER -+ return biomesettingsmobs.spawners; -+ }), Codec.simpleMap(Registry.ENTITY_TYPE, bProxy.CODEC, Registry.ENTITY_TYPE).fieldOf("spawn_costs").forGetter((biomesettingsmobs) -> { // Paper - decompile error - bProxy -+ return biomesettingsmobs.mobSpawnCosts; - }), Codec.BOOL.fieldOf("player_spawn_friendly").orElse(false).forGetter(MobSpawnSettings::playerSpawnFriendly)).apply(instance, MobSpawnSettings::new); - }); - private final float creatureGenerationProbability; -@@ -0,0 +0,0 @@ public class MobSpawnSettings { - - public static class Builder { - -- private final Map> spawners = (Map) Stream.of(MobCategory.values()).collect(ImmutableMap.toImmutableMap((enumcreaturetype) -> { -+ // Paper start - keep track of data in a pair set to give O(1) contains calls - we have to hook removals incase plugins mess with it -+ public static class MobList extends java.util.ArrayList { -+ java.util.Set biomes = new java.util.HashSet<>(); -+ -+ @Override -+ public boolean contains(Object o) { -+ return biomes.contains(o); -+ } -+ -+ @Override -+ public boolean add(MobSpawnSettings.SpawnerData BiomeSettingsMobs) { -+ biomes.add(BiomeSettingsMobs); -+ return super.add(BiomeSettingsMobs); -+ } -+ -+ @Override -+ public MobSpawnSettings.SpawnerData remove(int index) { -+ MobSpawnSettings.SpawnerData removed = super.remove(index); -+ if (removed != null) { -+ biomes.remove(removed); -+ } -+ return removed; -+ } -+ -+ @Override -+ public void clear() { -+ biomes.clear(); -+ super.clear(); -+ } -+ } -+ // use toImmutableEnumMap collector -+ private final Map> spawners = (Map) Stream.of(MobCategory.values()).collect(Maps.toImmutableEnumMap((enumcreaturetype) -> { - return enumcreaturetype; - }, (enumcreaturetype) -> { -- return Lists.newArrayList(); -+ return new MobList(); // Use MobList instead of ArrayList - })); -+ // Paper end - private final Map, MobSpawnSettings.MobSpawnCost> mobSpawnCosts = Maps.newLinkedHashMap(); - private float creatureGenerationProbability = 0.1F; - private boolean playerCanSpawn; diff --git a/patches/server-remapped/Prevent-Mob-AI-Rules-from-Loading-Chunks.patch b/patches/server-remapped/Prevent-Mob-AI-Rules-from-Loading-Chunks.patch deleted file mode 100644 index 6ff3c0a218..0000000000 --- a/patches/server-remapped/Prevent-Mob-AI-Rules-from-Loading-Chunks.patch +++ /dev/null @@ -1,83 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Mon, 10 Sep 2018 23:56:36 -0400 -Subject: [PATCH] Prevent Mob AI Rules from Loading Chunks - - -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java -@@ -0,0 +0,0 @@ public class RemoveBlockGoal extends MoveToBlockGoal { - private final Block blockToRemove; - private final Mob removerMob; - private int ticksSinceReachedGoal; -+ private Level world; // Paper - - public RemoveBlockGoal(Block targetBlock, PathfinderMob mob, double speed, int maxYDifference) { - super(mob, speed, 24, maxYDifference); - this.blockToRemove = targetBlock; - this.removerMob = mob; -+ this.world = mob.level; // Paper - } - - @Override -@@ -0,0 +0,0 @@ public class RemoveBlockGoal extends MoveToBlockGoal { - - @Nullable - private BlockPos getPosWithBlock(BlockPos pos, BlockGetter world) { -- if (world.getBlockState(pos).is(this.blockToRemove)) { -+ Block block = world.getBlockIfLoaded(pos); // Paper -+ if (block == null) return null; // Paper -+ if (block.is(this.blockToRemove)) { // Paper - return pos; - } else { - BlockPos[] ablockposition = new BlockPos[]{pos.below(), pos.west(), pos.east(), pos.north(), pos.south(), pos.below().below()}; -@@ -0,0 +0,0 @@ public class RemoveBlockGoal extends MoveToBlockGoal { - for (int j = 0; j < i; ++j) { - BlockPos blockposition1 = ablockposition1[j]; - -- if (world.getBlockState(blockposition1).is(this.blockToRemove)) { -+ if (world.getBlockIfLoaded(blockposition1).is(this.blockToRemove)) { // Paper - return blockposition1; - } - } -@@ -0,0 +0,0 @@ public class RemoveBlockGoal extends MoveToBlockGoal { - - @Override - protected boolean isValidTarget(LevelReader world, BlockPos pos) { -- ChunkAccess ichunkaccess = world.getChunk(pos.getX() >> 4, pos.getZ() >> 4, ChunkStatus.FULL, false); -+ ChunkAccess ichunkaccess = world.getChunkIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4); // Paper - - return ichunkaccess == null ? false : ichunkaccess.getBlockState(pos).is(this.blockToRemove) && ichunkaccess.getBlockState(pos.above()).isAir() && ichunkaccess.getBlockState(pos.above(2)).isAir(); - } -diff --git a/src/main/java/net/minecraft/world/entity/ai/util/RandomPos.java b/src/main/java/net/minecraft/world/entity/ai/util/RandomPos.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/util/RandomPos.java -+++ b/src/main/java/net/minecraft/world/entity/ai/util/RandomPos.java -@@ -0,0 +0,0 @@ import net.minecraft.util.Mth; - import net.minecraft.world.entity.PathfinderMob; - import net.minecraft.world.entity.ai.navigation.PathNavigation; - import net.minecraft.world.level.BlockGetter; -+import net.minecraft.world.level.material.FluidState; - import net.minecraft.world.level.pathfinder.BlockPathTypes; - import net.minecraft.world.level.pathfinder.WalkNodeEvaluator; - import net.minecraft.world.phys.Vec3; -@@ -0,0 +0,0 @@ public class RandomPos { - } - - blockposition2 = new BlockPos((double) k1 + mob.getX(), (double) l1 + mob.getY(), (double) i2 + mob.getZ()); -+ if (!mob.level.hasChunkAt(blockposition2)) continue; // Paper - if (blockposition2.getY() >= 0 && blockposition2.getY() <= mob.level.getMaxBuildHeight() && (!flag3 || mob.isWithinRestriction(blockposition2)) && (!validPositionsOnly || navigationabstract.isStableDestination(blockposition2))) { - if (aboveGround) { - blockposition2 = moveUpToAboveSolid(blockposition2, random.nextInt(distanceAboveGroundRange + 1) + minDistanceAboveGround, mob.level.getMaxBuildHeight(), (blockposition3) -> { -@@ -0,0 +0,0 @@ public class RandomPos { - }); - } - -- if (notInWater || !mob.level.getFluidState(blockposition2).is((Tag) FluidTags.WATER)) { -+ FluidState fluid = mob.level.getFluidIfLoaded(blockposition2); // Paper -+ if (notInWater || (fluid != null && !fluid.is((Tag) FluidTags.WATER))) { // Paper - BlockPathTypes pathtype = WalkNodeEvaluator.getBlockPathTypeStatic((BlockGetter) mob.level, blockposition2.mutable()); - - if (mob.getPathfindingMalus(pathtype) == 0.0F) { diff --git a/patches/server/Add-ray-tracing-methods-to-LivingEntity.patch b/patches/server/Add-ray-tracing-methods-to-LivingEntity.patch new file mode 100644 index 0000000000..cf6261cfad --- /dev/null +++ b/patches/server/Add-ray-tracing-methods-to-LivingEntity.patch @@ -0,0 +1,90 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: BillyGalbreath +Date: Mon, 3 Sep 2018 18:20:03 -0500 +Subject: [PATCH] Add ray tracing methods to LivingEntity + + +diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/server/MCUtil.java ++++ b/src/main/java/net/minecraft/server/MCUtil.java +@@ -0,0 +0,0 @@ public final class MCUtil { + return getNMSWorld(entity.getWorld()); + } + ++ public static ClipContext.Fluid getNMSFluidCollisionOption(com.destroystokyo.paper.block.TargetBlockInfo.FluidMode fluidMode) { ++ switch (fluidMode) { ++ case NEVER: ++ return ClipContext.Fluid.NONE; ++ case SOURCE_ONLY: ++ return ClipContext.Fluid.SOURCE_ONLY; ++ case ALWAYS: ++ return ClipContext.Fluid.ANY; ++ } ++ return null; ++ } ++ + public static BlockFace toBukkitBlockFace(Direction enumDirection) { + switch (enumDirection) { + case DOWN: +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity { + } + + // Paper start ++ public HitResult getRayTrace(int maxDistance) { ++ return getRayTrace(maxDistance, ClipContext.Fluid.NONE); ++ } ++ ++ public HitResult getRayTrace(int maxDistance, ClipContext.Fluid fluidCollisionOption) { ++ if (maxDistance < 1 || maxDistance > 120) { ++ throw new IllegalArgumentException("maxDistance must be between 1-120"); ++ } ++ ++ Vec3 start = new Vec3(getX(), getY() + getEyeHeight(), getZ()); ++ org.bukkit.util.Vector dir = getBukkitEntity().getLocation().getDirection().multiply(maxDistance); ++ Vec3 end = new Vec3(start.x + dir.getX(), start.y + dir.getY(), start.z + dir.getZ()); ++ ClipContext raytrace = new ClipContext(start, end, ClipContext.Block.OUTLINE, fluidCollisionOption, this); ++ ++ return level.clip(raytrace); ++ } ++ + public int shieldBlockingDelay = level.paperConfig.shieldBlockingDelay; + + public int getShieldBlockingDelay() { +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +@@ -0,0 +0,0 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { + return blocks.get(0); + } + ++ // Paper start ++ @Override ++ public Block getTargetBlock(int maxDistance, com.destroystokyo.paper.block.TargetBlockInfo.FluidMode fluidMode) { ++ net.minecraft.world.phys.HitResult rayTrace = getHandle().getRayTrace(maxDistance, net.minecraft.server.MCUtil.getNMSFluidCollisionOption(fluidMode)); ++ return !(rayTrace instanceof net.minecraft.world.phys.BlockHitResult) ? null : org.bukkit.craftbukkit.block.CraftBlock.at(getHandle().level, ((net.minecraft.world.phys.BlockHitResult)rayTrace).getBlockPos()); ++ } ++ ++ @Override ++ public org.bukkit.block.BlockFace getTargetBlockFace(int maxDistance, com.destroystokyo.paper.block.TargetBlockInfo.FluidMode fluidMode) { ++ net.minecraft.world.phys.HitResult rayTrace = getHandle().getRayTrace(maxDistance, net.minecraft.server.MCUtil.getNMSFluidCollisionOption(fluidMode)); ++ return !(rayTrace instanceof net.minecraft.world.phys.BlockHitResult) ? null : net.minecraft.server.MCUtil.toBukkitBlockFace(((net.minecraft.world.phys.BlockHitResult)rayTrace).getDirection()); ++ } ++ ++ @Override ++ public com.destroystokyo.paper.block.TargetBlockInfo getTargetBlockInfo(int maxDistance, com.destroystokyo.paper.block.TargetBlockInfo.FluidMode fluidMode) { ++ net.minecraft.world.phys.HitResult rayTrace = getHandle().getRayTrace(maxDistance, net.minecraft.server.MCUtil.getNMSFluidCollisionOption(fluidMode)); ++ return !(rayTrace instanceof net.minecraft.world.phys.BlockHitResult) ? null : ++ new com.destroystokyo.paper.block.TargetBlockInfo(org.bukkit.craftbukkit.block.CraftBlock.at(getHandle().level, ((net.minecraft.world.phys.BlockHitResult)rayTrace).getBlockPos()), ++ net.minecraft.server.MCUtil.toBukkitBlockFace(((net.minecraft.world.phys.BlockHitResult)rayTrace).getDirection())); ++ } ++ // Paper end ++ + @Override + public List getLastTwoTargetBlocks(Set transparent, int maxDistance) { + return this.getLineOfSight(transparent, maxDistance, 2); diff --git a/patches/server-remapped/Allow-chests-to-be-placed-with-NBT-data.patch b/patches/server/Allow-chests-to-be-placed-with-NBT-data.patch similarity index 98% rename from patches/server-remapped/Allow-chests-to-be-placed-with-NBT-data.patch rename to patches/server/Allow-chests-to-be-placed-with-NBT-data.patch index 433bb78a67..0043f8dedc 100644 --- a/patches/server-remapped/Allow-chests-to-be-placed-with-NBT-data.patch +++ b/patches/server/Allow-chests-to-be-placed-with-NBT-data.patch @@ -20,7 +20,7 @@ diff --git a/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEnti index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java -@@ -0,0 +0,0 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity { // Pape +@@ -0,0 +0,0 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement // CraftBukkit start @Override public boolean onlyOpCanSetNbt() { diff --git a/patches/server-remapped/Asynchronous-chunk-IO-and-loading.patch b/patches/server/Asynchronous-chunk-IO-and-loading.patch similarity index 76% rename from patches/server-remapped/Asynchronous-chunk-IO-and-loading.patch rename to patches/server/Asynchronous-chunk-IO-and-loading.patch index 1fa887d39b..42e8f47527 100644 --- a/patches/server-remapped/Asynchronous-chunk-IO-and-loading.patch +++ b/patches/server/Asynchronous-chunk-IO-and-loading.patch @@ -132,7 +132,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + public final Timing chunkUnload; + public final Timing poiSaveDataSerialization; + public final Timing chunkSave; -+ public final Timing chunkSaveOverwriteCheck; + public final Timing chunkSaveDataSerialization; + public final Timing chunkSaveIOWait; + public final Timing chunkUnloadPrepareSave; @@ -151,7 +150,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + chunkUnload = Timings.ofSafe(name + "Chunk unload - Chunk"); + poiSaveDataSerialization = Timings.ofSafe(name + "Chunk save - POI Data serialization"); + chunkSave = Timings.ofSafe(name + "Chunk save - Chunk"); -+ chunkSaveOverwriteCheck = Timings.ofSafe(name + "Chunk save - Chunk Overwrite Check"); + chunkSaveDataSerialization = Timings.ofSafe(name + "Chunk save - Chunk Data serialization"); + chunkSaveIOWait = Timings.ofSafe(name + "Chunk save - Chunk IO Wait"); + chunkUnloadPrepareSave = Timings.ofSafe(name + "Chunk unload - Async Save Prepare"); @@ -160,36 +158,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } public static Timing getTickList(ServerLevel worldserver, String timingsType) { -diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/com/destroystokyo/paper/PaperCommand.java -+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java -@@ -0,0 +0,0 @@ - package com.destroystokyo.paper; - -+import com.destroystokyo.paper.io.chunk.ChunkTaskManager; - import com.google.common.base.Functions; - import com.google.common.base.Joiner; - import com.google.common.collect.ImmutableSet; -@@ -0,0 +0,0 @@ import java.util.stream.Collectors; - - public class PaperCommand extends Command { - private static final String BASE_PERM = "bukkit.command.paper."; -- private static final ImmutableSet SUBCOMMANDS = ImmutableSet.builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo").build(); -+ private static final ImmutableSet SUBCOMMANDS = ImmutableSet.builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo", "dumpwaiting").build(); - - public PaperCommand(String name) { - super(name); -@@ -0,0 +0,0 @@ public class PaperCommand extends Command { - case "debug": - doDebug(sender, args); - break; -+ case "dumpwaiting": -+ ChunkTaskManager.dumpAllChunkLoadInfo(); -+ break; - case "chunkinfo": - doChunkInfo(sender, args); - break; diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -202,8 +170,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 import com.google.common.base.Throwables; @@ -0,0 +0,0 @@ public class PaperConfig { - maxBookPageSize = getInt("settings.book-size.page-max", maxBookPageSize); - maxBookTotalSizeMultiplier = getDouble("settings.book-size.total-multiplier", maxBookTotalSizeMultiplier); + } + tabSpamLimit = getInt("settings.spam-limiter.tab-spam-limit", tabSpamLimit); } + + public static boolean asyncChunks = false; @@ -1577,13 +1545,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + + try { -+ this.world.getChunkSource().chunkMap.updateChunkStatusOnDisk(chunkPos, chunkData.chunkData); -+ } catch (final Throwable ex) { -+ PaperFileIOThread.LOGGER.warn("Failed to update chunk status cache for task: " + this.toString(), ex); -+ // non-fatal, continue -+ } -+ -+ try { + chunkHolder = ChunkSerializer.loadChunk(this.world, + chunkManager.structureManager, chunkManager.getVillagePlace(), chunkPos, + chunkData.chunkData, true); @@ -1911,7 +1872,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + if (chunkHolder == null) { + PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Holder - null for (" + x +"," + z +")"); + } else { -+ ChunkAccess chunk = chunkHolder.getAvailableChunkNow(); ++ ChunkAccess chunk = chunkHolder.getLastAvailable(); + ChunkStatus holderStatus = chunkHolder.getChunkHolderStatus(); + PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Holder - non-null"); + PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Status - " + ((chunk == null) ? "null chunk" : chunk.getStatus().toString())); @@ -2304,8 +2265,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/net/minecraft/network/protocol/game/ServerboundCommandSuggestionPacket.java +++ b/src/main/java/net/minecraft/network/protocol/game/ServerboundCommandSuggestionPacket.java @@ -0,0 +0,0 @@ public class ServerboundCommandSuggestionPacket implements Packet { DedicatedServer dedicatedserver1 = new DedicatedServer(optionset, datapackconfiguration1, thread, iregistrycustom_dimension, convertable_conversionsession, resourcepackrepository, datapackresources, null, dedicatedserversettings, DataFixers.getDataFixer(), minecraftsessionservice, gameprofilerepository, usercache, LoggerChunkProgressListener::new); @@ -2351,7 +2303,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop> future = this.getFutureIfPresentUnchecked(curr); + Either either = future.getNow(null); + if (either == null || !either.left().isPresent()) { @@ -2377,19 +2331,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + return curr; + } -+ return null; -+ } - // Paper end - public CompletableFuture> getFutureIfPresentUnchecked(ChunkStatus leastStatus) { + return null; + } @@ -0,0 +0,0 @@ public class ChunkHolder { - ChunkStatus chunkstatus = getStatus(this.oldTicketLevel); - ChunkStatus chunkstatus1 = getStatus(this.ticketLevel); + ChunkStatus chunkstatus = ChunkHolder.getStatus(this.oldTicketLevel); + ChunkStatus chunkstatus1 = ChunkHolder.getStatus(this.ticketLevel); boolean flag = this.oldTicketLevel <= ChunkMap.MAX_CHUNK_DISTANCE; - boolean flag1 = this.ticketLevel <= ChunkMap.MAX_CHUNK_DISTANCE; + boolean flag1 = this.ticketLevel <= ChunkMap.MAX_CHUNK_DISTANCE; // Paper - diff on change: (flag1 = new ticket level is in loadable range) - ChunkHolder.FullChunkStatus playerchunk_state = getFullChunkStatus(this.oldTicketLevel); - ChunkHolder.FullChunkStatus playerchunk_state1 = getFullChunkStatus(this.ticketLevel); + ChunkHolder.FullChunkStatus playerchunk_state = ChunkHolder.getFullChunkStatus(this.oldTicketLevel); + ChunkHolder.FullChunkStatus playerchunk_state1 = ChunkHolder.getFullChunkStatus(this.ticketLevel); // CraftBukkit start @@ -0,0 +0,0 @@ public class ChunkHolder { } @@ -2403,19 +2355,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + for (int i = flag1 ? chunkstatus1.getIndex() + 1 : 0; i <= chunkstatus.getIndex(); ++i) { completablefuture = (CompletableFuture) this.futures.get(i); - if (completablefuture != null) { + if (completablefuture == null) { diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -0,0 +0,0 @@ import net.minecraft.world.level.chunk.ProtoChunk; - import net.minecraft.world.level.chunk.UpgradeData; - import net.minecraft.world.level.chunk.storage.ChunkSerializer; - import net.minecraft.world.level.chunk.storage.ChunkStorage; -+import net.minecraft.world.level.chunk.storage.RegionFile; - import net.minecraft.world.level.levelgen.structure.StructureStart; - import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager; - import net.minecraft.world.level.storage.DimensionDataStorage; @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider private final ThreadedLevelLightEngine lightEngine; private final BlockableEventLoop mainThreadExecutor; @@ -2426,7 +2370,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 public final LongSet toDrop; private boolean modified; @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - public final ChunkProgressListener progressListener; + private final ChunkStatusUpdateListener chunkStatusListener; public final ChunkMap.ChunkDistanceManager distanceManager; private final AtomicInteger tickingGenerated; - private final StructureManager structureManager; @@ -2434,27 +2378,18 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 private final File storageFolder; private final PlayerMap playerMap; public final Int2ObjectMap entityMap; -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - this.lightEngine = new ThreadedLevelLightEngine(chunkProvider, this, this.level.dimensionType().hasSkyLight(), threadedmailbox1, this.queueSorter.getProcessor(threadedmailbox1, false)); - this.distanceManager = new ChunkMap.ChunkDistanceManager(workerExecutor, mainThreadExecutor); - this.overworldDataStorage = supplier; -- this.poiManager = new PoiManager(new File(this.storageFolder, "poi"), dataFixer, flag); -+ this.poiManager = new PoiManager(new File(this.storageFolder, "poi"), dataFixer, flag, this.level); // Paper - this.setViewDistance(i); - } - @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } @Nullable - protected ChunkHolder getUpdatingChunkIfPresent(long pos) { -+ public ChunkHolder getUpdatingChunkIfPresent(long pos) { // Paper ++ public final ChunkHolder getUpdatingChunkIfPresent(long pos) { // Paper - protected -> public return (ChunkHolder) this.updatingChunkMap.get(pos); } @Nullable - protected ChunkHolder getVisibleChunkIfPresent(long pos) { -+ public ChunkHolder getVisibleChunkIfPresent(long pos) { // Paper - protected -> public ++ public final ChunkHolder getVisibleChunkIfPresent(long pos) { // Paper - protected -> public return (ChunkHolder) this.visibleChunkMap.get(pos); } @@ -2519,7 +2454,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Paper start - async chunk save for unload + // Note: This is very unsafe to call if the chunk is still in use. -+ // This is also modeled after PlayerChunkMap#saveChunk(IChunkAccess, boolean), with the intentional difference being ++ // This is also modeled after PlayerChunkMap#save(IChunkAccess, boolean), with the intentional difference being + // serializing the chunk is left to a worker thread. + private void asyncSave(ChunkAccess chunk) { + ChunkPos chunkPos = chunk.getPos(); @@ -2537,24 +2472,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + + ChunkStatus chunkstatus = chunk.getStatus(); + -+ // Copied from PlayerChunkMap#saveChunk(IChunkAccess, boolean) ++ // Copied from PlayerChunkMap#save(IChunkAccess, boolean) + if (chunkstatus.getChunkType() != ChunkStatus.ChunkType.LEVELCHUNK) { -+ try (co.aikar.timings.Timing ignored1 = this.level.timings.chunkSaveOverwriteCheck.startTiming()) { // Paper -+ // Paper start - Optimize save by using status cache -+ try { -+ ChunkStatus statusOnDisk = this.getChunkStatusOnDisk(chunkPos); -+ if (statusOnDisk != null && statusOnDisk.getChunkType() == ChunkStatus.ChunkType.LEVELCHUNK) { -+ // Paper end -+ return; -+ } -+ -+ if (chunkstatus == ChunkStatus.EMPTY && chunk.getAllStarts().values().stream().noneMatch(StructureStart::e)) { -+ return; -+ } -+ } catch (IOException ex) { -+ ex.printStackTrace(); -+ return; -+ } ++ // Paper start - Optimize save by using status cache ++ if (chunkstatus == ChunkStatus.EMPTY && chunk.getAllStarts().values().stream().noneMatch(StructureStart::isValid)) { ++ return; + } + } + @@ -2566,33 +2488,35 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + this.level.asyncChunkTaskManager.scheduleChunkSave(chunkPos.x, chunkPos.z, com.destroystokyo.paper.io.PrioritizedTaskQueue.LOW_PRIORITY, + asyncSaveData, chunk); + -+ chunk.setLastSaveTime(this.level.getGameTime()); + chunk.setUnsaved(false); + } + // Paper end + - private void scheduleUnload(long pos, ChunkHolder playerchunk) { - CompletableFuture completablefuture = playerchunk.getChunkToSave(); + private void scheduleUnload(long pos, ChunkHolder holder) { + CompletableFuture completablefuture = holder.getChunkToSave(); Consumer consumer = (ichunkaccess) -> { // CraftBukkit - decompile error @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider ((LevelChunk) ichunkaccess).setLoaded(false); } - this.save(ichunkaccess); -+ //this.saveChunk(ichunkaccess);// Paper - delay ++ //this.save(ichunkaccess);// Paper - delay if (this.entitiesInLevel.remove(pos) && ichunkaccess instanceof LevelChunk) { LevelChunk chunk = (LevelChunk) ichunkaccess; -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + this.level.unload(chunk); } - this.autoSaveQueue.remove(playerchunk); // Paper ++ // Paper start - async chunk saving + try { -+ this.asyncSave(ichunkaccess); // Paper - async chunk saving ++ this.asyncSave(ichunkaccess); ++ } catch (ThreadDeath ex) { ++ throw ex; // bye + } catch (Throwable ex) { + LOGGER.fatal("Failed to prepare async save, attempting synchronous save", ex); + this.save(ichunkaccess); + } ++ // Paper end - async chunk saving + this.lightEngine.updateChunkStatus(ichunkaccess.getPos()); this.lightEngine.tryScheduleUpdate(); @@ -2614,26 +2538,26 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + if (ioThrowable != null) { + com.destroystokyo.paper.util.SneakyThrow.sneaky(ioThrowable); + } -+ + +- if (nbttagcompound != null) {try (Timing ignored2 = this.level.timings.chunkLoadLevelTimer.startTimingIfSync()) { // Paper start - timings +- boolean flag = nbttagcompound.contains("Level", 10) && nbttagcompound.getCompound("Level").contains("Status", 8); + this.getVillagePlace().loadInData(pos, chunkHolder.poiData); + chunkHolder.tasks.forEach(Runnable::run); + // Paper end -- if (nbttagcompound != null) {try (Timing ignored2 = this.level.timings.chunkLoadLevelTimer.startTimingIfSync()) { // Paper start - timings -- boolean flag = nbttagcompound.contains("Level", 10) && nbttagcompound.getCompound("Level").contains("Status", 8); -+ if (chunkHolder.protoChunk != null) {try (Timing ignored2 = this.level.timings.chunkLoadLevelTimer.startTimingIfSync()) { // Paper start - timings // Paper - chunk is created async - - if (flag) { - ProtoChunk protochunk = ChunkSerializer.read(this.level, this.structureManager, this.poiManager, pos, nbttagcompound); ++ if (chunkHolder.protoChunk != null) {try (Timing ignored2 = this.level.timings.chunkLoadLevelTimer.startTimingIfSync()) { // Paper start - timings // Paper - chunk is created async ++ + if (true) { + ProtoChunk protochunk = chunkHolder.protoChunk; - protochunk.setLastSaveTime(this.level.getGameTime()); this.markPosition(pos, protochunk.getStatus().getChunkType()); + return Either.left(protochunk); @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.markPositionReplaceable(pos); - return Either.left(new ProtoChunk(pos, UpgradeData.EMPTY, this.level)); // Paper - Anti-Xray - Add parameter + return Either.left(new ProtoChunk(pos, UpgradeData.EMPTY, this.level)); - }, this.mainThreadExecutor); + // Paper start - Async chunk io + }; @@ -2676,15 +2600,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 ChunkStatus chunkstatus = chunk.getStatus(); if (chunkstatus.getChunkType() != ChunkStatus.ChunkType.LEVELCHUNK) { -+ try (co.aikar.timings.Timing ignored1 = this.level.timings.chunkSaveOverwriteCheck.startTiming()) { // Paper - if (this.isExistingChunkFull(chunkcoordintpair)) { +- if (this.isExistingChunkFull(chunkcoordintpair)) { ++ if (false && this.isExistingChunkFull(chunkcoordintpair)) { // Paper return false; } + @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - if (chunkstatus == ChunkStatus.EMPTY && chunk.getAllStarts().values().stream().noneMatch(StructureStart::e)) { - return false; - } -+ } // Paper } this.level.getProfiler().incrementCounter("chunkSave"); @@ -2693,9 +2614,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + try (co.aikar.timings.Timing ignored1 = this.level.timings.chunkSaveDataSerialization.startTiming()) { // Paper + nbttagcompound = ChunkSerializer.write(this.level, chunk); + } // Paper ++ - this.write(chunkcoordintpair, nbttagcompound); -+ + // Paper start - async chunk io + com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.scheduleSave(this.level, chunkcoordintpair.x, chunkcoordintpair.z, + null, nbttagcompound, com.destroystokyo.paper.io.PrioritizedTaskQueue.NORMAL_PRIORITY); @@ -2745,117 +2666,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Paper end + @Nullable - public CompoundTag readChunk(ChunkPos pos) throws IOException { // Paper - private -> public + private CompoundTag readChunk(ChunkPos pos) throws IOException { CompoundTag nbttagcompound = this.read(pos); @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - // Paper start - chunk status cache "api" - public ChunkStatus getChunkStatusOnDiskIfCached(ChunkPos chunkPos) { -- RegionFile regionFile = this.getIOWorker().getRegionFileCache().getRegionFileIfLoaded(chunkPos); -+ synchronized (this) { // Paper -+ RegionFile regionFile = this.regionFileCache.getRegionFileIfLoaded(chunkPos); - - return regionFile == null ? null : regionFile.getStatusIfCached(chunkPos.x, chunkPos.z); -+ } // Paper - } - - public ChunkStatus getChunkStatusOnDisk(ChunkPos chunkPos) throws IOException { -- RegionFile regionFile = this.getIOWorker().getRegionFileCache().getFile(chunkPos, true); -+ // Paper start - async chunk save for unload -+ ChunkAccess unloadingChunk = this.level.asyncChunkTaskManager.getChunkInSaveProgress(chunkPos.x, chunkPos.z); -+ if (unloadingChunk != null) { -+ return unloadingChunk.getStatus(); -+ } -+ // Paper end -+ // Paper start - async io -+ CompoundTag inProgressWrite = com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE -+ .getPendingWrite(this.level, chunkPos.x, chunkPos.z, false); - -- if (regionFile == null || !regionFile.chunkExists(chunkPos)) { -- return null; -+ if (inProgressWrite != null) { -+ return ChunkSerializer.getStatus(inProgressWrite); - } -+ // Paper end -+ synchronized (this) { // Paper - async io -+ RegionFile regionFile = this.regionFileCache.getFile(chunkPos, true); -+ -+ if (regionFile == null || !regionFile.hasChunk(chunkPos)) { -+ return null; -+ } - -- ChunkStatus status = regionFile.getStatusIfCached(chunkPos.x, chunkPos.z); -+ ChunkStatus status = regionFile.getStatusIfCached(chunkPos.x, chunkPos.z); - -- if (status != null) { -- return status; -+ if (status != null) { -+ return status; -+ } -+ // Paper start - async io - } - -- this.readChunk(chunkPos); -+ CompoundTag compound = this.readChunk(chunkPos); - -- return regionFile.getStatusIfCached(chunkPos.x, chunkPos.z); -+ return ChunkSerializer.getStatus(compound); -+ // Paper end - } - - public void updateChunkStatusOnDisk(ChunkPos chunkPos, @Nullable CompoundTag compound) throws IOException { -- RegionFile regionFile = this.getIOWorker().getRegionFileCache().getFile(chunkPos, false); -+ synchronized (this) { -+ RegionFile regionFile = this.regionFileCache.getFile(chunkPos, false); - -- regionFile.setStatus(chunkPos.x, chunkPos.z, ChunkSerializer.getStatus(compound)); -+ regionFile.setStatus(chunkPos.x, chunkPos.z, ChunkSerializer.getStatus(compound)); -+ } - } - - public ChunkAccess getUnloadingChunk(int chunkX, int chunkZ) { -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - // Paper end - -+ -+ // Paper start - async io -+ // this function will not load chunk data off disk to check for status -+ // ret null for unknown, empty for empty status on disk or absent from disk -+ public ChunkStatus getStatusOnDiskNoLoad(int x, int z) { -+ // Paper start - async chunk save for unload -+ ChunkAccess unloadingChunk = this.level.asyncChunkTaskManager.getChunkInSaveProgress(x, z); -+ if (unloadingChunk != null) { -+ return unloadingChunk.getStatus(); -+ } -+ // Paper end -+ // Paper start - async io -+ CompoundTag inProgressWrite = com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE -+ .getPendingWrite(this.level, x, z, false); -+ -+ if (inProgressWrite != null) { -+ return ChunkSerializer.getStatus(inProgressWrite); -+ } -+ // Paper end -+ // variant of PlayerChunkMap#getChunkStatusOnDisk that does not load data off disk, but loads the region file -+ ChunkPos chunkPos = new ChunkPos(x, z); -+ synchronized (level.getChunkSource().chunkMap) { -+ RegionFile file; -+ try { -+ file = level.getChunkSource().chunkMap.regionFileCache.getFile(chunkPos, false); -+ } catch (IOException ex) { -+ throw new RuntimeException(ex); -+ } -+ -+ return !file.hasChunk(chunkPos) ? ChunkStatus.EMPTY : file.getStatusIfCached(x, z); -+ } -+ } -+ - boolean noPlayersCloseForSpawning(ChunkPos chunkcoordintpair) { - // Spigot start - return isOutsideOfRange(chunkcoordintpair, false); -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } + public PoiManager getVillagePlace() { return this.getPoiManager(); } // Paper - OBFHELPER @@ -2866,18 +2680,28 @@ diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/sr index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -0,0 +0,0 @@ import net.minecraft.world.level.chunk.ChunkAccess; - import net.minecraft.world.level.chunk.ChunkGenerator; - import net.minecraft.world.level.chunk.ChunkSource; - import net.minecraft.world.level.chunk.ChunkStatus; -+import net.minecraft.world.level.chunk.ImposterProtoChunk; - import net.minecraft.world.level.chunk.LevelChunk; - import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager; - import net.minecraft.world.level.storage.DimensionDataStorage; @@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource { - return playerChunk.getAvailableChunkNow(); - + final ServerLevel level; + public final Thread mainThread; // Paper - package-private -> public + final ThreadedLevelLightEngine lightEngine; +- private final ServerChunkCache.MainThreadExecutor mainThreadProcessor; ++ public final ServerChunkCache.MainThreadExecutor mainThreadProcessor; // Paper - private -> public + public final ChunkMap chunkMap; + private final DimensionDataStorage dataStorage; + private long lastInhabitedUpdate; +@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource { + return ret; } + // Paper end ++ // Paper start - async chunk io ++ public ChunkAccess getChunkAtImmediately(int x, int z) { ++ ChunkHolder holder = this.chunkMap.getVisibleChunkIfPresent(ChunkPos.asLong(x, z)); ++ if (holder == null) { ++ return null; ++ } ++ ++ return holder.getLastAvailable(); ++ } + + private long asyncLoadSeqCounter; + @@ -2896,12 +2720,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return future; + } + -+ if (!com.destroystokyo.paper.PaperConfig.asyncChunks) { -+ level.getWorld().loadChunk(x, z, gen); -+ LevelChunk chunk = getChunkAtIfLoadedMainThread(x, z); -+ return CompletableFuture.completedFuture(chunk != null ? Either.left(chunk) : ChunkHolder.UNLOADED_CHUNK); -+ } -+ + long k = ChunkPos.asLong(x, z); + ChunkPos chunkPos = new ChunkPos(x, z); + @@ -2936,35 +2754,22 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + + ChunkAccess current = this.getChunkAtImmediately(x, z); // we want to bypass ticket restrictions + if (current != null) { -+ if (!(current instanceof ImposterProtoChunk) && !(current instanceof LevelChunk)) { ++ if (!(current instanceof net.minecraft.world.level.chunk.ImposterProtoChunk) && !(current instanceof LevelChunk)) { + return CompletableFuture.completedFuture(ChunkHolder.UNLOADED_CHUNK); + } + // we know the chunk is at full status here (either in read-only mode or the real thing) + return this.bringToFullStatusAsync(x, z, chunkPos, isUrgent); + } + -+ ChunkStatus status = level.getChunkSource().chunkMap.getStatusOnDiskNoLoad(x, z); -+ -+ if (status != null && status != ChunkStatus.FULL) { -+ // does not exist on disk -+ return CompletableFuture.completedFuture(ChunkHolder.UNLOADED_CHUNK); -+ } -+ -+ if (status == ChunkStatus.FULL) { -+ return this.bringToFullStatusAsync(x, z, chunkPos, isUrgent); -+ } -+ -+ // status is null here -+ + // here we don't know what status it is and we're not supposed to generate + // so we asynchronously load empty status + return this.bringToStatusAsync(x, z, chunkPos, ChunkStatus.EMPTY, isUrgent).thenCompose((either) -> { + ChunkAccess chunk = either.left().orElse(null); -+ if (!(chunk instanceof ImposterProtoChunk) && !(chunk instanceof LevelChunk)) { ++ if (!(chunk instanceof net.minecraft.world.level.chunk.ImposterProtoChunk) && !(chunk instanceof LevelChunk)) { + // the chunk on disk was not a full status chunk + return CompletableFuture.completedFuture(ChunkHolder.UNLOADED_CHUNK); + } -+ ; // bring to full status if required ++ // bring to full status if required + return this.bringToFullStatusAsync(x, z, chunkPos, isUrgent); + }); + } @@ -2976,7 +2781,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + private CompletableFuture> bringToStatusAsync(int x, int z, ChunkPos chunkPos, ChunkStatus status, boolean isUrgent) { + CompletableFuture> future = this.getChunkFutureMainThread(x, z, status, true, isUrgent); + Long identifier = Long.valueOf(this.asyncLoadSeqCounter++); -+ int ticketLevel = MCUtil.getTicketLevelFor(status); ++ int ticketLevel = net.minecraft.server.MCUtil.getTicketLevelFor(status); + this.addTicketAtLevel(TicketType.ASYNC_LOAD, chunkPos, ticketLevel, identifier); + + return future.thenComposeAsync((Either either) -> { @@ -3004,7 +2809,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + public void removeTicketAtLevel(TicketType ticketType, ChunkPos chunkPos, int ticketLevel, T identifier) { + this.distanceManager.removeTicketAtLevel(ticketType, chunkPos, ticketLevel, identifier); + } - // Paper end ++ // Paper end - async chunk io @Nullable @Override @@ -3019,14 +2824,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 gameprofilerfiller.incrementCounter("getChunkCacheMiss"); - CompletableFuture> completablefuture = this.getChunkFutureMainThread(x, z, leastStatus, create); + CompletableFuture> completablefuture = this.getChunkFutureMainThread(x, z, leastStatus, create, true); // Paper + ServerChunkCache.MainThreadExecutor chunkproviderserver_a = this.mainThreadProcessor; + Objects.requireNonNull(completablefuture); if (!completablefuture.isDone()) { // Paper + // Paper start - async chunk io/loading + this.level.asyncChunkTaskManager.raisePriority(x1, z1, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY); + com.destroystokyo.paper.io.chunk.ChunkTaskManager.pushChunkWait(this.level, x1, z1); + // Paper end this.level.timings.syncChunkLoad.startTiming(); // Paper - this.mainThreadProcessor.managedBlock(completablefuture::isDone); + chunkproviderserver_a.managedBlock(completablefuture::isDone); + com.destroystokyo.paper.io.chunk.ChunkTaskManager.popChunkWait(); // Paper - async chunk debug this.level.timings.syncChunkLoad.stopTiming(); // Paper } // Paper @@ -3034,41 +2841,18 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource { } - private CompletableFuture> getChunkFutureMainThread(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create) { -- ChunkPos chunkcoordintpair = new ChunkPos(chunkX, chunkZ); + private CompletableFuture> getChunkFutureMainThread(int i, int j, ChunkStatus chunkstatus, boolean flag) { + // Paper start - add isUrgent - old sig left in place for dirty nms plugins -+ return getChunkFutureMainThread(chunkX, chunkZ, leastStatus, create, false); ++ return getChunkFutureMainThread(i, j, chunkstatus, flag, false); + } + private CompletableFuture> getChunkFutureMainThread(int i, int j, ChunkStatus chunkstatus, boolean flag, boolean isUrgent) { + // Paper end -+ ChunkPos chunkcoordintpair = new ChunkPos(i, j); + ChunkPos chunkcoordintpair = new ChunkPos(i, j); long k = chunkcoordintpair.toLong(); -- int l = 33 + ChunkStatus.getDistance(leastStatus); -+ int l = 33 + ChunkStatus.getDistance(chunkstatus); - ChunkHolder playerchunk = this.getVisibleChunkIfPresent(k); - - // CraftBukkit start - don't add new ticket for currently unloading chunk + int l = 33 + ChunkStatus.getDistance(chunkstatus); @@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource { - ChunkHolder.FullChunkStatus currentChunkState = ChunkHolder.getFullChunkStatus(playerchunk.getTicketLevel()); - currentlyUnloading = (oldChunkState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER) && !currentChunkState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)); - } -- if (create && !currentlyUnloading) { -+ if (flag && !currentlyUnloading) { - // CraftBukkit end - this.distanceManager.addTicket(TicketType.UNKNOWN, chunkcoordintpair, l, chunkcoordintpair); - if (this.chunkAbsent(playerchunk, l)) { -@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource { - } - } - -- return this.chunkAbsent(playerchunk, l) ? ChunkHolder.UNLOADED_CHUNK_FUTURE : playerchunk.getOrScheduleFuture(leastStatus, this.chunkMap); -+ return this.chunkAbsent(playerchunk, l) ? ChunkHolder.UNLOADED_CHUNK_FUTURE : playerchunk.getOrScheduleFuture(chunkstatus, this.chunkMap); - } - - private boolean chunkAbsent(@Nullable ChunkHolder holder, int maxLevel) { -@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource { - protected boolean pollTask() { // CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task + public boolean pollTask() { try { + boolean execChunkTask = com.destroystokyo.paper.io.chunk.ChunkTaskManager.pollChunkWaitQueue() || ServerChunkCache.this.level.asyncChunkTaskManager.pollNextChunkTask(); // Paper if (ServerChunkCache.this.runDistanceManagerUpdates()) { @@ -3084,22 +2868,6 @@ diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/mai index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -0,0 +0,0 @@ import net.minecraft.core.RegistryAccess; - import net.minecraft.core.SectionPos; - import net.minecraft.core.Vec3i; - import net.minecraft.core.particles.ParticleOptions; -+import net.minecraft.nbt.CompoundTag; - import net.minecraft.network.chat.Component; - import net.minecraft.network.chat.TranslatableComponent; - import net.minecraft.network.protocol.Packet; -@@ -0,0 +0,0 @@ import net.minecraft.world.level.chunk.ChunkGenerator; - import net.minecraft.world.level.chunk.ChunkStatus; - import net.minecraft.world.level.chunk.LevelChunk; - import net.minecraft.world.level.chunk.LevelChunkSection; -+import net.minecraft.world.level.chunk.storage.RegionFile; - import net.minecraft.world.level.dimension.DimensionType; - import net.minecraft.world.level.dimension.end.EndDragonFight; - import net.minecraft.world.level.levelgen.Heightmap; @@ -0,0 +0,0 @@ public class ServerLevel extends net.minecraft.world.level.Level implements Worl return this.chunkSource.getChunk(x, z, false); } @@ -3107,19 +2875,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Paper start - Asynchronous IO + public final com.destroystokyo.paper.io.PaperFileIOThread.ChunkDataController poiDataController = new com.destroystokyo.paper.io.PaperFileIOThread.ChunkDataController() { + @Override -+ public void writeData(int x, int z, CompoundTag compound) throws java.io.IOException { ++ public void writeData(int x, int z, net.minecraft.nbt.CompoundTag compound) throws java.io.IOException { + ServerLevel.this.getChunkSource().chunkMap.getVillagePlace().write(new ChunkPos(x, z), compound); + } + + @Override -+ public CompoundTag readData(int x, int z) throws java.io.IOException { ++ public net.minecraft.nbt.CompoundTag readData(int x, int z) throws java.io.IOException { + return ServerLevel.this.getChunkSource().chunkMap.getVillagePlace().read(new ChunkPos(x, z)); + } + + @Override -+ public T computeForRegionFile(int chunkX, int chunkZ, java.util.function.Function function) { ++ public T computeForRegionFile(int chunkX, int chunkZ, java.util.function.Function function) { + synchronized (ServerLevel.this.getChunkSource().chunkMap.getVillagePlace()) { -+ RegionFile file; ++ net.minecraft.world.level.chunk.storage.RegionFile file; + + try { + file = ServerLevel.this.getChunkSource().chunkMap.getVillagePlace().getFile(new ChunkPos(chunkX, chunkZ), false); @@ -3132,9 +2900,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + + @Override -+ public T computeForRegionFileIfLoaded(int chunkX, int chunkZ, java.util.function.Function function) { ++ public T computeForRegionFileIfLoaded(int chunkX, int chunkZ, java.util.function.Function function) { + synchronized (ServerLevel.this.getChunkSource().chunkMap.getVillagePlace()) { -+ RegionFile file = ServerLevel.this.getChunkSource().chunkMap.getVillagePlace().getRegionFileIfLoaded(new ChunkPos(chunkX, chunkZ)); ++ net.minecraft.world.level.chunk.storage.RegionFile file = ServerLevel.this.getChunkSource().chunkMap.getVillagePlace().getRegionFileIfLoaded(new ChunkPos(chunkX, chunkZ)); + return function.apply(file); + } + } @@ -3142,19 +2910,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + + public final com.destroystokyo.paper.io.PaperFileIOThread.ChunkDataController chunkDataController = new com.destroystokyo.paper.io.PaperFileIOThread.ChunkDataController() { + @Override -+ public void writeData(int x, int z, CompoundTag compound) throws java.io.IOException { ++ public void writeData(int x, int z, net.minecraft.nbt.CompoundTag compound) throws java.io.IOException { + ServerLevel.this.getChunkSource().chunkMap.write(new ChunkPos(x, z), compound); + } + + @Override -+ public CompoundTag readData(int x, int z) throws java.io.IOException { ++ public net.minecraft.nbt.CompoundTag readData(int x, int z) throws java.io.IOException { + return ServerLevel.this.getChunkSource().chunkMap.read(new ChunkPos(x, z)); + } + + @Override -+ public T computeForRegionFile(int chunkX, int chunkZ, java.util.function.Function function) { ++ public T computeForRegionFile(int chunkX, int chunkZ, java.util.function.Function function) { + synchronized (ServerLevel.this.getChunkSource().chunkMap) { -+ RegionFile file; ++ net.minecraft.world.level.chunk.storage.RegionFile file; + + try { + file = ServerLevel.this.getChunkSource().chunkMap.regionFileCache.getFile(new ChunkPos(chunkX, chunkZ), false); @@ -3167,9 +2935,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + + @Override -+ public T computeForRegionFileIfLoaded(int chunkX, int chunkZ, java.util.function.Function function) { ++ public T computeForRegionFileIfLoaded(int chunkX, int chunkZ, java.util.function.Function function) { + synchronized (ServerLevel.this.getChunkSource().chunkMap) { -+ RegionFile file = ServerLevel.this.getChunkSource().chunkMap.regionFileCache.getRegionFileIfLoaded(new ChunkPos(chunkX, chunkZ)); ++ net.minecraft.world.level.chunk.storage.RegionFile file = ServerLevel.this.getChunkSource().chunkMap.regionFileCache.getRegionFileIfLoaded(new ChunkPos(chunkX, chunkZ)); + return function.apply(file); + } + } @@ -3179,45 +2947,33 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Add env and gen to constructor, WorldData -> WorldDataServer public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, ServerLevelData iworlddataserver, ResourceKey resourcekey, DimensionType dimensionmanager, ChunkProgressListener worldloadlistener, ChunkGenerator chunkgenerator, boolean flag, long i, List list, boolean flag1, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen) { - super(iworlddataserver, resourcekey, dimensionmanager, minecraftserver::getProfiler, false, flag, i, gen, env, executor); // Paper pass executor + // Objects.requireNonNull(minecraftserver); // CraftBukkit - decompile error @@ -0,0 +0,0 @@ public class ServerLevel extends net.minecraft.world.level.Level implements Worl - this.dragonFight = null; - } + + this.sleepStatus = new SleepStatus(); this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit + + this.asyncChunkTaskManager = new com.destroystokyo.paper.io.chunk.ChunkTaskManager(this); // Paper } // CraftBukkit start -@@ -0,0 +0,0 @@ public class ServerLevel extends net.minecraft.world.level.Level implements Worl - } - - MCUtil.getSpiralOutChunks(spawn, radiusInBlocks >> 4).forEach(pair -> { -- getChunkSource().getChunkAtMainThread(pair.x, pair.z); -+ getChunkSource().getChunkAtAsynchronously(pair.x, pair.z, true, false).exceptionally((ex) -> { -+ ex.printStackTrace(); -+ return null; -+ }); - }); - } - public void removeTicketsForSpawn(int radiusInBlocks, BlockPos spawn) { diff --git a/src/main/java/net/minecraft/server/level/TicketType.java b/src/main/java/net/minecraft/server/level/TicketType.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/level/TicketType.java +++ b/src/main/java/net/minecraft/server/level/TicketType.java -@@ -0,0 +0,0 @@ public class TicketType { - public static final TicketType PLUGIN = create("plugin", (a, b) -> 0); // CraftBukkit - public static final TicketType PLUGIN_TICKET = create("plugin_ticket", (plugin1, plugin2) -> plugin1.getClass().getName().compareTo(plugin2.getClass().getName())); // CraftBukkit +@@ -0,0 +0,0 @@ import net.minecraft.world.level.ChunkPos; + + public class TicketType { public static final TicketType FUTURE_AWAIT = create("future_await", Long::compareTo); // Paper + public static final TicketType ASYNC_LOAD = create("async_load", Long::compareTo); // Paper - public static TicketType create(String name, Comparator comparator) { - return new TicketType<>(name, comparator, 0L); + private final String name; + private final Comparator comparator; diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener { +@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser server.scheduleOnMain(() -> this.disconnect(new TranslatableComponent("disconnect.spam", new Object[0]))); // Paper return; } @@ -3235,67 +2991,46 @@ diff --git a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java b/s index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java +++ b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java -@@ -0,0 +0,0 @@ public abstract class BlockableEventLoop implements Processo - +@@ -0,0 +0,0 @@ public abstract class BlockableEventLoop implements Profiler + this.pendingRunnables.clear(); } - protected void runAllTasks() { + public void runAllTasks() { // Paper - protected -> public - while (this.pollTask()) { - ; + while(this.pollTask()) { } + diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java +++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java -@@ -0,0 +0,0 @@ import java.util.stream.Stream; - import net.minecraft.Util; - import net.minecraft.core.BlockPos; - import net.minecraft.core.SectionPos; -+import net.minecraft.nbt.CompoundTag; - import net.minecraft.server.level.SectionTracker; -+import net.minecraft.server.level.ServerLevel; - import net.minecraft.util.datafix.DataFixTypes; - import net.minecraft.world.level.ChunkPos; - import net.minecraft.world.level.LevelReader; @@ -0,0 +0,0 @@ public class PoiManager extends SectionStorage { - private final PoiManager.DistanceTracker distanceTracker = new PoiManager.DistanceTracker(); + public static final int VILLAGE_SECTION_SIZE = 1; + private final PoiManager.DistanceTracker distanceTracker; private final LongSet loadedChunks = new LongOpenHashSet(); ++ private final net.minecraft.server.level.ServerLevel world; // Paper -+ private final ServerLevel world; // Paper -+ - public PoiManager(File directory, DataFixer datafixer, boolean flag) { -- super(directory, PoiSection::codec, PoiSection::new, datafixer, DataFixTypes.POI_CHUNK, flag); -+ // Paper start - add world parameter -+ this(directory, datafixer, flag, null); -+ } -+ public PoiManager(File file, DataFixer datafixer, boolean flag, ServerLevel world) { -+ super(file, PoiSection::codec, PoiSection::new, datafixer, DataFixTypes.POI_CHUNK, flag); -+ this.world = world; -+ // Paper end - add world parameter + public PoiManager(File directory, DataFixer dataFixer, boolean dsync, LevelHeightAccessor world) { + super(directory, PoiSection::codec, PoiSection::new, dataFixer, DataFixTypes.POI_CHUNK, dsync, world); ++ this.world = (net.minecraft.server.level.ServerLevel)world; // Paper + this.distanceTracker = new PoiManager.DistanceTracker(); } - public void add(BlockPos pos, PoiType type) { @@ -0,0 +0,0 @@ public class PoiManager extends SectionStorage { @Override public void tick(BooleanSupplier shouldKeepTicking) { - super.tick(shouldKeepTicking); + // Paper start - async chunk io -+ if (this.world == null) { -+ super.tick(shouldKeepTicking); -+ } else { -+ //super.a(booleansupplier); // re-implement below -+ while (!((SectionStorage)this).dirty.isEmpty() && shouldKeepTicking.getAsBoolean()) { -+ ChunkPos chunkcoordintpair = SectionPos.of(((SectionStorage)this).dirty.firstLong()).chunk(); ++ while (!this.dirty.isEmpty() && shouldKeepTicking.getAsBoolean()) { ++ ChunkPos chunkcoordintpair = SectionPos.of(this.dirty.firstLong()).chunk(); + -+ CompoundTag data; -+ try (co.aikar.timings.Timing ignored1 = this.world.timings.poiSaveDataSerialization.startTiming()) { -+ data = this.getData(chunkcoordintpair); -+ } -+ com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.scheduleSave(this.world, -+ chunkcoordintpair.x, chunkcoordintpair.z, data, null, com.destroystokyo.paper.io.PrioritizedTaskQueue.LOW_PRIORITY); ++ net.minecraft.nbt.CompoundTag data; ++ try (co.aikar.timings.Timing ignored1 = this.world.timings.poiSaveDataSerialization.startTiming()) { ++ data = this.getData(chunkcoordintpair); + } ++ com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.scheduleSave(this.world, ++ chunkcoordintpair.x, chunkcoordintpair.z, data, null, com.destroystokyo.paper.io.PrioritizedTaskQueue.LOW_PRIORITY); + } + // Paper end this.distanceTracker.runAllUpdates(); @@ -3308,9 +3043,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Paper start - Asynchronous chunk io + @javax.annotation.Nullable + @Override -+ public CompoundTag read(ChunkPos chunkcoordintpair) throws java.io.IOException { ++ public net.minecraft.nbt.CompoundTag read(ChunkPos chunkcoordintpair) throws java.io.IOException { + if (this.world != null && Thread.currentThread() != com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE) { -+ CompoundTag ret = com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE ++ net.minecraft.nbt.CompoundTag ret = com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE + .loadChunkDataAsyncFuture(this.world, chunkcoordintpair.x, chunkcoordintpair.z, com.destroystokyo.paper.io.IOUtil.getPriorityForCurrentThread(), + true, false, true).join().poiData; + @@ -3323,7 +3058,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + + @Override -+ public void write(ChunkPos chunkcoordintpair, CompoundTag nbttagcompound) throws java.io.IOException { ++ public void write(ChunkPos chunkcoordintpair, net.minecraft.nbt.CompoundTag nbttagcompound) throws java.io.IOException { + if (this.world != null && Thread.currentThread() != com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE) { + com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.scheduleSave( + this.world, chunkcoordintpair.x, chunkcoordintpair.z, nbttagcompound, null, @@ -3335,16 +3070,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Paper end + public static enum Occupancy { - - HAS_SPACE(PoiRecord::hasSpace), IS_OCCUPIED(PoiRecord::isOccupied), ANY((villageplacerecord) -> { + HAS_SPACE(PoiRecord::hasSpace), + IS_OCCUPIED(PoiRecord::isOccupied), diff --git a/src/main/java/net/minecraft/world/level/TickNextTickData.java b/src/main/java/net/minecraft/world/level/TickNextTickData.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/world/level/TickNextTickData.java +++ b/src/main/java/net/minecraft/world/level/TickNextTickData.java -@@ -0,0 +0,0 @@ import net.minecraft.core.BlockPos; +@@ -0,0 +0,0 @@ import java.util.Comparator; + import net.minecraft.core.BlockPos; public class TickNextTickData { - - private static long counter; + private static final java.util.concurrent.atomic.AtomicLong COUNTER = new java.util.concurrent.atomic.AtomicLong(); // Paper - async chunk loading private final T type; @@ -3354,165 +3089,62 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } public TickNextTickData(BlockPos pos, T t, long time, TickPriority priority) { -- this.c = (long) (TickNextTickData.counter++); -+ this.c = (long) (TickNextTickData.COUNTER.getAndIncrement()); // Paper - async chunk loading +- this.c = (long)(counter++); ++ this.c = (TickNextTickData.COUNTER.getAndIncrement()); // Paper - async chunk loading this.pos = pos.immutable(); this.type = t; this.triggerTick = time; -diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java b/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java -+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java -@@ -0,0 +0,0 @@ public class ChunkStatus { - return ChunkStatus.STATUS_BY_RANGE.size(); - } - -+ public static int getTicketLevelOffset(ChunkStatus status) { return ChunkStatus.getDistance(status); } // Paper - OBFHELPER - public static int getDistance(ChunkStatus status) { - return ChunkStatus.RANGE_BY_STATUS.getInt(status.getIndex()); - } -@@ -0,0 +0,0 @@ public class ChunkStatus { - this.index = previous == null ? 0 : previous.getIndex() + 1; - } - -+ public final int getStatusIndex() { return getIndex(); } // Paper - OBFHELPER - public int getIndex() { - return this.index; - } -@@ -0,0 +0,0 @@ public class ChunkStatus { - return this.name; - } - -- public ChunkStatus getPreviousStatus() { return this.getParent(); } // Paper - OBFHELPER -+ public final ChunkStatus getPreviousStatus() { return this.getParent(); } // Paper - OBFHELPER - public ChunkStatus getParent() { - return this.parent; - } -@@ -0,0 +0,0 @@ public class ChunkStatus { - return this.loadingTask.doWork(this, world, structureManager, lightingProvider, function, chunk); - } - -+ public final int getNeighborRadius() { return this.getRange(); } // Paper - OBFHELPER - public int getRange() { - return this.range; - } -@@ -0,0 +0,0 @@ public class ChunkStatus { - return this.heightmapsAfter; - } - -+ public final boolean isAtLeastStatus(ChunkStatus chunkstatus) { return isOrAfter(chunkstatus); } // Paper - OBFHELPER - public boolean isOrAfter(ChunkStatus chunk) { - return this.getIndex() >= chunk.getIndex(); - } -diff --git a/src/main/java/net/minecraft/world/level/chunk/DataLayer.java b/src/main/java/net/minecraft/world/level/chunk/DataLayer.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/DataLayer.java -+++ b/src/main/java/net/minecraft/world/level/chunk/DataLayer.java -@@ -0,0 +0,0 @@ public class DataLayer { - return this.data; - } - -+ public DataLayer copy() { return this.copy(); } // Paper - OBFHELPER - public DataLayer copy() { - return this.data == null ? new DataLayer() : new DataLayer((byte[]) this.data.clone()); - } 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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 -@@ -0,0 +0,0 @@ import it.unimi.dsi.fastutil.longs.LongOpenHashSet; - import it.unimi.dsi.fastutil.longs.LongSet; - import it.unimi.dsi.fastutil.shorts.ShortList; - import it.unimi.dsi.fastutil.shorts.ShortListIterator; -+import java.util.ArrayDeque; // Paper - import java.util.Arrays; - import java.util.BitSet; - import java.util.EnumSet; @@ -0,0 +0,0 @@ public class ChunkSerializer { - private static final Logger LOGGER = LogManager.getLogger(); + public ChunkSerializer() {} + // Paper start + public static final class InProgressChunkHolder { + + public final ProtoChunk protoChunk; -+ public final ArrayDeque tasks; ++ public final java.util.ArrayDeque tasks; + + public CompoundTag poiData; + -+ public InProgressChunkHolder(final ProtoChunk protoChunk, final ArrayDeque tasks) { ++ public InProgressChunkHolder(final ProtoChunk protoChunk, final java.util.ArrayDeque tasks) { + this.protoChunk = protoChunk; + this.tasks = tasks; + } + } ++ // Paper end + - public static ProtoChunk read(ServerLevel world, StructureManager structureManager, PoiManager poiStorage, ChunkPos pos, CompoundTag tag) { -- ChunkGenerator chunkgenerator = world.getChunkSource().getGenerator(); -+ InProgressChunkHolder holder = loadChunk(world, structureManager, poiStorage, pos, tag, true); + public static ProtoChunk read(ServerLevel world, StructureManager structureManager, PoiManager poiStorage, ChunkPos pos, CompoundTag nbt) { ++ // Paper start - add variant for async calls ++ InProgressChunkHolder holder = loadChunk(world, structureManager, poiStorage, pos, nbt, true); + holder.tasks.forEach(Runnable::run); + return holder.protoChunk; + } -+ -+ public static InProgressChunkHolder loadChunk(ServerLevel worldserver, StructureManager definedstructuremanager, PoiManager villageplace, ChunkPos chunkcoordintpair, CompoundTag nbttagcompound, boolean distinguish) { -+ ArrayDeque tasksToExecuteOnMain = new ArrayDeque<>(); ++ public static InProgressChunkHolder loadChunk(ServerLevel world, StructureManager structureManager, PoiManager poiStorage, ChunkPos pos, CompoundTag nbt, boolean distinguish) { ++ java.util.ArrayDeque tasksToExecuteOnMain = new java.util.ArrayDeque<>(); + // Paper end -+ ChunkGenerator chunkgenerator = worldserver.getChunkSource().getGenerator(); + ChunkGenerator chunkgenerator = world.getChunkSource().getGenerator(); BiomeSource worldchunkmanager = chunkgenerator.getBiomeSource(); -- CompoundTag nbttagcompound1 = tag.getCompound("Level"); -+ CompoundTag nbttagcompound1 = nbttagcompound.getCompound("Level"); - ChunkPos chunkcoordintpair1 = new ChunkPos(nbttagcompound1.getInt("xPos"), nbttagcompound1.getInt("zPos")); - -- if (!Objects.equals(pos, chunkcoordintpair1)) { -- ChunkSerializer.LOGGER.error("Chunk file at {} is in the wrong location; relocating. (Expected {}, got {})", pos, pos, chunkcoordintpair1); -+ if (!Objects.equals(chunkcoordintpair, chunkcoordintpair1)) { -+ ChunkSerializer.LOGGER.error("Chunk file at {} is in the wrong location; relocating. (Expected {}, got {})", chunkcoordintpair, chunkcoordintpair, chunkcoordintpair1); - } - -- ChunkBiomeContainer biomestorage = new ChunkBiomeContainer(world.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), pos, worldchunkmanager, nbttagcompound1.contains("Biomes", 11) ? nbttagcompound1.getIntArray("Biomes") : null); -+ ChunkBiomeContainer biomestorage = new ChunkBiomeContainer(worldserver.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunkcoordintpair, worldchunkmanager, nbttagcompound1.contains("Biomes", 11) ? nbttagcompound1.getIntArray("Biomes") : null); - UpgradeData chunkconverter = nbttagcompound1.contains("UpgradeData", 10) ? new UpgradeData(nbttagcompound1.getCompound("UpgradeData")) : UpgradeData.EMPTY; - ProtoTickList protochunkticklist = new ProtoTickList<>((block) -> { - return block == null || block.defaultBlockState().isAir(); -- }, pos, nbttagcompound1.getList("ToBeTicked", 9)); -+ }, chunkcoordintpair, nbttagcompound1.getList("ToBeTicked", 9)); - ProtoTickList protochunkticklist1 = new ProtoTickList<>((fluidtype) -> { - return fluidtype == null || fluidtype == Fluids.EMPTY; -- }, pos, nbttagcompound1.getList("LiquidsToBeTicked", 9)); -+ }, chunkcoordintpair, nbttagcompound1.getList("LiquidsToBeTicked", 9)); - boolean flag = nbttagcompound1.getBoolean("isLightOn"); - ListTag nbttaglist = nbttagcompound1.getList("Sections", 10); - boolean flag1 = true; - LevelChunkSection[] achunksection = new LevelChunkSection[16]; -- boolean flag2 = world.dimensionType().hasSkyLight(); -- ServerChunkCache chunkproviderserver = world.getChunkSource(); -+ boolean flag2 = worldserver.dimensionType().hasSkyLight(); -+ ServerChunkCache chunkproviderserver = worldserver.getChunkSource(); + CompoundTag nbttagcompound1 = nbt.getCompound("Level"); +@@ -0,0 +0,0 @@ public class ChunkSerializer { LevelLightEngine lightengine = chunkproviderserver.getLightEngine(); if (flag) { -- lightengine.retainData(pos, true); + tasksToExecuteOnMain.add(() -> { // Paper - delay this task since we're executing off-main -+ lightengine.retainData(chunkcoordintpair, true); + lightengine.retainData(pos, true); + }); // Paper - delay this task since we're executing off-main } - for (int i = 0; i < nbttaglist.size(); ++i) { + for (int j = 0; j < nbttaglist.size(); ++j) { @@ -0,0 +0,0 @@ public class ChunkSerializer { - byte b0 = nbttagcompound2.getByte("Y"); - - if (nbttagcompound2.contains("Palette", 9) && nbttagcompound2.contains("BlockStates", 12)) { -- LevelChunkSection chunksection = new LevelChunkSection(b0 << 4, null, world, false); // Paper - Anti-Xray - Add parameters -+ LevelChunkSection chunksection = new LevelChunkSection(b0 << 4, null, worldserver, false); // Paper - Anti-Xray - Add parameters - - chunksection.getStates().read(nbttagcompound2.getList("Palette", 10), nbttagcompound2.getLongArray("BlockStates")); - chunksection.recalcBlockCounts(); -@@ -0,0 +0,0 @@ public class ChunkSerializer { - achunksection[b0] = chunksection; + achunksection[world.getSectionIndexFromSectionY(b0)] = chunksection; } -- poiStorage.checkConsistencyWithBlocks(pos, chunksection); + tasksToExecuteOnMain.add(() -> { // Paper - delay this task since we're executing off-main -+ villageplace.checkConsistencyWithBlocks(chunkcoordintpair, chunksection); + poiStorage.checkConsistencyWithBlocks(pos, chunksection); + }); // Paper - delay this task since we're executing off-main } @@ -3522,67 +3154,22 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Paper start - delay this task since we're executing off-main + DataLayer blockLight = new DataLayer(nbttagcompound2.getByteArray("BlockLight")); + tasksToExecuteOnMain.add(() -> { -+ lightengine.queueSectionData(LightLayer.BLOCK, SectionPos.of(chunkcoordintpair, b0), blockLight, true); ++ lightengine.queueSectionData(LightLayer.BLOCK, SectionPos.of(chunkcoordintpair1, b0), blockLight, true); + }); + // Paper end - delay this task since we're executing off-main } - if (flag2 && nbttagcompound2.contains("SkyLight", 7)) { + if (flag1 && nbttagcompound2.contains("SkyLight", 7)) { - lightengine.queueSectionData(LightLayer.SKY, SectionPos.of(pos, b0), new DataLayer(nbttagcompound2.getByteArray("SkyLight")), true); + // Paper start - delay this task since we're executing off-main + DataLayer skyLight = new DataLayer(nbttagcompound2.getByteArray("SkyLight")); + tasksToExecuteOnMain.add(() -> { -+ lightengine.queueSectionData(LightLayer.SKY, SectionPos.of(chunkcoordintpair, b0), skyLight, true); ++ lightengine.queueSectionData(LightLayer.SKY, SectionPos.of(chunkcoordintpair1, b0), skyLight, true); + }); + // Paper end - delay this task since we're executing off-main } } } - - long j = nbttagcompound1.getLong("InhabitedTime"); -- ChunkStatus.ChunkType chunkstatus_type = getChunkTypeFromTag(tag); -+ ChunkStatus.ChunkType chunkstatus_type = getChunkTypeFromTag(nbttagcompound); - Object object; - - if (chunkstatus_type == ChunkStatus.ChunkType.LEVELCHUNK) { -@@ -0,0 +0,0 @@ public class ChunkSerializer { - object2 = protochunkticklist1; - } - -- object = new LevelChunk(world.getLevel(), pos, biomestorage, chunkconverter, (TickList) object1, (TickList) object2, j, achunksection, (chunk) -> { -+ object = new LevelChunk(worldserver.getLevel(), chunkcoordintpair, biomestorage, chunkconverter, (TickList) object1, (TickList) object2, j, achunksection, (chunk) -> { - postLoadChunk(nbttagcompound1, chunk); - // CraftBukkit start - load chunk persistent data from nbt - net.minecraft.nbt.Tag persistentBase = nbttagcompound1.get("ChunkBukkitValues"); -@@ -0,0 +0,0 @@ public class ChunkSerializer { - // CraftBukkit end - }); - } else { -- ProtoChunk protochunk = new ProtoChunk(pos, chunkconverter, achunksection, protochunkticklist, protochunkticklist1, world); // Paper - Anti-Xray - Add parameter -+ ProtoChunk protochunk = new ProtoChunk(chunkcoordintpair, chunkconverter, achunksection, protochunkticklist, protochunkticklist1, worldserver); // Paper - Anti-Xray - Add parameter - - protochunk.setBiomes(biomestorage); - object = protochunk; -@@ -0,0 +0,0 @@ public class ChunkSerializer { - } - - if (!flag && protochunk.getStatus().isOrAfter(ChunkStatus.LIGHT)) { -- Iterator iterator = BlockPos.betweenClosed(pos.getMinBlockX(), 0, pos.getMinBlockZ(), pos.getMaxBlockX(), 255, pos.getMaxBlockZ()).iterator(); -+ Iterator iterator = BlockPos.betweenClosed(chunkcoordintpair.getMinBlockX(), 0, chunkcoordintpair.getMinBlockZ(), chunkcoordintpair.getMaxBlockX(), 255, chunkcoordintpair.getMaxBlockZ()).iterator(); - - while (iterator.hasNext()) { - BlockPos blockposition = (BlockPos) iterator.next(); -@@ -0,0 +0,0 @@ public class ChunkSerializer { - Heightmap.primeHeightmaps((ChunkAccess) object, enumset); - CompoundTag nbttagcompound4 = nbttagcompound1.getCompound("Structures"); - -- ((ChunkAccess) object).setAllStarts(unpackStructureStart(structureManager, nbttagcompound4, world.getSeed())); -- ((ChunkAccess) object).setAllReferences(unpackStructureReferences(pos, nbttagcompound4)); -+ ((ChunkAccess) object).setAllStarts(unpackStructureStart(definedstructuremanager, nbttagcompound4, worldserver.getSeed())); -+ ((ChunkAccess) object).setAllReferences(unpackStructureReferences(chunkcoordintpair, nbttagcompound4)); - if (nbttagcompound1.getBoolean("shouldSave")) { - ((ChunkAccess) object).setUnsaved(true); - } @@ -0,0 +0,0 @@ public class ChunkSerializer { } @@ -3670,47 +3257,25 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + public static CompoundTag write(ServerLevel world, ChunkAccess chunk) { -- ChunkPos chunkcoordintpair = chunk.getPos(); + return saveChunk(world, chunk, null); + } -+ public static CompoundTag saveChunk(ServerLevel worldserver, ChunkAccess ichunkaccess, AsyncSaveData asyncsavedata) { ++ public static CompoundTag saveChunk(ServerLevel world, ChunkAccess chunk, AsyncSaveData asyncsavedata) { + // Paper end -+ ChunkPos chunkcoordintpair = ichunkaccess.getPos(); + ChunkPos chunkcoordintpair = chunk.getPos(); CompoundTag nbttagcompound = new CompoundTag(); CompoundTag nbttagcompound1 = new CompoundTag(); - @@ -0,0 +0,0 @@ public class ChunkSerializer { nbttagcompound.put("Level", nbttagcompound1); nbttagcompound1.putInt("xPos", chunkcoordintpair.x); nbttagcompound1.putInt("zPos", chunkcoordintpair.z); - nbttagcompound1.putLong("LastUpdate", world.getGameTime()); -- nbttagcompound1.putLong("InhabitedTime", chunk.getInhabitedTime()); -- nbttagcompound1.putString("Status", chunk.getStatus().getName()); -- UpgradeData chunkconverter = chunk.getUpgradeData(); -+ nbttagcompound1.putLong("LastUpdate", asyncsavedata != null ? asyncsavedata.worldTime : worldserver.getGameTime()); // Paper - async chunk unloading -+ nbttagcompound1.putLong("InhabitedTime", ichunkaccess.getInhabitedTime()); -+ nbttagcompound1.putString("Status", ichunkaccess.getStatus().getName()); -+ UpgradeData chunkconverter = ichunkaccess.getUpgradeData(); - - if (!chunkconverter.isEmpty()) { - nbttagcompound1.put("UpgradeData", chunkconverter.write()); - } - -- LevelChunkSection[] achunksection = chunk.getSections(); -+ LevelChunkSection[] achunksection = ichunkaccess.getSections(); - ListTag nbttaglist = new ListTag(); -- ThreadedLevelLightEngine lightenginethreaded = world.getChunkSource().getLightEngine(); -- boolean flag = chunk.isLightCorrect(); -+ ThreadedLevelLightEngine lightenginethreaded = worldserver.getChunkSource().getLightEngine(); -+ boolean flag = ichunkaccess.isLightCorrect(); - - CompoundTag nbttagcompound2; - -- for (int i = -1; i < 17; ++i) { -+ for (int i = -1; i < 17; ++i) { // Paper - conflict on loop parameter change - int finalI = i; // CraftBukkit - decompile errors ++ nbttagcompound1.putLong("LastUpdate", asyncsavedata != null ? asyncsavedata.worldTime : world.getGameTime()); // Paper - async chunk unloading + nbttagcompound1.putLong("InhabitedTime", chunk.getInhabitedTime()); + nbttagcompound1.putString("Status", chunk.getStatus().getName()); + UpgradeData chunkconverter = chunk.getUpgradeData(); +@@ -0,0 +0,0 @@ public class ChunkSerializer { LevelChunkSection chunksection = (LevelChunkSection) Arrays.stream(achunksection).filter((chunksection1) -> { - return chunksection1 != null && chunksection1.bottomBlockY() >> 4 == finalI; // CraftBukkit - decompile errors + return chunksection1 != null && SectionPos.blockToSectionCoord(chunksection1.bottomBlockY()) == finalI; // CraftBukkit - decompile errors }).findFirst().orElse(LevelChunk.EMPTY_SECTION); - DataLayer nibblearray = lightenginethreaded.getLayerListener(LightLayer.BLOCK).getDataLayerData(SectionPos.of(chunkcoordintpair, i)); - DataLayer nibblearray1 = lightenginethreaded.getLayerListener(LightLayer.SKY).getDataLayerData(SectionPos.of(chunkcoordintpair, i)); @@ -3727,99 +3292,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + // Paper end if (chunksection != LevelChunk.EMPTY_SECTION || nibblearray != null || nibblearray1 != null) { - nbttagcompound2 = new CompoundTag(); - nbttagcompound2.putByte("Y", (byte) (i & 255)); + CompoundTag nbttagcompound2 = new CompoundTag(); + @@ -0,0 +0,0 @@ public class ChunkSerializer { - nbttagcompound1.putBoolean("isLightOn", true); - } - -- ChunkBiomeContainer biomestorage = chunk.getBiomes(); -+ ChunkBiomeContainer biomestorage = ichunkaccess.getBiomes(); - - if (biomestorage != null) { - nbttagcompound1.putIntArray("Biomes", biomestorage.writeBiomes()); - } - - ListTag nbttaglist1 = new ListTag(); -- Iterator iterator = chunk.getBlockEntitiesPos().iterator(); -+ Iterator iterator = ichunkaccess.getBlockEntitiesPos().iterator(); - - CompoundTag nbttagcompound3; - - while (iterator.hasNext()) { - BlockPos blockposition = (BlockPos) iterator.next(); - -- nbttagcompound3 = chunk.getBlockEntityNbtForSaving(blockposition); -+ nbttagcompound3 = ichunkaccess.getBlockEntityNbtForSaving(blockposition); - if (nbttagcompound3 != null) { - nbttaglist1.add(nbttagcompound3); - } -@@ -0,0 +0,0 @@ public class ChunkSerializer { - ListTag nbttaglist2 = new ListTag(); - - java.util.List toUpdate = new java.util.ArrayList<>(); // Paper -- if (chunk.getStatus().getChunkType() == ChunkStatus.ChunkType.LEVELCHUNK) { -- LevelChunk chunk1 = (LevelChunk) chunk; -+ if (ichunkaccess.getStatus().getChunkType() == ChunkStatus.ChunkType.LEVELCHUNK) { -+ LevelChunk chunk = (LevelChunk) ichunkaccess; - - // CraftBukkit start - store chunk persistent data in nbt -- if (!chunk1.persistentDataContainer.isEmpty()) { -- nbttagcompound1.put("ChunkBukkitValues", chunk1.persistentDataContainer.toTagCompound()); -+ if (!chunk.persistentDataContainer.isEmpty()) { -+ nbttagcompound1.put("ChunkBukkitValues", chunk.persistentDataContainer.toTagCompound()); - } - // CraftBukkit end - -- chunk1.setLastSaveHadEntities(false); -+ chunk.setLastSaveHadEntities(false); - -- for (int j = 0; j < chunk1.getEntitySlices().length; ++j) { -- Iterator iterator1 = chunk1.getEntitySlices()[j].iterator(); -+ for (int j = 0; j < chunk.getEntitySlices().length; ++j) { -+ Iterator iterator1 = chunk.getEntitySlices()[j].iterator(); - - 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) { -+ if (asyncsavedata == null && !entity.removed && (int) Math.floor(entity.getX()) >> 4 != chunk.getPos().x || (int) Math.floor(entity.getZ()) >> 4 != chunk.getPos().z) { - toUpdate.add(entity); - continue; - } -@@ -0,0 +0,0 @@ public class ChunkSerializer { - } - // Paper end - if (entity.save(nbttagcompound4)) { -- chunk1.setLastSaveHadEntities(true); -+ chunk.setLastSaveHadEntities(true); - nbttaglist2.add(nbttagcompound4); - } - } -@@ -0,0 +0,0 @@ public class ChunkSerializer { - - // Paper start - move entities to the correct chunk - for (Entity entity : toUpdate) { -- world.updateChunkPos(entity); -+ worldserver.updateChunkPos(entity); - } - // Paper end - - } else { -- ProtoChunk protochunk = (ProtoChunk) chunk; -+ ProtoChunk protochunk = (ProtoChunk) ichunkaccess; - - nbttaglist2.addAll(protochunk.getEntities()); - nbttagcompound1.put("Lights", packOffsets(protochunk.getPackedLights())); -@@ -0,0 +0,0 @@ public class ChunkSerializer { - } - - nbttagcompound1.put("Entities", nbttaglist2); -- TickList ticklist = chunk.getBlockTicks(); -+ TickList ticklist = ichunkaccess.getBlockTicks(); // Paper - diff on method change (see getAsyncSaveData) - - if (ticklist instanceof ProtoTickList) { nbttagcompound1.put("ToBeTicked", ((ProtoTickList) ticklist).save()); } else if (ticklist instanceof ChunkTickList) { nbttagcompound1.put("TileTicks", ((ChunkTickList) ticklist).save()); @@ -3828,14 +3303,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + nbttagcompound1.put("TileTicks", asyncsavedata.blockTickList); + // Paper end } else { -- nbttagcompound1.put("TileTicks", world.getBlockTicks().save(chunkcoordintpair)); -+ nbttagcompound1.put("TileTicks", worldserver.getBlockTicks().save(chunkcoordintpair)); // Paper - diff on method change (see getAsyncSaveData) + nbttagcompound1.put("TileTicks", world.getBlockTicks().save(chunkcoordintpair)); } - -- TickList ticklist1 = chunk.getLiquidTicks(); -+ TickList ticklist1 = ichunkaccess.getLiquidTicks(); // Paper - diff on method change (see getAsyncSaveData) - - if (ticklist1 instanceof ProtoTickList) { +@@ -0,0 +0,0 @@ public class ChunkSerializer { nbttagcompound1.put("LiquidsToBeTicked", ((ProtoTickList) ticklist1).save()); } else if (ticklist1 instanceof ChunkTickList) { nbttagcompound1.put("LiquidTicks", ((ChunkTickList) ticklist1).save()); @@ -3844,65 +3314,34 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + nbttagcompound1.put("LiquidTicks", asyncsavedata.fluidTickList); + // Paper end } else { -- nbttagcompound1.put("LiquidTicks", world.getLiquidTicks().save(chunkcoordintpair)); -+ nbttagcompound1.put("LiquidTicks", worldserver.getLiquidTicks().save(chunkcoordintpair)); // Paper - diff on method change (see getAsyncSaveData) + nbttagcompound1.put("LiquidTicks", world.getLiquidTicks().save(chunkcoordintpair)); } - -- nbttagcompound1.put("PostProcessing", packOffsets(chunk.getPostProcessing())); -+ nbttagcompound1.put("PostProcessing", packOffsets(ichunkaccess.getPostProcessing())); - nbttagcompound2 = new CompoundTag(); -- Iterator iterator2 = chunk.getHeightmaps().iterator(); -+ Iterator iterator2 = ichunkaccess.getHeightmaps().iterator(); - - while (iterator2.hasNext()) { - Entry entry = (Entry) iterator2.next(); - -- if (chunk.getStatus().heightmapsAfter().contains(entry.getKey())) { -+ if (ichunkaccess.getStatus().heightmapsAfter().contains(entry.getKey())) { - nbttagcompound2.put(((Heightmap.Types) entry.getKey()).getSerializationKey(), new LongArrayTag(((Heightmap) entry.getValue()).getRawData())); - } - } - - nbttagcompound1.put("Heightmaps", nbttagcompound2); -- nbttagcompound1.put("Structures", packStructureData(chunkcoordintpair, chunk.getAllStarts(), chunk.getAllReferences())); -+ nbttagcompound1.put("Structures", packStructureData(chunkcoordintpair, ichunkaccess.getAllStarts(), ichunkaccess.getAllReferences())); - return nbttagcompound; - } - // Paper start - this is saved with the player diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java +++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java -@@ -0,0 +0,0 @@ package net.minecraft.world.level.chunk.storage; - import com.mojang.datafixers.DataFixer; - import java.io.File; - import java.io.IOException; -+// Paper start -+import java.util.concurrent.CompletableFuture; -+import java.util.concurrent.CompletionException; -+// Paper end - import java.util.function.Supplier; - import javax.annotation.Nullable; - import net.minecraft.SharedConstants; @@ -0,0 +0,0 @@ import net.minecraft.world.level.storage.DimensionDataStorage; public class ChunkStorage implements AutoCloseable { -- private final IOWorker worker; public IOWorker getIOWorker() { return worker; } // Paper - OBFHELPER -+ // Paper - OBFHELPER - nuke IOWorker +- private final IOWorker worker; ++ // Paper - nuke IO worker protected final DataFixer fixerUpper; @Nullable - private LegacyStructureDataHandler legacyStructureHandler; -+ private volatile LegacyStructureDataHandler legacyStructureHandler; // Paper - async chunk loading -+ ++ // Paper start - async chunk loading ++ private volatile LegacyStructureDataHandler legacyStructureHandler; + private final Object persistentDataLock = new Object(); // Paper + public final RegionFileStorage regionFileCache; ++ // Paper end - async chunk loading - public ChunkStorage(File file, DataFixer datafixer, boolean flag) { -+ this.regionFileCache = new RegionFileStorage(file, flag); // Paper - nuke IOWorker - this.fixerUpper = datafixer; -- this.worker = new IOWorker(file, flag, "chunk"); -+ // Paper - nuke IOWorker + public ChunkStorage(File directory, DataFixer dataFixer, boolean dsync) { + this.fixerUpper = dataFixer; +- this.worker = new IOWorker(directory, dsync, "chunk"); ++ // Paper start - async chunk io ++ // remove IO worker ++ this.regionFileCache = new RegionFileStorage(directory, dsync); // Paper - nuke IOWorker ++ // Paper end - async chunk io } // CraftBukkit start @@ -3917,26 +3356,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } } -- CompoundTag nbt = read(pos); -- if (nbt != null) { -- CompoundTag level = nbt.getCompound("Level"); -- if (level.getBoolean("TerrainPopulated")) { -- return true; -- } -+ -+ // Paper start - prioritize -+ CompoundTag nbt = cps == null ? read(pos) : -+ com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.loadChunkData((ServerLevel)cps.getLevel(), x, z, -+ com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHER_PRIORITY, false, true).chunkData; -+ // Paper end -+ if (nbt != null) { -+ CompoundTag level = nbt.getCompound("Level"); -+ if (level.getBoolean("TerrainPopulated")) { -+ return true; -+ } - - ChunkStatus status = ChunkStatus.byName(level.getString("Status")); - if (status != null && status.isOrAfter(ChunkStatus.FEATURES)) { +- CompoundTag nbt = this.read(pos); ++ // Paper start - prioritize ++ CompoundTag nbt = cps == null ? read(pos) : ++ com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.loadChunkData((ServerLevel)cps.getLevel(), x, z, ++ com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHER_PRIORITY, false, true).chunkData; ++ // Paper end + if (nbt != null) { + CompoundTag level = nbt.getCompound("Level"); + if (level.getBoolean("TerrainPopulated")) { @@ -0,0 +0,0 @@ public class ChunkStorage implements AutoCloseable { if (i < 1493) { nbttagcompound = NbtUtils.update(this.fixerUpper, DataFixTypes.CHUNK, nbttagcompound, i, 1493); @@ -3954,31 +3382,33 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @@ -0,0 +0,0 @@ public class ChunkStorage implements AutoCloseable { @Nullable - public CompoundTag read(ChunkPos chunkcoordintpair) throws IOException { -- return this.worker.load(chunkcoordintpair); -+ return this.regionFileCache.read(chunkcoordintpair); + public CompoundTag read(ChunkPos chunkPos) throws IOException { +- return this.worker.load(chunkPos); ++ return this.regionFileCache.read(chunkPos); // Paper - async chunk io } -- public void write(ChunkPos chunkcoordintpair, CompoundTag nbttagcompound) { -- this.worker.store(chunkcoordintpair, nbttagcompound); -+ public void write(ChunkPos chunkcoordintpair, CompoundTag nbttagcompound) throws IOException { write(chunkcoordintpair, nbttagcompound); } // Paper OBFHELPER -+ public void write(ChunkPos chunkcoordintpair, CompoundTag nbttagcompound) throws IOException { // Paper - OBFHELPER - (Switched around for safety) -+ this.regionFileCache.write(chunkcoordintpair, nbttagcompound); +- public void write(ChunkPos chunkPos, CompoundTag nbt) { +- this.worker.store(chunkPos, nbt); ++ // Paper start - async chunk io ++ public void write(ChunkPos chunkPos, CompoundTag nbt) throws IOException { ++ this.regionFileCache.write(chunkPos, nbt); ++ // Paper end - Async chunk loading if (this.legacyStructureHandler != null) { + synchronized (this.persistentDataLock) { // Paper - Async chunk loading - this.legacyStructureHandler.removeIndex(chunkcoordintpair.toLong()); -+ } // Paper - Async chunk loading} + this.legacyStructureHandler.removeIndex(chunkPos.toLong()); ++ } // Paper - Async chunk loading } -- -- } -- -- public void flushWorker() { + + } + + public void flushWorker() { - this.worker.synchronize().join(); ++ com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.flush(); // Paper - nuke IO worker } public void close() throws IOException { - this.worker.close(); -+ this.regionFileCache.close(); ++ this.regionFileCache.close(); // Paper - nuke IO worker } } diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java @@ -3986,14 +3416,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java +++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java @@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { + private final IntBuffer timestamps; + @VisibleForTesting protected final RegionBitmap usedSectors; - public final File file; // Paper - + public final java.util.concurrent.locks.ReentrantLock fileLock = new java.util.concurrent.locks.ReentrantLock(true); // Paper -+ - // Paper start - Cache chunk status - private final ChunkStatus[] statuses = new ChunkStatus[32 * 32]; + public RegionFile(File file, File directory, boolean dsync) throws IOException { + this(file.toPath(), directory.toPath(), RegionFileVersion.VERSION_DEFLATE, dsync); @@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { return (byteCount + 4096 - 1) / 4096; } @@ -4012,9 +3441,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + synchronized (this) { + try { + // Paper end - this.closed = true; // Paper try { this.padToFullSector(); + } finally { @@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { this.file.close(); } @@ -4037,21 +3466,25 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 -public final class RegionFileStorage implements AutoCloseable { +public class RegionFileStorage implements AutoCloseable { // Paper - no final - public final Long2ObjectLinkedOpenHashMap regionCache = new Long2ObjectLinkedOpenHashMap(); - private final File folder; + public static final String ANVIL_EXTENSION = ".mca"; + private static final int MAX_CACHE_SIZE = 256; @@ -0,0 +0,0 @@ public final class RegionFileStorage implements AutoCloseable { - - - // Paper start -- public RegionFile getRegionFileIfLoaded(ChunkPos chunkcoordintpair) { -+ public synchronized RegionFile getRegionFileIfLoaded(ChunkPos chunkcoordintpair) { // Paper - synchronize for async io - return this.regionCache.getAndMoveToFirst(ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ())); + this.sync = dsync; } - // Paper end -- public RegionFile getFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit // Paper - private > public -+ public synchronized RegionFile getFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit // Paper - private > public, synchronize -+ // Paper start - add lock parameter +- private RegionFile getFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit ++ // Paper start ++ public synchronized RegionFile getRegionFileIfLoaded(ChunkPos chunkcoordintpair) { ++ return this.regionCache.getAndMoveToFirst(ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ())); ++ } ++ ++ public synchronized boolean chunkExists(ChunkPos pos) throws IOException { ++ RegionFile regionfile = getFile(pos, true); ++ ++ return regionfile != null ? regionfile.hasChunk(pos) : false; ++ } ++ ++ public synchronized RegionFile getFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit // Paper - public + return this.getFile(chunkcoordintpair, existingOnly, false); + } + public synchronized RegionFile getFile(ChunkPos chunkcoordintpair, boolean existingOnly, boolean lock) throws IOException { @@ -4070,7 +3503,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } else { if (this.regionCache.size() >= com.destroystokyo.paper.PaperConfig.regionFileCacheSize) { // Paper - configurable @@ -0,0 +0,0 @@ public final class RegionFileStorage implements AutoCloseable { - RegionFile regionfile1 = new RegionFile(file, this.folder, this.sync); + RegionFile regionfile1 = new RegionFile(file1, this.folder, this.sync); this.regionCache.putAndMoveToFirst(i, regionfile1); + // Paper start @@ -4094,8 +3527,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 // CraftBukkit end + try { // Paper DataInputStream datainputstream = regionfile.getChunkDataInputStream(pos); - // Paper start - if (regionfile.isOversized(pos.x, pos.z)) { + + CompoundTag nbttagcompound; @@ -0,0 +0,0 @@ public final class RegionFileStorage implements AutoCloseable { } @@ -4105,13 +3538,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } // Paper end } - protected void write(ChunkPos pos, CompoundTag tag) throws IOException { + protected void write(ChunkPos pos, @Nullable CompoundTag nbt) throws IOException { - RegionFile regionfile = this.getFile(pos, false); // CraftBukkit + RegionFile regionfile = this.getFile(pos, false, true); // CraftBukkit // Paper + try { // Paper int attempts = 0; Exception laste = null; while (attempts++ < 5) { try { // Paper - DataOutputStream dataoutputstream = regionfile.getChunkDataOutputStream(pos); - Throwable throwable = null; + + if (nbt == null) { @@ -0,0 +0,0 @@ public final class RegionFileStorage implements AutoCloseable { MinecraftServer.LOGGER.error("Failed to save chunk", laste); } @@ -4127,68 +3560,60 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 ObjectIterator objectiterator = this.regionCache.values().iterator(); @@ -0,0 +0,0 @@ public final class RegionFileStorage implements AutoCloseable { - } - + exceptionsuppressor.throwIfPresent(); } -+ -+ // CraftBukkit start -+ public synchronized boolean chunkExists(ChunkPos pos) throws IOException { // Paper - synchronize -+ RegionFile regionfile = getFile(pos, true); -+ -+ return regionfile != null ? regionfile.hasChunk(pos) : false; -+ } -+ // CraftBukkit end - } + +- public void flush() throws IOException { ++ public synchronized void flush() throws IOException { // Paper - synchronize + ObjectIterator objectiterator = this.regionCache.values().iterator(); + + while (objectiterator.hasNext()) { diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java +++ b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java -@@ -0,0 +0,0 @@ import net.minecraft.world.level.Level; +@@ -0,0 +0,0 @@ import net.minecraft.world.level.LevelHeightAccessor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -public class SectionStorage implements AutoCloseable { +public class SectionStorage extends RegionFileStorage implements AutoCloseable { // Paper - nuke IOWorker - private static final Logger LOGGER = LogManager.getLogger(); + private static final String SECTIONS_TAG = "Sections"; - private final IOWorker worker; -+ // Paper - nuke IOWorker - private final Long2ObjectMap> storage = new Long2ObjectOpenHashMap(); ++ // Paper - remove mojang I/O thread + private final Long2ObjectMap> storage = new Long2ObjectOpenHashMap<>(); - private final LongLinkedOpenHashSet dirty = new LongLinkedOpenHashSet(); + public final LongLinkedOpenHashSet dirty = new LongLinkedOpenHashSet(); // Paper - private -> public private final Function> codec; private final Function factory; private final DataFixer fixerUpper; - private final DataFixTypes type; +@@ -0,0 +0,0 @@ public class SectionStorage implements AutoCloseable { + protected final LevelHeightAccessor levelHeightAccessor; - public SectionStorage(File directory, Function> codecFactory, Function factory, DataFixer datafixer, DataFixTypes datafixtypes, boolean flag) { -+ super(directory, flag); // Paper - nuke IOWorker + public SectionStorage(File directory, Function> codecFactory, Function factory, DataFixer dataFixer, DataFixTypes dataFixTypes, boolean dsync, LevelHeightAccessor world) { ++ super(directory, dsync); // Paper - nuke IOWorker this.codec = codecFactory; this.factory = factory; - this.fixerUpper = datafixer; - this.type = datafixtypes; -- this.worker = new IOWorker(directory, flag, directory.getName()); -+ //this.b = new IOWorker(file, flag, file.getName()); // Paper - nuke IOWorker + this.fixerUpper = dataFixer; + this.type = dataFixTypes; + this.levelHeightAccessor = world; +- this.worker = new IOWorker(directory, dsync, directory.getName()); ++ // Paper - remove mojang I/O thread } protected void tick(BooleanSupplier shouldKeepTicking) { - while (!this.dirty.isEmpty() && shouldKeepTicking.getAsBoolean()) { -- ChunkPos chunkcoordintpair = SectionPos.of(this.dirty.firstLong()).chunk(); -+ ChunkPos chunkcoordintpair = SectionPos.of(this.dirty.firstLong()).chunk(); // Paper - conflict here to avoid obfhelpers - - this.writeColumn(chunkcoordintpair); - } @@ -0,0 +0,0 @@ public class SectionStorage implements AutoCloseable { } - private void readColumn(ChunkPos chunkcoordintpair) { -- this.readColumn(chunkcoordintpair, NbtOps.INSTANCE, this.tryRead(chunkcoordintpair)); -+ // Paper start - load data in function -+ this.loadInData(chunkcoordintpair, this.tryRead(chunkcoordintpair)); + private void readColumn(ChunkPos chunkPos) { +- this.readColumn(chunkPos, NbtOps.INSTANCE, this.tryRead(chunkPos)); ++ // Paper start - expose function to load in data ++ this.loadInData(chunkPos, this.tryRead(chunkPos)); + } + public void loadInData(ChunkPos chunkPos, CompoundTag compound) { + this.readColumn(chunkPos, NbtOps.INSTANCE, compound); -+ // Paper end ++ // Paper end - expose function to load in data } @Nullable @@ -4196,22 +3621,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 try { - return this.worker.load(pos); + return this.read(pos); // Paper - nuke IOWorker - } catch (IOException ioexception) { - SectionStorage.LOGGER.error("Error reading chunk {} data from disk", pos, ioexception); + } catch (IOException var3) { + LOGGER.error("Error reading chunk {} data from disk", pos, var3); return null; @@ -0,0 +0,0 @@ public class SectionStorage implements AutoCloseable { - } - - private void writeColumn(ChunkPos chunkcoordintpair) { -- Dynamic dynamic = this.writeColumn(chunkcoordintpair, NbtOps.INSTANCE); -+ Dynamic dynamic = this.writeColumn(chunkcoordintpair, NbtOps.INSTANCE); // Paper - conflict here to avoid adding obfhelpers :) - Tag nbtbase = (Tag) dynamic.getValue(); - - if (nbtbase instanceof CompoundTag) { -- this.worker.store(chunkcoordintpair, (CompoundTag) nbtbase); -+ try { this.write(chunkcoordintpair, (CompoundTag) nbtbase); } catch (IOException ioexception) { SectionStorage.LOGGER.error("Error writing data to disk", ioexception); } // Paper - nuke IOWorker // TODO make this write async + Dynamic dynamic = this.writeColumn(chunkPos, NbtOps.INSTANCE); + Tag tag = dynamic.getValue(); + if (tag instanceof CompoundTag) { +- this.worker.store(chunkPos, (CompoundTag)tag); ++ try { this.write(chunkPos, (CompoundTag)tag); } catch (IOException ioexception) { SectionStorage.LOGGER.error("Error writing data to disk", ioexception); } // Paper - nuke IOWorker } else { - SectionStorage.LOGGER.error("Expected compound tag, got {}", nbtbase); + LOGGER.error("Expected compound tag, got {}", (Object)tag); } } @@ -4229,40 +3649,23 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return null; + } + // Paper end -+ - private Dynamic writeColumn(ChunkPos chunkcoordintpair, DynamicOps dynamicops) { + private Dynamic writeColumn(ChunkPos chunkPos, DynamicOps dynamicOps) { Map map = Maps.newHashMap(); @@ -0,0 +0,0 @@ public class SectionStorage implements AutoCloseable { - public void flush(ChunkPos chunkcoordintpair) { - if (!this.dirty.isEmpty()) { - for (int i = 0; i < 16; ++i) { -- long j = SectionPos.of(chunkcoordintpair, i).asLong(); -+ long j = SectionPos.of(chunkcoordintpair, i).asLong(); // Paper - conflict here to avoid obfhelpers -- if (this.dirty.contains(j)) { -+ if (this.dirty.contains(j)) { // Paper - conflict here to avoid obfhelpers - this.writeColumn(chunkcoordintpair); - return; - } -@@ -0,0 +0,0 @@ public class SectionStorage implements AutoCloseable { - - } - -- public void close() throws IOException { + @Override + public void close() throws IOException { - this.worker.close(); -+// Paper start - nuke IOWorker -+// public void close() throws IOException { -+// this.b.close(); -+// } -+// Paper end ++ //this.worker.close(); // Paper - nuke I/O worker ++ } + + // Paper start - get data function + public CompoundTag getData(ChunkPos chunkcoordintpair) { + // Note: Copied from above -+ // This is checking if the data exists, then it builds it later in getDataInternal(ChunkCoordIntPair) ++ // This is checking if the data needs to be written, then it builds it later in getDataInternal(ChunkCoordIntPair) + if (!this.dirty.isEmpty()) { -+ for (int i = 0; i < 16; ++i) { ++ for (int i = this.levelHeightAccessor.getMinSection(); i < this.levelHeightAccessor.getMaxSection(); ++i) { + long j = SectionPos.of(chunkcoordintpair, i).asLong(); + + if (this.dirty.contains(j)) { @@ -4278,52 +3681,20 @@ diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/jav index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -0,0 +0,0 @@ public class CraftWorld implements World { - return true; - } - -- net.minecraft.world.level.chunk.storage.RegionFile file; -- try { -- file = world.getChunkSource().chunkMap.getIOWorker().getRegionFileCache().getFile(chunkPos, false); -- } catch (IOException ex) { -- throw new RuntimeException(ex); -- } -+ ChunkStatus status = world.getChunkSource().chunkMap.getStatusOnDiskNoLoad(x, z); // Paper - async io - move to own method - -- ChunkStatus status = file.getStatusIfCached(x, z); -- if (!file.hasChunk(chunkPos) || (status != null && status != ChunkStatus.FULL)) { -+ // Paper start - async io -+ if (status == ChunkStatus.EMPTY) { -+ // does not exist on disk - return false; - } - -+ if (status == null) { // at this stage we don't know what it is on disk - ChunkAccess chunk = world.getChunkSource().getChunk(x, z, ChunkStatus.EMPTY, true); - if (!(chunk instanceof ImposterProtoChunk) && !(chunk instanceof net.minecraft.world.level.chunk.LevelChunk)) { - return false; - } -+ } else if (status != ChunkStatus.FULL) { -+ return false; // not full status on disk -+ } -+ // Paper end - - // fall through to load - // we do this so we do not re-read the chunk data on disk @@ -0,0 +0,0 @@ public class CraftWorld implements World { public DragonBattle getEnderDragonBattle() { - return (getHandle().dragonFight() == null) ? null : new CraftDragonBattle(getHandle().dragonFight()); + return (this.getHandle().dragonFight() == null) ? null : new CraftDragonBattle(this.getHandle().dragonFight()); } + // Paper start + @Override -+ public CompletableFuture getChunkAtAsync(int x, int z, boolean gen, boolean urgent) { ++ public java.util.concurrent.CompletableFuture getChunkAtAsync(int x, int z, boolean gen, boolean urgent) { + if (Bukkit.isPrimaryThread()) { + net.minecraft.world.level.chunk.LevelChunk immediate = this.world.getChunkSource().getChunkAtIfLoadedImmediately(x, z); + if (immediate != null) { -+ return CompletableFuture.completedFuture(immediate.getBukkitChunk()); ++ return java.util.concurrent.CompletableFuture.completedFuture(immediate.getBukkitChunk()); + } + } else { -+ CompletableFuture future = new CompletableFuture(); ++ java.util.concurrent.CompletableFuture future = new java.util.concurrent.CompletableFuture(); + world.getServer().execute(() -> { + getChunkAtAsync(x, z, gen, urgent).whenComplete((chunk, err) -> { + if (err != null) { @@ -4338,7 +3709,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + + return this.world.getChunkSource().getChunkAtAsynchronously(x, z, gen, urgent).thenComposeAsync((either) -> { + net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk) either.left().orElse(null); -+ return CompletableFuture.completedFuture(chunk == null ? null : chunk.getBukkitChunk()); ++ return java.util.concurrent.CompletableFuture.completedFuture(chunk == null ? null : chunk.getBukkitChunk()); + }, net.minecraft.server.MinecraftServer.getServer()); + } + // Paper end @@ -4349,29 +3720,27 @@ diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/ index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -@@ -0,0 +0,0 @@ import net.minecraft.core.BlockPos; - import net.minecraft.nbt.CompoundTag; +@@ -0,0 +0,0 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; import net.minecraft.network.chat.Component; -+import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.TicketType; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.AreaEffectCloud; import net.minecraft.world.entity.Entity; @@ -0,0 +0,0 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { - entity.setYHeadRot(yaw); + this.entity.setYHeadRot(yaw); } + @Override// Paper start + public java.util.concurrent.CompletableFuture teleportAsync(Location loc, @javax.annotation.Nonnull org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) { -+ ChunkMap playerChunkMap = ((CraftWorld) loc.getWorld()).getHandle().getChunkSource().chunkMap; ++ net.minecraft.server.level.ChunkMap playerChunkMap = ((CraftWorld) loc.getWorld()).getHandle().getChunkSource().chunkMap; + java.util.concurrent.CompletableFuture future = new java.util.concurrent.CompletableFuture<>(); + + loc.getWorld().getChunkAtAsyncUrgently(loc).thenCompose(chunk -> { -+ ChunkCoordIntPair pair = new ChunkCoordIntPair(chunk.getX(), chunk.getZ()); -+ ((CraftWorld) loc.getWorld()).getHandle().getChunkProvider().addTicketAtLevel(TicketType.POST_TELEPORT, pair, 31, 0); -+ PlayerChunk updatingChunk = playerChunkMap.getUpdatingChunk(pair.pair()); ++ net.minecraft.world.level.ChunkPos pair = new net.minecraft.world.level.ChunkPos(chunk.getX(), chunk.getZ()); ++ ((CraftWorld) loc.getWorld()).getHandle().getChunkSource().addTicketAtLevel(TicketType.POST_TELEPORT, pair, 31, 0); ++ net.minecraft.server.level.ChunkHolder updatingChunk = playerChunkMap.getUpdatingChunkIfPresent(pair.toLong()); + if (updatingChunk != null) { + return updatingChunk.getEntityTickingFuture(); + } else { @@ -4387,24 +3756,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + @Override public boolean teleport(Location location) { - return teleport(location, TeleportCause.PLUGIN); + return this.teleport(location, TeleportCause.PLUGIN); diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/spigotmc/WatchdogThread.java +++ b/src/main/java/org/spigotmc/WatchdogThread.java -@@ -0,0 +0,0 @@ import java.lang.management.ThreadInfo; - import java.util.logging.Level; - import java.util.logging.Logger; - import com.destroystokyo.paper.PaperConfig; -+import com.destroystokyo.paper.io.chunk.ChunkTaskManager; // Paper - import net.minecraft.server.MinecraftServer; - import org.bukkit.Bukkit; - @@ -0,0 +0,0 @@ public class WatchdogThread extends Thread // Paper end - Different message for short timeout log.log( Level.SEVERE, "------------------------------" ); log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper -+ ChunkTaskManager.dumpAllChunkLoadInfo(); // Paper - dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log ); ++ com.destroystokyo.paper.io.chunk.ChunkTaskManager.dumpAllChunkLoadInfo(); // Paper + WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log ); log.log( Level.SEVERE, "------------------------------" ); // diff --git a/patches/server-remapped/Expose-attack-cooldown-methods-for-Player.patch b/patches/server/Expose-attack-cooldown-methods-for-Player.patch similarity index 60% rename from patches/server-remapped/Expose-attack-cooldown-methods-for-Player.patch rename to patches/server/Expose-attack-cooldown-methods-for-Player.patch index 2494cb9ba6..314a45240a 100644 --- a/patches/server-remapped/Expose-attack-cooldown-methods-for-Player.patch +++ b/patches/server/Expose-attack-cooldown-methods-for-Player.patch @@ -4,18 +4,6 @@ Date: Tue, 4 Sep 2018 15:02:00 -0500 Subject: [PATCH] Expose attack cooldown methods for Player -diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -0,0 +0,0 @@ public abstract class Player extends LivingEntity { - this.entityData.set(Player.DATA_SHOULDER_RIGHT, entityTag); - } - -+ public float getCooldownPeriod() { return this.getCurrentItemAttackStrengthDelay(); } // Paper - OBFHELPER - public float getCurrentItemAttackStrengthDelay() { - return (float) (1.0D / this.getAttributeValue(Attributes.ATTACK_SPEED) * 20.0D); - } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -25,14 +13,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 connection.send(new net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket(0, slot, inventory.getSelected())); } + ++ @Override + public float getCooldownPeriod() { -+ return getHandle().getCooldownPeriod(); ++ return getHandle().getCurrentItemAttackStrengthDelay(); + } + ++ @Override + public float getCooledAttackStrength(float adjustTicks) { + return getHandle().getAttackStrengthScale(adjustTicks); + } + ++ @Override + public void resetCooldown() { + getHandle().resetAttackStrengthTicker(); + } diff --git a/patches/server-remapped/Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch b/patches/server/Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch similarity index 85% rename from patches/server-remapped/Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch rename to patches/server/Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch index e1aff60ffc..2f9d8bcb77 100644 --- a/patches/server-remapped/Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch +++ b/patches/server/Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch @@ -17,39 +17,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 private StateDefinition definition; private BlockState state; @Nullable -@@ -0,0 +0,0 @@ public class BlockStateParser { - return this.nbt; - } - -+ public final @Nullable ResourceLocation getTagKey() { return getTag(); } // Paper - OBFHELPER - @Nullable - public ResourceLocation getTag() { - return this.tag; - } - -+ public final BlockStateParser parse(boolean parseTile) throws CommandSyntaxException { return this.parse(parseTile); } // Paper - OBFHELPER - public BlockStateParser parse(boolean allowNbt) throws CommandSyntaxException { - this.suggestions = this::suggestBlockIdOrTag; - if (this.reader.canRead() && this.reader.peek() == '#') { diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java -@@ -0,0 +0,0 @@ import java.util.logging.Level; - import java.util.logging.Logger; - import javax.annotation.Nonnull; - import javax.annotation.Nullable; -+import net.minecraft.commands.arguments.blocks.BlockStateParser; - import net.minecraft.nbt.CompoundTag; - import net.minecraft.nbt.ListTag; - import net.minecraft.nbt.NbtIo; - import net.minecraft.nbt.StringTag; - import net.minecraft.nbt.Tag; - import net.minecraft.network.chat.TextComponent; -+import net.minecraft.resources.ResourceLocation; - import net.minecraft.world.item.BlockItem; - import org.apache.commons.codec.binary.Base64; - import org.apache.commons.lang.Validate; @@ -0,0 +0,0 @@ import org.bukkit.persistence.PersistentDataContainer; import static org.spigotmc.ValidateUtils.*; // Spigot end @@ -102,7 +73,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 this.persistentDataContainer.putAll(meta.persistentDataContainer.getRaw()); @@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { - persistentDataContainer.put(key, compound.get(key)); + this.persistentDataContainer.put(key, compound.get(key)); } } + // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values @@ -134,7 +105,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 Set keys = tag.getAllKeys(); for (String key : keys) { @@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { - setDamage(damage); + this.setDamage(damage); } + // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values @@ -169,7 +140,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 if (internal != null) { ByteArrayInputStream buf = new ByteArrayInputStream(Base64.decodeBase64(internal)); @@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { - if (hasDamage()) { + if (this.hasDamage()) { itemTag.putInt(DAMAGE.NBT, damage); } + // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values @@ -190,7 +161,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + // Paper end - for (Map.Entry e : unhandledTags.entrySet()) { + for (Map.Entry e : this.unhandledTags.entrySet()) { itemTag.put(e.getKey(), e.getValue()); @@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { } @@ -218,8 +189,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @Overridden boolean isEmpty() { -- return !(hasDisplayName() || hasLocalizedName() || hasEnchants() || (lore != null) || hasCustomModelData() || hasBlockData() || hasRepairCost() || !unhandledTags.isEmpty() || !persistentDataContainer.isEmpty() || hideFlag != 0 || isUnbreakable() || hasDamage() || hasAttributeModifiers()); -+ return !(hasDisplayName() || hasLocalizedName() || hasEnchants() || (lore != null) || hasCustomModelData() || hasBlockData() || hasRepairCost() || !unhandledTags.isEmpty() || !persistentDataContainer.isEmpty() || hideFlag != 0 || isUnbreakable() || hasDamage() || hasAttributeModifiers() || hasPlaceableKeys() || hasDestroyableKeys()); // Paper - Implement an API for CanPlaceOn and CanDestroy NBT values +- return !(this.hasDisplayName() || this.hasLocalizedName() || this.hasEnchants() || (this.lore != null) || this.hasCustomModelData() || this.hasBlockData() || this.hasRepairCost() || !this.unhandledTags.isEmpty() || !this.persistentDataContainer.isEmpty() || this.hideFlag != 0 || this.isUnbreakable() || this.hasDamage() || this.hasAttributeModifiers()); ++ return !(this.hasDisplayName() || this.hasLocalizedName() || this.hasEnchants() || (this.lore != null) || this.hasCustomModelData() || this.hasBlockData() || this.hasRepairCost() || !this.unhandledTags.isEmpty() || !this.persistentDataContainer.isEmpty() || this.hideFlag != 0 || this.isUnbreakable() || this.hasDamage() || this.hasAttributeModifiers() || this.hasPlaceableKeys() || this.hasDestroyableKeys()); // Paper - Implement an API for CanPlaceOn and CanDestroy NBT values } // Paper start @@ -237,12 +208,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 /** @@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { - hash = 61 * hash + (hasDamage() ? this.damage : 0); - hash = 61 * hash + (hasAttributeModifiers() ? this.attributeModifiers.hashCode() : 0); - hash = 61 * hash + version; + hash = 61 * hash + (this.hasDamage() ? this.damage : 0); + hash = 61 * hash + (this.hasAttributeModifiers() ? this.attributeModifiers.hashCode() : 0); + hash = 61 * hash + this.version; + // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values -+ hash = 61 * hash + (hasPlaceableKeys() ? this.placeableKeys.hashCode() : 0); -+ hash = 61 * hash + (hasDestroyableKeys() ? this.destroyableKeys.hashCode() : 0); ++ hash = 61 * hash + (this.hasPlaceableKeys() ? this.placeableKeys.hashCode() : 0); ++ hash = 61 * hash + (this.hasDestroyableKeys() ? this.destroyableKeys.hashCode() : 0); + // Paper end return hash; } @@ -267,7 +238,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } + // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values -+ if (hasPlaceableKeys()) { ++ if (this.hasPlaceableKeys()) { + List cerealPlaceable = this.placeableKeys.stream() + .map(this::serializeNamespaced) + .collect(java.util.stream.Collectors.toList()); @@ -275,7 +246,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + builder.put(CAN_PLACE_ON.BUKKIT, cerealPlaceable); + } + -+ if (hasDestroyableKeys()) { ++ if (this.hasDestroyableKeys()) { + List cerealDestroyable = this.destroyableKeys.stream() + .map(this::serializeNamespaced) + .collect(java.util.stream.Collectors.toList()); @@ -283,9 +254,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + builder.put(CAN_DESTROY.BUKKIT, cerealDestroyable); + } + // Paper end -+ - final Map internalTags = new HashMap(unhandledTags); - serializeInternal(internalTags); + final Map internalTags = new HashMap(this.unhandledTags); + this.serializeInternal(internalTags); if (!internalTags.isEmpty()) { @@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { CraftMetaArmorStand.SHOW_ARMS.NBT, @@ -392,7 +362,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + + private @Nullable Namespaced deserializeNamespaced(String raw) { + boolean isTag = raw.length() > 0 && raw.codePointAt(0) == '#'; -+ BlockStateParser blockParser = new BlockStateParser(new com.mojang.brigadier.StringReader(raw), true); ++ net.minecraft.commands.arguments.blocks.BlockStateParser blockParser = new net.minecraft.commands.arguments.blocks.BlockStateParser(new com.mojang.brigadier.StringReader(raw), true); + try { + blockParser = blockParser.parse(false); + } catch (com.mojang.brigadier.exceptions.CommandSyntaxException e) { @@ -400,9 +370,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return null; + } + -+ ResourceLocation key; ++ net.minecraft.resources.ResourceLocation key; + if (isTag) { -+ key = blockParser.getTagKey(); ++ key = blockParser.getTag(); + } else { + key = blockParser.getBlockKey(); + } diff --git a/patches/server-remapped/Improve-death-events.patch b/patches/server/Improve-death-events.patch similarity index 66% rename from patches/server-remapped/Improve-death-events.patch rename to patches/server/Improve-death-events.patch index 6f11b086d8..9d6de22b6c 100644 --- a/patches/server-remapped/Improve-death-events.patch +++ b/patches/server/Improve-death-events.patch @@ -14,11 +14,15 @@ to cancel the death which has the benefit of also receiving the dropped items and experience which is otherwise only properly possible by using internal code. +TODO 1.17: this needs to be checked (actually get off your lazy ass and cancel the events) for the following entities, +maybe more (please check patch overrides for drops for more): +- players, armor stands, foxes, chested donkeys/llamas + diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -0,0 +0,0 @@ public class ServerPlayer extends Player implements ContainerListener { +@@ -0,0 +0,0 @@ public class ServerPlayer extends Player { public int latency; public boolean wonGame; private int containerUpdateDelay; // Paper @@ -29,7 +33,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 // CraftBukkit start public String displayName; -@@ -0,0 +0,0 @@ public class ServerPlayer extends Player implements ContainerListener { +@@ -0,0 +0,0 @@ public class ServerPlayer extends Player { Component defaultMessage = this.getCombatTracker().getDeathMessage(); org.bukkit.event.entity.PlayerDeathEvent event = CraftEventFactory.callPlayerDeathEvent(this, loot, PaperAdventure.asAdventure(defaultMessage), defaultMessage.getString(), keepInventory); // Paper - Adventure @@ -45,14 +49,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 // SPIGOT-943 - only call if they have an inventory open if (this.containerMenu != this.inventoryMenu) { -@@ -0,0 +0,0 @@ public class ServerPlayer extends Player implements ContainerListener { +@@ -0,0 +0,0 @@ public class ServerPlayer extends Player { } } } - - return super.hurt(source, amount); + // Paper start - cancellable death events -+ //return super.damageEntity(damagesource, f); ++ //return super.hurt(source, amount); + this.queueHealthUpdatePacket = true; + boolean damaged = super.hurt(source, amount); + this.queueHealthUpdatePacket = false; @@ -65,54 +69,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } } } -diff --git a/src/main/java/net/minecraft/world/damagesource/CombatTracker.java b/src/main/java/net/minecraft/world/damagesource/CombatTracker.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/damagesource/CombatTracker.java -+++ b/src/main/java/net/minecraft/world/damagesource/CombatTracker.java -@@ -0,0 +0,0 @@ public class CombatTracker { - this.nextLocation = null; - } - -+ public final void reset() { this.recheckStatus(); } // Paper - OBFHELPER - public void recheckStatus() { - int i = this.inCombat ? 300 : 100; - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, CommandSource, net.minecraft.s - } - // CraftBukkit end - -+ public final void runKillTrigger(Entity entity, int kills, DamageSource damageSource) { this.awardKillScore(entity, kills, damageSource); } // Paper - OBFHELPER - public void awardKillScore(Entity killer, int score, DamageSource damageSource) { - if (killer instanceof ServerPlayer) { - CriteriaTriggers.ENTITY_KILLED_PLAYER.trigger((ServerPlayer) killer, this, damageSource); -@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, CommandSource, net.minecraft.s - this.fallDistance = 0.0F; - } - -+ public final void onKill(ServerLevel worldserver, net.minecraft.world.entity.LivingEntity entityLiving) { this.killed(worldserver, entityLiving); } // Paper - OBFHELPER - public void killed(ServerLevel worldserver, net.minecraft.world.entity.LivingEntity entityliving) {} - - protected void moveTowardsClosestSpace(double x, double y, double z) { diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity { - protected float animStep; - protected float animStepO; - protected float rotOffs; -- protected int deathScore; -+ protected int deathScore;protected int getKillCount() { return this.deathScore; } // Paper - OBFHELPER - public float lastHurt; - protected boolean jumping; - public float xxa; @@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity { public Set collidableExemptions = new HashSet<>(); - public boolean canPickUpLoot; + public boolean bukkitPickUpLoot; public org.bukkit.craftbukkit.entity.CraftLivingEntity getBukkitLivingEntity() { return (org.bukkit.craftbukkit.entity.CraftLivingEntity) super.getBukkitEntity(); } // Paper + public boolean silentDeath = false; // Paper - mark entity as dying silently for cancellable death event @@ -123,15 +86,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 if (this.isDeadOrDying()) { if (!this.checkTotemDeathProtection(source)) { - SoundEvent soundeffect = this.getDeathSound(); -+ // Paper start - moved into CraftEventFactory event caller for cancellable death event -+ //SoundEffect soundeffect = this.getSoundDeath(); - +- - if (flag1 && soundeffect != null) { - this.playSound(soundeffect, this.getSoundVolume(), this.getVoicePitch()); - } -+// if (flag1 && soundeffect != null) { -+// this.playSound(soundeffect, this.getSoundVolume(), this.dH()); -+// } ++ // Paper start - moved into CraftEventFactory event caller for cancellable death event + this.silentDeath = !flag1; // mark entity as dying silently + // Paper end @@ -141,44 +100,45 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } else if (flag1) { this.playHurtSound(source); @@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity { + if (!this.isRemoved() && !this.dead) { Entity entity = source.getEntity(); LivingEntity entityliving = this.getKillCredit(); - -- if (this.deathScore >= 0 && entityliving != null) { -- entityliving.awardKillScore(this, this.deathScore, source); +- + /* // Paper - move down to make death event cancellable - this is the runKillTrigger below -+ if (this.aO >= 0 && entityliving != null) { -+ entityliving.a(this, this.aO, damagesource); + if (this.deathScore >= 0 && entityliving != null) { + entityliving.awardKillScore(this, this.deathScore, source); } - +@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity { if (this.isSleeping()) { -- this.stopSleeping(); -+ this.entityWakeup(); + this.stopSleeping(); } -+ */ // Paper ++ */ // Paper - move down to make death event cancellable - this is the runKillTrigger below ++ this.dead = true; - this.getCombatTracker().recheckStatus(); -+ // this.getCombatTracker().g(); // Paper - moved into if below as .reset() ++ // Paper - moved into if below if (this.level instanceof ServerLevel) { if (entity != null) { - entity.killed((ServerLevel) this.level, this); -+ // entity.a((WorldServer) this.world, this); // Paper - move below into if for onKill ++ // Paper - move below into if for onKill } - this.dropAllDeathLoot(source); + // Paper start -+ org.bukkit.event.entity.EntityDeathEvent deathEvent = this.d(source); ++ org.bukkit.event.entity.EntityDeathEvent deathEvent = this.dropAllDeathLoot(source); + if (deathEvent == null || !deathEvent.isCancelled()) { -+ if (this.getKillCount() >= 0 && entityliving != null) { -+ entityliving.runKillTrigger(this, this.getKillCount(), source); ++ if (this.deathScore >= 0 && entityliving != null) { ++ entityliving.awardKillScore(this, this.deathScore, source); + } ++ + if (this.isSleeping()) { + this.stopSleeping(); + } -+ this.getCombatTracker().reset(); ++ ++ this.getCombatTracker().recheckStatus(); + if (entity != null) { -+ entity.onKill((ServerLevel) this.level, this); ++ entity.killed((ServerLevel) this.level, this); + } + } else { + this.dead = false; @@ -209,21 +169,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } - protected void dropAllDeathLoot(DamageSource source) { -- Entity entity = source.getEntity(); -+ protected org.bukkit.event.entity.EntityDeathEvent processDeath(DamageSource damagesource) { return d(damagesource); } // Paper - OBFHELPER -+ protected org.bukkit.event.entity.EntityDeathEvent d(DamageSource damagesource) { // Paper -+ Entity entity = damagesource.getEntity(); ++ protected org.bukkit.event.entity.EntityDeathEvent dropAllDeathLoot(DamageSource source) { // Paper + Entity entity = source.getEntity(); int i; - if (entity instanceof net.minecraft.world.entity.player.Player) { @@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity { - - this.dropEquipment(); // CraftBukkit - from below - if (this.shouldDropLoot() && this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) { -- this.dropFromLootTable(source, flag); -- this.dropCustomDeathLoot(source, i, flag); -+ this.dropFromLootTable(damagesource, flag); -+ this.dropCustomDeathLoot(damagesource, i, flag); + this.dropCustomDeathLoot(source, i, flag); } // CraftBukkit start - Call death event - CraftEventFactory.callEntityDeathEvent(this, this.drops); @@ -246,23 +197,22 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 return SoundEvents.GENERIC_HURT; } -+ public final SoundEvent getDeathSoundEffect() { return this.getDeathSound(); } // Paper - OBFHELPER ++ @Nullable - protected SoundEvent getDeathSound() { +- protected SoundEvent getDeathSound() { ++ public final SoundEvent getDeathSoundPublic() { return this.getDeathSound(); } protected SoundEvent getDeathSound() { // Paper - provide PUBLIC accessor, overrides are hell to deal with return SoundEvents.GENERIC_DEATH; + } + @@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity { } -+ public final float getDeathSoundVolume() { return this.getSoundVolume(); } // Paper - OBFHELPER - protected float getSoundVolume() { +- protected float getSoundVolume() { ++ public final float getSoundVolumePublic() { return this.getSoundVolume(); } protected float getSoundVolume() { // Paper - provide PUBLIC accessor, overrides are hell return 1.0F; } -+ public float getSoundPitch() { return getVoicePitch();} // Paper - OBFHELPER - protected float getVoicePitch() { - return this.isBaby() ? (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.5F : (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F; - } diff --git a/src/main/java/net/minecraft/world/entity/animal/Fox.java b/src/main/java/net/minecraft/world/entity/animal/Fox.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/world/entity/animal/Fox.java @@ -273,11 +223,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @Override - protected void dropAllDeathLoot(DamageSource source) { - ItemStack itemstack = this.getItemBySlot(EquipmentSlot.MAINHAND); -+ protected org.bukkit.event.entity.EntityDeathEvent d(DamageSource damagesource) { // Paper -+ ItemStack itemstack = this.getItemBySlot(EquipmentSlot.MAINHAND).copy(); // Paper ++ // Paper start - Cancellable death event ++ protected org.bukkit.event.entity.EntityDeathEvent dropAllDeathLoot(DamageSource source) { ++ ItemStack itemstack = this.getItemBySlot(EquipmentSlot.MAINHAND).copy(); // Paper - modified by supercall + -+ // Paper start - Cancellable death event -+ org.bukkit.event.entity.EntityDeathEvent deathEvent = super.d(damagesource); ++ org.bukkit.event.entity.EntityDeathEvent deathEvent = super.dropAllDeathLoot(source); + + // Below is code to drop + @@ -301,7 +251,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractChestedHorse.java +++ b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractChestedHorse.java @@ -0,0 +0,0 @@ public abstract class AbstractChestedHorse extends AbstractHorse { - this.spawnAtLocation((ItemLike) Blocks.CHEST); + this.spawnAtLocation(Blocks.CHEST); } - this.setChest(false); @@ -319,21 +269,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Paper end + @Override - public void addAdditionalSaveData(CompoundTag tag) { - super.addAdditionalSaveData(tag); + public void addAdditionalSaveData(CompoundTag nbt) { + super.addAdditionalSaveData(nbt); diff --git a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java +++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -@@ -0,0 +0,0 @@ public class ArmorStand extends LivingEntity { - this.armorItems.set(i, ItemStack.EMPTY); - } - } -- this.dropAllDeathLoot(damageSource); // CraftBukkit - moved from above -+ this.d(damageSource); // CraftBukkit - moved from above - - } - @@ -0,0 +0,0 @@ public class ArmorStand extends LivingEntity { @Override @@ -341,22 +282,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 - org.bukkit.craftbukkit.event.CraftEventFactory.callEntityDeathEvent(this, drops); // CraftBukkit - call event + org.bukkit.event.entity.EntityDeathEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityDeathEvent(this, drops); // CraftBukkit - call event // Paper - make cancellable + if (event.isCancelled()) return; // Paper - make cancellable - this.remove(); + this.remove(Entity.RemovalReason.KILLED); } -diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -0,0 +0,0 @@ public abstract class Player extends LivingEntity { - super.die(source); - this.reapplyPosition(); - if (!this.isSpectator()) { -- this.dropAllDeathLoot(source); -+ this.d(source); - } - - if (source != null) { diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -365,10 +293,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } public void sendHealthUpdate() { -- getHandle().connection.send(new ClientboundSetHealthPacket(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel())); +- this.getHandle().connection.send(new ClientboundSetHealthPacket(this.getScaledHealth(), this.getHandle().getFoodData().getFoodLevel(), this.getHandle().getFoodData().getSaturationLevel())); + // Paper start - cancellable death event -+ //getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel())); -+ ClientboundSetHealthPacket packet = new ClientboundSetHealthPacket(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel()); ++ ClientboundSetHealthPacket packet = new ClientboundSetHealthPacket(this.getScaledHealth(), this.getHandle().getFoodData().getFoodLevel(), this.getHandle().getFoodData().getSaturationLevel()); + if (this.getHandle().queueHealthUpdatePacket) { + this.getHandle().queuedHealthUpdatePacket = packet; + } else { @@ -382,15 +309,6 @@ diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java -@@ -0,0 +0,0 @@ import net.minecraft.network.protocol.game.ServerboundContainerClosePacket; - import net.minecraft.resources.ResourceLocation; - import net.minecraft.server.level.ServerLevel; - import net.minecraft.server.level.ServerPlayer; -+import net.minecraft.sounds.SoundEvent; -+import net.minecraft.sounds.SoundSource; - import net.minecraft.util.Unit; - import net.minecraft.world.Container; - import net.minecraft.world.InteractionHand; @@ -0,0 +0,0 @@ public class CraftEventFactory { public static EntityDeathEvent callEntityDeathEvent(net.minecraft.world.entity.LivingEntity victim, List drops) { CraftLivingEntity entity = (CraftLivingEntity) victim.getBukkitEntity(); @@ -433,11 +351,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + private static void populateFields(net.minecraft.world.entity.LivingEntity victim, EntityDeathEvent event) { + event.setReviveHealth(event.getEntity().getAttribute(org.bukkit.attribute.Attribute.GENERIC_MAX_HEALTH).getValue()); + event.setShouldPlayDeathSound(!victim.silentDeath && !victim.isSilent()); -+ SoundEvent soundEffect = victim.getDeathSoundEffect(); ++ net.minecraft.sounds.SoundEvent soundEffect = victim.getDeathSoundPublic(); + event.setDeathSound(soundEffect != null ? org.bukkit.craftbukkit.CraftSound.getBukkit(soundEffect) : null); + event.setDeathSoundCategory(org.bukkit.SoundCategory.valueOf(victim.getSoundSource().name())); -+ event.setDeathSoundVolume(victim.getDeathSoundVolume()); -+ event.setDeathSoundPitch(victim.getSoundPitch()); ++ event.setDeathSoundVolume(victim.getSoundVolumePublic()); ++ event.setDeathSoundPitch(victim.getVoicePitch()); + } + + // Play death sound manually @@ -447,8 +365,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + double x = event.getEntity().getLocation().getX(); + double y = event.getEntity().getLocation().getY(); + double z = event.getEntity().getLocation().getZ(); -+ SoundEvent soundEffect = org.bukkit.craftbukkit.CraftSound.getSoundEffect(event.getDeathSound()); -+ SoundSource soundCategory = SoundSource.valueOf(event.getDeathSoundCategory().name()); ++ net.minecraft.sounds.SoundEvent soundEffect = org.bukkit.craftbukkit.CraftSound.getSoundEffect(event.getDeathSound()); ++ net.minecraft.sounds.SoundSource soundCategory = net.minecraft.sounds.SoundSource.valueOf(event.getDeathSoundCategory().name()); + victim.level.playSound(source, x, y, z, soundEffect, soundCategory, event.getDeathSoundVolume(), event.getDeathSoundPitch()); + } + } diff --git a/patches/server-remapped/Mob-Pathfinding-API.patch b/patches/server/Mob-Pathfinding-API.patch similarity index 94% rename from patches/server-remapped/Mob-Pathfinding-API.patch rename to patches/server/Mob-Pathfinding-API.patch index 779e72fec4..5d58d5d851 100644 --- a/patches/server-remapped/Mob-Pathfinding-API.patch +++ b/patches/server/Mob-Pathfinding-API.patch @@ -18,12 +18,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import org.bukkit.craftbukkit.entity.CraftLivingEntity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Mob; -+ +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import net.minecraft.world.level.pathfinder.Node; +import net.minecraft.world.level.pathfinder.Path; -+import PathResult; +import java.util.ArrayList; +import java.util.List; + @@ -171,11 +169,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @Nullable - public Path createPath(Entity entity, int distance) { + public final Path calculateDestination(Entity entity) { return createPath(entity, 0); } public Path createPath(Entity entity, int distance) { - return this.a(ImmutableSet.of(entity.blockPosition()), entity, 16, true, distance); // Paper + return this.createPath(ImmutableSet.of(entity.blockPosition()), entity, 16, true, distance); // Paper } @@ -0,0 +0,0 @@ public abstract class PathNavigation { - return pathentity != null && this.moveTo(pathentity, speed); + return path != null && this.moveTo(path, speed); } + public boolean setDestination(@Nullable Path pathentity, double speed) { return moveTo(pathentity, speed); } // Paper - OBFHELPER @@ -204,9 +202,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/net/minecraft/world/level/pathfinder/Node.java +++ b/src/main/java/net/minecraft/world/level/pathfinder/Node.java @@ -0,0 +0,0 @@ import net.minecraft.util.Mth; + import net.minecraft.world.phys.Vec3; public class Node { - - public final int x; - public final int y; - public final int z; @@ -231,20 +229,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + protected boolean canOpenDoors; public boolean shouldOpenDoors() { return canOpenDoors; } public void setShouldOpenDoors(boolean b) { canOpenDoors = b; } // Paper - obfhelper + protected boolean canFloat; public boolean shouldFloat() { return canFloat; } public void setShouldFloat(boolean b) { canFloat = b; } // Paper - obfhelper - public NodeEvaluator() {} - + public void prepare(PathNavigationRegion cachedWorld, Mob entity) { + this.level = cachedWorld; diff --git a/src/main/java/net/minecraft/world/level/pathfinder/Path.java b/src/main/java/net/minecraft/world/level/pathfinder/Path.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/world/level/pathfinder/Path.java +++ b/src/main/java/net/minecraft/world/level/pathfinder/Path.java -@@ -0,0 +0,0 @@ import net.minecraft.world.phys.Vec3; +@@ -0,0 +0,0 @@ import net.minecraft.world.entity.Entity; + import net.minecraft.world.phys.Vec3; public class Path { - - private final List nodes; + private final List nodes; public List getPoints() { return nodes; } // Paper - OBFHELPER private Node[] openSet = new Node[0]; private Node[] closedSet = new Node[0]; + private Set targetNodes; - private int nextNodeIndex; + private int nextNodeIndex; public int getNextIndex() { return this.nextNodeIndex; } // Paper - OBFHELPER private final BlockPos target; @@ -260,7 +259,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @Nullable - public Node getEndNode() { + public Node getFinalPoint() { return getEndNode(); } @Nullable public Node getEndNode() { // Paper - OBFHELPER - return !this.nodes.isEmpty() ? (Node) this.nodes.get(this.nodes.size() - 1) : null; + return !this.nodes.isEmpty() ? this.nodes.get(this.nodes.size() - 1) : null; } @@ -0,0 +0,0 @@ public class Path { @@ -269,7 +268,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 - public BlockPos getNextNodePos() { + public BlockPos getNext() { return getNextNodePos(); } public BlockPos getNextNodePos() { // Paper - OBFHELPER - return ((Node) this.nodes.get(this.nextNodeIndex)).asBlockPos(); + return this.nodes.get(this.nextNodeIndex).asBlockPos(); } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java @@ -287,4 +286,4 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + @Override public com.destroystokyo.paper.entity.Pathfinder getPathfinder() { return paperPathfinder; } // Paper @Override public void setTarget(LivingEntity target) { - net.minecraft.world.entity.Mob entity = getHandle(); + net.minecraft.world.entity.Mob entity = this.getHandle(); diff --git a/patches/server/Prevent-Mob-AI-Rules-from-Loading-Chunks.patch b/patches/server/Prevent-Mob-AI-Rules-from-Loading-Chunks.patch new file mode 100644 index 0000000000..ac69a2264c --- /dev/null +++ b/patches/server/Prevent-Mob-AI-Rules-from-Loading-Chunks.patch @@ -0,0 +1,40 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Mon, 10 Sep 2018 23:56:36 -0400 +Subject: [PATCH] Prevent Mob AI Rules from Loading Chunks + + +diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java ++++ b/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java +@@ -0,0 +0,0 @@ public class RemoveBlockGoal extends MoveToBlockGoal { + + @Nullable + private BlockPos getPosWithBlock(BlockPos pos, BlockGetter world) { +- if (world.getBlockState(pos).is(this.blockToRemove)) { ++ net.minecraft.world.level.block.state.BlockState block = world.getTypeIfLoaded(pos); // Paper ++ if (block == null) return null; // Paper ++ if (block.is(this.blockToRemove)) { // Paper + return pos; + } else { + BlockPos[] ablockposition = new BlockPos[]{pos.below(), pos.west(), pos.east(), pos.north(), pos.south(), pos.below().below()}; +@@ -0,0 +0,0 @@ public class RemoveBlockGoal extends MoveToBlockGoal { + for (int j = 0; j < i; ++j) { + BlockPos blockposition1 = ablockposition1[j]; + +- if (world.getBlockState(blockposition1).is(this.blockToRemove)) { ++ net.minecraft.world.level.block.state.BlockState block2 = world.getTypeIfLoaded(blockposition1); // Paper ++ if (block2 != null && block2.is(this.blockToRemove)) { // Paper + return blockposition1; + } + } +@@ -0,0 +0,0 @@ public class RemoveBlockGoal extends MoveToBlockGoal { + + @Override + protected boolean isValidTarget(LevelReader world, BlockPos pos) { +- ChunkAccess ichunkaccess = world.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()), ChunkStatus.FULL, false); ++ ChunkAccess ichunkaccess = world.getChunkIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4); // Paper + + return ichunkaccess == null ? false : ichunkaccess.getBlockState(pos).is(this.blockToRemove) && ichunkaccess.getBlockState(pos.above()).isAir() && ichunkaccess.getBlockState(pos.above(2)).isAir(); + } diff --git a/patches/server-remapped/Prevent-chunk-loading-from-Fluid-Flowing.patch b/patches/server/Prevent-chunk-loading-from-Fluid-Flowing.patch similarity index 96% rename from patches/server-remapped/Prevent-chunk-loading-from-Fluid-Flowing.patch rename to patches/server/Prevent-chunk-loading-from-Fluid-Flowing.patch index b2114ce773..02c2bf2d5e 100644 --- a/patches/server-remapped/Prevent-chunk-loading-from-Fluid-Flowing.patch +++ b/patches/server/Prevent-chunk-loading-from-Fluid-Flowing.patch @@ -31,7 +31,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @@ -0,0 +0,0 @@ public abstract class FlowingFluid extends Fluid { if (enumdirection1 != enumdirection) { BlockPos blockposition2 = blockposition.relative(enumdirection1); - short short0 = getCacheKey(blockposition1, blockposition2); + short short0 = FlowingFluid.getCacheKey(blockposition1, blockposition2); - Pair pair = (Pair) short2objectmap.computeIfAbsent(short0, (k) -> { - BlockState iblockdata1 = world.getBlockState(blockposition2); + // Paper start - avoid loading chunks @@ -54,7 +54,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @@ -0,0 +0,0 @@ public abstract class FlowingFluid extends Fluid { Direction enumdirection = (Direction) iterator.next(); BlockPos blockposition1 = pos.relative(enumdirection); - short short0 = getCacheKey(pos, blockposition1); + short short0 = FlowingFluid.getCacheKey(pos, blockposition1); - Pair pair = (Pair) short2objectmap.computeIfAbsent(short0, (j) -> { - BlockState iblockdata1 = world.getBlockState(blockposition1); - diff --git a/patches/server-remapped/Prevent-mob-spawning-from-loading-generating-chunks.patch b/patches/server/Prevent-mob-spawning-from-loading-generating-chunks.patch similarity index 72% rename from patches/server-remapped/Prevent-mob-spawning-from-loading-generating-chunks.patch rename to patches/server/Prevent-mob-spawning-from-loading-generating-chunks.patch index 15232f075d..acc3086a69 100644 --- a/patches/server-remapped/Prevent-mob-spawning-from-loading-generating-chunks.patch +++ b/patches/server/Prevent-mob-spawning-from-loading-generating-chunks.patch @@ -25,8 +25,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 if (entityhuman != null) { double d2 = entityhuman.distanceToSqr(d0, (double) i, d1); -- if (isRightDistanceToPlayerAndSpawnPoint(world, chunk, blockposition_mutableblockposition, d2)) { -+ if (isRightDistanceToPlayerAndSpawnPoint(world, chunk, blockposition_mutableblockposition, d2) && world.isLoadedAndInBounds(blockposition_mutableblockposition)) { // Paper - don't load chunks for mob spawn +- if (NaturalSpawner.isRightDistanceToPlayerAndSpawnPoint(world, chunk, blockposition_mutableblockposition, d2)) { ++ if (world.isLoadedAndInBounds(blockposition_mutableblockposition) && NaturalSpawner.isRightDistanceToPlayerAndSpawnPoint(world, chunk, blockposition_mutableblockposition, d2)) { // Paper - don't load chunks for mob spawn if (biomesettingsmobs_c == null) { - biomesettingsmobs_c = getRandomSpawnMobAt(world, structuremanager, chunkgenerator, group, world.random, (BlockPos) blockposition_mutableblockposition); - if (biomesettingsmobs_c == null) { + Optional optional = NaturalSpawner.getRandomSpawnMobAt(world, structuremanager, chunkgenerator, group, world.random, (BlockPos) blockposition_mutableblockposition); +