net/minecraft/world/level
This commit is contained in:
@@ -1,39 +0,0 @@
|
||||
--- a/net/minecraft/world/level/BaseCommandBlock.java
|
||||
+++ b/net/minecraft/world/level/BaseCommandBlock.java
|
||||
@@ -33,6 +33,10 @@
|
||||
private String command = "";
|
||||
@Nullable
|
||||
private Component customName;
|
||||
+ // CraftBukkit start
|
||||
+ @Override
|
||||
+ public abstract org.bukkit.command.CommandSender getBukkitSender(CommandSourceStack wrapper);
|
||||
+ // CraftBukkit end
|
||||
|
||||
public BaseCommandBlock() {}
|
||||
|
||||
@@ -132,7 +136,7 @@
|
||||
|
||||
});
|
||||
|
||||
- minecraftserver.getCommands().performPrefixedCommand(commandlistenerwrapper, this.command);
|
||||
+ minecraftserver.getCommands().dispatchServerCommand(commandlistenerwrapper, this.command); // CraftBukkit
|
||||
} catch (Throwable throwable) {
|
||||
CrashReport crashreport = CrashReport.forThrowable(throwable, "Executing command block");
|
||||
CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Command to be executed");
|
||||
@@ -174,6 +178,7 @@
|
||||
@Override
|
||||
public void sendSystemMessage(Component message) {
|
||||
if (this.trackOutput) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("sendSystemMessage to a command block"); // Paper - Don't broadcast messages to command blocks
|
||||
SimpleDateFormat simpledateformat = BaseCommandBlock.TIME_FORMAT;
|
||||
Date date = new Date();
|
||||
|
||||
@@ -200,7 +205,7 @@
|
||||
}
|
||||
|
||||
public InteractionResult usedBy(Player player) {
|
||||
- if (!player.canUseGameMasterBlocks()) {
|
||||
+ if (!player.canUseGameMasterBlocks() && (!player.isCreative() || !player.getBukkitEntity().hasPermission("minecraft.commandblock"))) { // Paper - command block permission
|
||||
return InteractionResult.PASS;
|
||||
} else {
|
||||
if (player.getCommandSenderWorld().isClientSide) {
|
||||
@@ -1,167 +0,0 @@
|
||||
--- a/net/minecraft/world/level/BaseSpawner.java
|
||||
+++ b/net/minecraft/world/level/BaseSpawner.java
|
||||
@@ -49,15 +49,17 @@
|
||||
public int maxNearbyEntities = 6;
|
||||
public int requiredPlayerRange = 16;
|
||||
public int spawnRange = 4;
|
||||
+ private int tickDelay = 0; // Paper - Configurable mob spawner tick rate
|
||||
|
||||
public BaseSpawner() {}
|
||||
|
||||
public void setEntityId(EntityType<?> type, @Nullable Level world, RandomSource random, BlockPos pos) {
|
||||
this.getOrCreateNextSpawnData(world, random, pos).getEntityToSpawn().putString("id", BuiltInRegistries.ENTITY_TYPE.getKey(type).toString());
|
||||
+ this.spawnPotentials = SimpleWeightedRandomList.empty(); // CraftBukkit - SPIGOT-3496, MC-92282
|
||||
}
|
||||
|
||||
public boolean isNearPlayer(Level world, BlockPos pos) {
|
||||
- return world.hasNearbyAlivePlayer((double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, (double) this.requiredPlayerRange);
|
||||
+ return world.hasNearbyAlivePlayerThatAffectsSpawning((double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, (double) this.requiredPlayerRange); // Paper - Affects Spawning API
|
||||
}
|
||||
|
||||
public void clientTick(Level world, BlockPos pos) {
|
||||
@@ -82,13 +84,19 @@
|
||||
}
|
||||
|
||||
public void serverTick(ServerLevel world, BlockPos pos) {
|
||||
+ if (spawnCount <= 0 || maxNearbyEntities <= 0) return; // Paper - Ignore impossible spawn tick
|
||||
+ // Paper start - Configurable mob spawner tick rate
|
||||
+ if (spawnDelay > 0 && --tickDelay > 0) return;
|
||||
+ tickDelay = world.paperConfig().tickRates.mobSpawner;
|
||||
+ if (tickDelay == -1) { return; } // If disabled
|
||||
+ // Paper end - Configurable mob spawner tick rate
|
||||
if (this.isNearPlayer(world, pos)) {
|
||||
- if (this.spawnDelay == -1) {
|
||||
+ if (this.spawnDelay < -tickDelay) { // Paper - Configurable mob spawner tick rate
|
||||
this.delay(world, pos);
|
||||
}
|
||||
|
||||
if (this.spawnDelay > 0) {
|
||||
- --this.spawnDelay;
|
||||
+ this.spawnDelay -= tickDelay; // Paper - Configurable mob spawner tick rate
|
||||
} else {
|
||||
boolean flag = false;
|
||||
RandomSource randomsource = world.getRandom();
|
||||
@@ -125,6 +133,20 @@
|
||||
} else if (!SpawnPlacements.checkSpawnRules((EntityType) optional.get(), world, EntitySpawnReason.SPAWNER, blockposition1, world.getRandom())) {
|
||||
continue;
|
||||
}
|
||||
+ // Paper start - PreCreatureSpawnEvent
|
||||
+ com.destroystokyo.paper.event.entity.PreSpawnerSpawnEvent event = new com.destroystokyo.paper.event.entity.PreSpawnerSpawnEvent(
|
||||
+ io.papermc.paper.util.MCUtil.toLocation(world, d0, d1, d2),
|
||||
+ org.bukkit.craftbukkit.entity.CraftEntityType.minecraftToBukkit(optional.get()),
|
||||
+ io.papermc.paper.util.MCUtil.toLocation(world, pos)
|
||||
+ );
|
||||
+ if (!event.callEvent()) {
|
||||
+ flag = true;
|
||||
+ if (event.shouldAbortSpawn()) {
|
||||
+ break;
|
||||
+ }
|
||||
+ continue;
|
||||
+ }
|
||||
+ // Paper end - PreCreatureSpawnEvent
|
||||
|
||||
Entity entity = EntityType.loadEntityRecursive(nbttagcompound, world, EntitySpawnReason.SPAWNER, (entity1) -> {
|
||||
entity1.moveTo(d0, d1, d2, entity1.getYRot(), entity1.getXRot());
|
||||
@@ -143,6 +165,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
+ entity.preserveMotion = true; // Paper - Fix Entity Teleportation and cancel velocity if teleported; preserve entity motion from tag
|
||||
entity.moveTo(entity.getX(), entity.getY(), entity.getZ(), randomsource.nextFloat() * 360.0F, 0.0F);
|
||||
if (entity instanceof Mob) {
|
||||
Mob entityinsentient = (Mob) entity;
|
||||
@@ -157,13 +180,27 @@
|
||||
((Mob) entity).finalizeSpawn(world, world.getCurrentDifficultyAt(entity.blockPosition()), EntitySpawnReason.SPAWNER, (SpawnGroupData) null);
|
||||
}
|
||||
|
||||
- Optional optional1 = mobspawnerdata.getEquipment();
|
||||
+ Optional<net.minecraft.world.entity.EquipmentTable> optional1 = mobspawnerdata.getEquipment(); // CraftBukkit - decompile error
|
||||
|
||||
Objects.requireNonNull(entityinsentient);
|
||||
optional1.ifPresent(entityinsentient::equip);
|
||||
+ // Spigot Start
|
||||
+ if ( entityinsentient.level().spigotConfig.nerfSpawnerMobs )
|
||||
+ {
|
||||
+ entityinsentient.aware = false;
|
||||
+ }
|
||||
+ // Spigot End
|
||||
}
|
||||
|
||||
- if (!world.tryAddFreshEntityWithPassengers(entity)) {
|
||||
+ entity.spawnedViaMobSpawner = true; // Paper
|
||||
+ entity.spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER; // Paper - Entity#getEntitySpawnReason
|
||||
+ flag = true; // Paper
|
||||
+ // CraftBukkit start
|
||||
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callSpawnerSpawnEvent(entity, pos).isCancelled()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (!world.tryAddFreshEntityWithPassengers(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER)) {
|
||||
+ // CraftBukkit end
|
||||
this.delay(world, pos);
|
||||
return;
|
||||
}
|
||||
@@ -174,7 +211,7 @@
|
||||
((Mob) entity).spawnAnim();
|
||||
}
|
||||
|
||||
- flag = true;
|
||||
+ //flag = true; // Paper - moved up above cancellable event
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +239,13 @@
|
||||
}
|
||||
|
||||
public void load(@Nullable Level world, BlockPos pos, CompoundTag nbt) {
|
||||
+ // Paper start - use larger int if set
|
||||
+ if (nbt.contains("Paper.Delay")) {
|
||||
+ this.spawnDelay = nbt.getInt("Paper.Delay");
|
||||
+ } else {
|
||||
this.spawnDelay = nbt.getShort("Delay");
|
||||
+ }
|
||||
+ // Paper end
|
||||
boolean flag = nbt.contains("SpawnData", 10);
|
||||
|
||||
if (flag) {
|
||||
@@ -225,9 +268,15 @@
|
||||
this.spawnPotentials = SimpleWeightedRandomList.single(this.nextSpawnData != null ? this.nextSpawnData : new SpawnData());
|
||||
}
|
||||
|
||||
+ // Paper start - use ints if set
|
||||
+ if (nbt.contains("Paper.MinSpawnDelay", net.minecraft.nbt.Tag.TAG_ANY_NUMERIC)) {
|
||||
+ this.minSpawnDelay = nbt.getInt("Paper.MinSpawnDelay");
|
||||
+ this.maxSpawnDelay = nbt.getInt("Paper.MaxSpawnDelay");
|
||||
+ this.spawnCount = nbt.getShort("SpawnCount");
|
||||
+ } else // Paper end
|
||||
if (nbt.contains("MinSpawnDelay", 99)) {
|
||||
- this.minSpawnDelay = nbt.getShort("MinSpawnDelay");
|
||||
- this.maxSpawnDelay = nbt.getShort("MaxSpawnDelay");
|
||||
+ this.minSpawnDelay = nbt.getInt("MinSpawnDelay"); // Paper - short -> int
|
||||
+ this.maxSpawnDelay = nbt.getInt("MaxSpawnDelay"); // Paper - short -> int
|
||||
this.spawnCount = nbt.getShort("SpawnCount");
|
||||
}
|
||||
|
||||
@@ -244,9 +293,20 @@
|
||||
}
|
||||
|
||||
public CompoundTag save(CompoundTag nbt) {
|
||||
- nbt.putShort("Delay", (short) this.spawnDelay);
|
||||
- nbt.putShort("MinSpawnDelay", (short) this.minSpawnDelay);
|
||||
- nbt.putShort("MaxSpawnDelay", (short) this.maxSpawnDelay);
|
||||
+ // Paper start
|
||||
+ if (spawnDelay > Short.MAX_VALUE) {
|
||||
+ nbt.putInt("Paper.Delay", this.spawnDelay);
|
||||
+ }
|
||||
+ nbt.putShort("Delay", (short) Math.min(Short.MAX_VALUE, this.spawnDelay));
|
||||
+
|
||||
+ if (minSpawnDelay > Short.MAX_VALUE || maxSpawnDelay > Short.MAX_VALUE) {
|
||||
+ nbt.putInt("Paper.MinSpawnDelay", this.minSpawnDelay);
|
||||
+ nbt.putInt("Paper.MaxSpawnDelay", this.maxSpawnDelay);
|
||||
+ }
|
||||
+
|
||||
+ nbt.putShort("MinSpawnDelay", (short) Math.min(Short.MAX_VALUE, this.minSpawnDelay));
|
||||
+ nbt.putShort("MaxSpawnDelay", (short) Math.min(Short.MAX_VALUE, this.maxSpawnDelay));
|
||||
+ // Paper end
|
||||
nbt.putShort("SpawnCount", (short) this.spawnCount);
|
||||
nbt.putShort("MaxNearbyEntities", (short) this.maxNearbyEntities);
|
||||
nbt.putShort("RequiredPlayerRange", (short) this.requiredPlayerRange);
|
||||
@@ -1,90 +0,0 @@
|
||||
--- a/net/minecraft/world/level/BlockGetter.java
|
||||
+++ b/net/minecraft/world/level/BlockGetter.java
|
||||
@@ -12,6 +12,7 @@
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.Mth;
|
||||
+import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
@@ -31,11 +32,20 @@
|
||||
default <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos pos, BlockEntityType<T> type) {
|
||||
BlockEntity tileentity = this.getBlockEntity(pos);
|
||||
|
||||
- return tileentity != null && tileentity.getType() == type ? Optional.of(tileentity) : Optional.empty();
|
||||
+ return tileentity != null && tileentity.getType() == type ? (Optional<T>) Optional.of(tileentity) : Optional.empty(); // CraftBukkit - decompile error
|
||||
}
|
||||
|
||||
BlockState getBlockState(BlockPos pos);
|
||||
+ // Paper start - if loaded util
|
||||
+ @Nullable BlockState getBlockStateIfLoaded(BlockPos blockposition);
|
||||
|
||||
+ default @Nullable Block getBlockIfLoaded(BlockPos blockposition) {
|
||||
+ BlockState type = this.getBlockStateIfLoaded(blockposition);
|
||||
+ return type == null ? null : type.getBlock();
|
||||
+ }
|
||||
+ @Nullable FluidState getFluidIfLoaded(BlockPos blockposition);
|
||||
+ // Paper end
|
||||
+
|
||||
FluidState getFluidState(BlockPos pos);
|
||||
|
||||
default int getLightEmission(BlockPos pos) {
|
||||
@@ -59,10 +69,25 @@
|
||||
});
|
||||
}
|
||||
|
||||
- default BlockHitResult clip(ClipContext context) {
|
||||
- return (BlockHitResult) BlockGetter.traverseBlocks(context.getFrom(), context.getTo(), context, (raytrace1, blockposition) -> {
|
||||
- BlockState iblockdata = this.getBlockState(blockposition);
|
||||
- FluidState fluid = this.getFluidState(blockposition);
|
||||
+ // CraftBukkit start - moved block handling into separate method for use by Block#rayTrace
|
||||
+ default BlockHitResult clip(ClipContext raytrace1, BlockPos blockposition) {
|
||||
+ // Paper start - Add predicate for blocks when raytracing
|
||||
+ return clip(raytrace1, blockposition, null);
|
||||
+ }
|
||||
+
|
||||
+ default BlockHitResult clip(ClipContext raytrace1, BlockPos blockposition, java.util.function.Predicate<? super org.bukkit.block.Block> canCollide) {
|
||||
+ // Paper end - Add predicate for blocks when raytracing
|
||||
+ // Paper start - Prevent raytrace from loading chunks
|
||||
+ BlockState iblockdata = this.getBlockStateIfLoaded(blockposition);
|
||||
+ if (iblockdata == null) {
|
||||
+ // copied the last function parameter (listed below)
|
||||
+ Vec3 vec3d = raytrace1.getFrom().subtract(raytrace1.getTo());
|
||||
+
|
||||
+ return BlockHitResult.miss(raytrace1.getTo(), Direction.getApproximateNearest(vec3d.x, vec3d.y, vec3d.z), BlockPos.containing(raytrace1.getTo()));
|
||||
+ }
|
||||
+ // Paper end - Prevent raytrace from loading chunks
|
||||
+ if (iblockdata.isAir() || (canCollide != null && this instanceof LevelAccessor levelAccessor && !canCollide.test(org.bukkit.craftbukkit.block.CraftBlock.at(levelAccessor, blockposition)))) return null; // Paper - Perf: optimise air cases & check canCollide predicate
|
||||
+ FluidState fluid = iblockdata.getFluidState(); // Paper - Perf: don't need to go to world state again
|
||||
Vec3 vec3d = raytrace1.getFrom();
|
||||
Vec3 vec3d1 = raytrace1.getTo();
|
||||
VoxelShape voxelshape = raytrace1.getBlockShape(iblockdata, this, blockposition);
|
||||
@@ -73,6 +98,18 @@
|
||||
double d1 = movingobjectpositionblock1 == null ? Double.MAX_VALUE : raytrace1.getFrom().distanceToSqr(movingobjectpositionblock1.getLocation());
|
||||
|
||||
return d0 <= d1 ? movingobjectpositionblock : movingobjectpositionblock1;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
+ default BlockHitResult clip(ClipContext context) {
|
||||
+ // Paper start - Add predicate for blocks when raytracing
|
||||
+ return clip(context, (java.util.function.Predicate<org.bukkit.block.Block>) null);
|
||||
+ }
|
||||
+
|
||||
+ default BlockHitResult clip(ClipContext context, java.util.function.Predicate<? super org.bukkit.block.Block> canCollide) {
|
||||
+ // Paper end - Add predicate for blocks when raytracing
|
||||
+ return (BlockHitResult) BlockGetter.traverseBlocks(context.getFrom(), context.getTo(), context, (raytrace1, blockposition) -> {
|
||||
+ return this.clip(raytrace1, blockposition, canCollide); // CraftBukkit - moved into separate method // Paper - Add predicate for blocks when raytracing
|
||||
}, (raytrace1) -> {
|
||||
Vec3 vec3d = raytrace1.getFrom().subtract(raytrace1.getTo());
|
||||
|
||||
@@ -145,7 +182,7 @@
|
||||
double d13 = d10 * (i1 > 0 ? 1.0D - Mth.frac(d4) : Mth.frac(d4));
|
||||
double d14 = d11 * (j1 > 0 ? 1.0D - Mth.frac(d5) : Mth.frac(d5));
|
||||
|
||||
- Object object;
|
||||
+ T object; // CraftBukkit - decompile error
|
||||
|
||||
do {
|
||||
if (d12 > 1.0D && d13 > 1.0D && d14 > 1.0D) {
|
||||
@@ -1,39 +0,0 @@
|
||||
--- a/net/minecraft/world/level/ChunkPos.java
|
||||
+++ b/net/minecraft/world/level/ChunkPos.java
|
||||
@@ -46,6 +46,7 @@
|
||||
public static final int REGION_MAX_INDEX = 31;
|
||||
public final int x;
|
||||
public final int z;
|
||||
+ public final long longKey; // Paper
|
||||
private static final int HASH_A = 1664525;
|
||||
private static final int HASH_C = 1013904223;
|
||||
private static final int HASH_Z_XOR = -559038737;
|
||||
@@ -53,16 +54,19 @@
|
||||
public ChunkPos(int x, int z) {
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
+ this.longKey = asLong(this.x, this.z); // Paper
|
||||
}
|
||||
|
||||
public ChunkPos(BlockPos pos) {
|
||||
this.x = SectionPos.blockToSectionCoord(pos.getX());
|
||||
this.z = SectionPos.blockToSectionCoord(pos.getZ());
|
||||
+ this.longKey = asLong(this.x, this.z); // Paper
|
||||
}
|
||||
|
||||
public ChunkPos(long pos) {
|
||||
this.x = (int)pos;
|
||||
this.z = (int)(pos >> 32);
|
||||
+ this.longKey = asLong(this.x, this.z); // Paper
|
||||
}
|
||||
|
||||
public static ChunkPos minFromRegion(int x, int z) {
|
||||
@@ -74,7 +78,7 @@
|
||||
}
|
||||
|
||||
public long toLong() {
|
||||
- return asLong(this.x, this.z);
|
||||
+ return longKey; // Paper
|
||||
}
|
||||
|
||||
public static long asLong(int chunkX, int chunkZ) {
|
||||
@@ -1,20 +0,0 @@
|
||||
--- a/net/minecraft/world/level/ClipContext.java
|
||||
+++ b/net/minecraft/world/level/ClipContext.java
|
||||
@@ -22,7 +22,7 @@
|
||||
private final CollisionContext collisionContext;
|
||||
|
||||
public ClipContext(Vec3 start, Vec3 end, ClipContext.Block shapeType, ClipContext.Fluid fluidHandling, Entity entity) {
|
||||
- this(start, end, shapeType, fluidHandling, CollisionContext.of(entity));
|
||||
+ this(start, end, shapeType, fluidHandling, (entity == null) ? CollisionContext.empty() : CollisionContext.of(entity)); // CraftBukkit
|
||||
}
|
||||
|
||||
public ClipContext(Vec3 start, Vec3 end, ClipContext.Block shapeType, ClipContext.Fluid fluidHandling, CollisionContext shapeContext) {
|
||||
@@ -79,7 +79,7 @@
|
||||
|
||||
private final Predicate<FluidState> canPick;
|
||||
|
||||
- private Fluid(final Predicate predicate) {
|
||||
+ private Fluid(final Predicate<FluidState> predicate) { // CraftBukkit - decompile error
|
||||
this.canPick = predicate;
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
--- a/net/minecraft/world/level/EmptyBlockGetter.java
|
||||
+++ b/net/minecraft/world/level/EmptyBlockGetter.java
|
||||
@@ -17,7 +17,19 @@
|
||||
return null;
|
||||
}
|
||||
|
||||
+ // Paper start - If loaded util
|
||||
@Override
|
||||
+ public final FluidState getFluidIfLoaded(BlockPos blockposition) {
|
||||
+ return Fluids.EMPTY.defaultFluidState();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final BlockState getBlockStateIfLoaded(BlockPos blockposition) {
|
||||
+ return Blocks.AIR.defaultBlockState();
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
+ @Override
|
||||
public BlockState getBlockState(BlockPos pos) {
|
||||
return Blocks.AIR.defaultBlockState();
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
--- a/net/minecraft/world/level/EntityGetter.java
|
||||
+++ b/net/minecraft/world/level/EntityGetter.java
|
||||
@@ -71,6 +71,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start - Affects Spawning API
|
||||
+ default @Nullable Player findNearbyPlayer(Entity entity, double maxDistance, @Nullable Predicate<Entity> predicate) {
|
||||
+ return this.getNearestPlayer(entity.getX(), entity.getY(), entity.getZ(), maxDistance, predicate);
|
||||
+ }
|
||||
+ // Paper end - Affects Spawning API
|
||||
@Nullable
|
||||
default Player getNearestPlayer(double x, double y, double z, double maxDistance, @Nullable Predicate<Entity> targetPredicate) {
|
||||
double d = -1.0;
|
||||
@@ -89,6 +94,28 @@
|
||||
return player;
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ default List<org.bukkit.entity.HumanEntity> findNearbyBukkitPlayers(double x, double y, double z, double radius, boolean notSpectator) {
|
||||
+ return findNearbyBukkitPlayers(x, y, z, radius, notSpectator ? EntitySelector.NO_SPECTATORS : net.minecraft.world.entity.EntitySelector.NO_CREATIVE_OR_SPECTATOR);
|
||||
+ }
|
||||
+
|
||||
+ default List<org.bukkit.entity.HumanEntity> findNearbyBukkitPlayers(double x, double y, double z, double radius, @Nullable Predicate<Entity> predicate) {
|
||||
+ com.google.common.collect.ImmutableList.Builder<org.bukkit.entity.HumanEntity> builder = com.google.common.collect.ImmutableList.builder();
|
||||
+
|
||||
+ for (Player human : this.players()) {
|
||||
+ if (predicate == null || predicate.test(human)) {
|
||||
+ double distanceSquared = human.distanceToSqr(x, y, z);
|
||||
+
|
||||
+ if (radius < 0.0D || distanceSquared < radius * radius) {
|
||||
+ builder.add(human.getBukkitEntity());
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return builder.build();
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Nullable
|
||||
default Player getNearestPlayer(Entity entity, double maxDistance) {
|
||||
return this.getNearestPlayer(entity.getX(), entity.getY(), entity.getZ(), maxDistance, false);
|
||||
@@ -100,6 +127,20 @@
|
||||
return this.getNearestPlayer(x, y, z, maxDistance, predicate);
|
||||
}
|
||||
|
||||
+ // Paper start - Affects Spawning API
|
||||
+ default boolean hasNearbyAlivePlayerThatAffectsSpawning(double x, double y, double z, double range) {
|
||||
+ for (Player player : this.players()) {
|
||||
+ if (EntitySelector.PLAYER_AFFECTS_SPAWNING.test(player)) { // combines NO_SPECTATORS and LIVING_ENTITY_STILL_ALIVE with an "affects spawning" check
|
||||
+ double distanceSqr = player.distanceToSqr(x, y, z);
|
||||
+ if (range < 0.0D || distanceSqr < range * range) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Paper end - Affects Spawning API
|
||||
+
|
||||
default boolean hasNearbyAlivePlayer(double x, double y, double z, double range) {
|
||||
for (Player player : this.players()) {
|
||||
if (EntitySelector.NO_SPECTATORS.test(player) && EntitySelector.LIVING_ENTITY_STILL_ALIVE.test(player)) {
|
||||
@@ -124,4 +165,11 @@
|
||||
|
||||
return null;
|
||||
}
|
||||
+
|
||||
+ // Paper start - check global player list where appropriate
|
||||
+ @Nullable
|
||||
+ default Player getGlobalPlayerByUUID(UUID uuid) {
|
||||
+ return this.getPlayerByUUID(uuid);
|
||||
+ }
|
||||
+ // Paper end - check global player list where appropriate
|
||||
}
|
||||
@@ -1,321 +0,0 @@
|
||||
--- a/net/minecraft/world/level/GameRules.java
|
||||
+++ b/net/minecraft/world/level/GameRules.java
|
||||
@@ -36,6 +36,14 @@
|
||||
|
||||
public class GameRules {
|
||||
|
||||
+ // Paper start - allow disabling gamerule limits
|
||||
+ private static final boolean DISABLE_LIMITS = Boolean.getBoolean("paper.disableGameRuleLimits");
|
||||
+
|
||||
+ private static int limit(final int limit, final int unlimited) {
|
||||
+ return DISABLE_LIMITS ? unlimited : limit;
|
||||
+ }
|
||||
+ // Paper end - allow disabling gamerule limits
|
||||
+
|
||||
public static final int DEFAULT_RANDOM_TICK_SPEED = 3;
|
||||
static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static final Map<GameRules.Key<?>, GameRules.Type<?>> GAME_RULE_TYPES = Maps.newTreeMap(Comparator.comparing((gamerules_gamerulekey) -> {
|
||||
@@ -58,7 +66,7 @@
|
||||
public static final GameRules.Key<GameRules.BooleanValue> RULE_SENDCOMMANDFEEDBACK = GameRules.register("sendCommandFeedback", GameRules.Category.CHAT, GameRules.BooleanValue.create(true));
|
||||
public static final GameRules.Key<GameRules.BooleanValue> RULE_REDUCEDDEBUGINFO = GameRules.register("reducedDebugInfo", GameRules.Category.MISC, GameRules.BooleanValue.create(false, (minecraftserver, gamerules_gameruleboolean) -> {
|
||||
int i = gamerules_gameruleboolean.get() ? 22 : 23;
|
||||
- Iterator iterator = minecraftserver.getPlayerList().getPlayers().iterator();
|
||||
+ Iterator iterator = minecraftserver.players().iterator(); // CraftBukkit - per-world
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
ServerPlayer entityplayer = (ServerPlayer) iterator.next();
|
||||
@@ -74,7 +82,7 @@
|
||||
public static final GameRules.Key<GameRules.IntegerValue> RULE_MAX_ENTITY_CRAMMING = GameRules.register("maxEntityCramming", GameRules.Category.MOBS, GameRules.IntegerValue.create(24));
|
||||
public static final GameRules.Key<GameRules.BooleanValue> RULE_WEATHER_CYCLE = GameRules.register("doWeatherCycle", GameRules.Category.UPDATES, GameRules.BooleanValue.create(true));
|
||||
public static final GameRules.Key<GameRules.BooleanValue> RULE_LIMITED_CRAFTING = GameRules.register("doLimitedCrafting", GameRules.Category.PLAYER, GameRules.BooleanValue.create(false, (minecraftserver, gamerules_gameruleboolean) -> {
|
||||
- Iterator iterator = minecraftserver.getPlayerList().getPlayers().iterator();
|
||||
+ Iterator iterator = minecraftserver.players().iterator(); // CraftBukkit - per-world
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
ServerPlayer entityplayer = (ServerPlayer) iterator.next();
|
||||
@@ -90,7 +98,7 @@
|
||||
public static final GameRules.Key<GameRules.BooleanValue> RULE_DISABLE_RAIDS = GameRules.register("disableRaids", GameRules.Category.MOBS, GameRules.BooleanValue.create(false));
|
||||
public static final GameRules.Key<GameRules.BooleanValue> RULE_DOINSOMNIA = GameRules.register("doInsomnia", GameRules.Category.SPAWNING, GameRules.BooleanValue.create(true));
|
||||
public static final GameRules.Key<GameRules.BooleanValue> RULE_DO_IMMEDIATE_RESPAWN = GameRules.register("doImmediateRespawn", GameRules.Category.PLAYER, GameRules.BooleanValue.create(false, (minecraftserver, gamerules_gameruleboolean) -> {
|
||||
- Iterator iterator = minecraftserver.getPlayerList().getPlayers().iterator();
|
||||
+ Iterator iterator = minecraftserver.players().iterator(); // CraftBukkit - per-world
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
ServerPlayer entityplayer = (ServerPlayer) iterator.next();
|
||||
@@ -120,15 +128,16 @@
|
||||
public static final GameRules.Key<GameRules.BooleanValue> RULE_GLOBAL_SOUND_EVENTS = GameRules.register("globalSoundEvents", GameRules.Category.MISC, GameRules.BooleanValue.create(true));
|
||||
public static final GameRules.Key<GameRules.BooleanValue> RULE_DO_VINES_SPREAD = GameRules.register("doVinesSpread", GameRules.Category.UPDATES, GameRules.BooleanValue.create(true));
|
||||
public static final GameRules.Key<GameRules.BooleanValue> RULE_ENDER_PEARLS_VANISH_ON_DEATH = GameRules.register("enderPearlsVanishOnDeath", GameRules.Category.PLAYER, GameRules.BooleanValue.create(true));
|
||||
- public static final GameRules.Key<GameRules.IntegerValue> RULE_MINECART_MAX_SPEED = GameRules.register("minecartMaxSpeed", GameRules.Category.MISC, GameRules.IntegerValue.create(8, 1, 1000, FeatureFlagSet.of(FeatureFlags.MINECART_IMPROVEMENTS), (minecraftserver, gamerules_gameruleint) -> {
|
||||
+ public static final GameRules.Key<GameRules.IntegerValue> RULE_MINECART_MAX_SPEED = GameRules.register("minecartMaxSpeed", GameRules.Category.MISC, GameRules.IntegerValue.create(8, 1, limit(1000, Integer.MAX_VALUE), FeatureFlagSet.of(FeatureFlags.MINECART_IMPROVEMENTS), (minecraftserver, gamerules_gameruleint) -> { // Paper - allow disabling gamerule limits
|
||||
}));
|
||||
- public static final GameRules.Key<GameRules.IntegerValue> RULE_SPAWN_CHUNK_RADIUS = GameRules.register("spawnChunkRadius", GameRules.Category.MISC, GameRules.IntegerValue.create(2, 0, 32, FeatureFlagSet.of(), (minecraftserver, gamerules_gameruleint) -> {
|
||||
- ServerLevel worldserver = minecraftserver.overworld();
|
||||
+ public static final GameRules.Key<GameRules.IntegerValue> RULE_SPAWN_CHUNK_RADIUS = GameRules.register("spawnChunkRadius", GameRules.Category.MISC, GameRules.IntegerValue.create(2, 0, limit(32, Integer.MAX_VALUE), FeatureFlagSet.of(), (minecraftserver, gamerules_gameruleint) -> { // Paper - allow disabling gamerule limits
|
||||
+ ServerLevel worldserver = minecraftserver; // CraftBukkit - per-world
|
||||
|
||||
worldserver.setDefaultSpawnPos(worldserver.getSharedSpawnPos(), worldserver.getSharedSpawnAngle());
|
||||
}));
|
||||
private final Map<GameRules.Key<?>, GameRules.Value<?>> rules;
|
||||
private final FeatureFlagSet enabledFeatures;
|
||||
+ private final GameRules.Value<?>[] gameruleArray; // Paper - Perf: Use array for gamerule storage
|
||||
|
||||
private static <T extends GameRules.Value<T>> GameRules.Key<T> register(String name, GameRules.Category category, GameRules.Type<T> type) {
|
||||
GameRules.Key<T> gamerules_gamerulekey = new GameRules.Key<>(name, category);
|
||||
@@ -161,10 +170,21 @@
|
||||
private GameRules(Map<GameRules.Key<?>, GameRules.Value<?>> rules, FeatureFlagSet enabledFeatures) {
|
||||
this.rules = rules;
|
||||
this.enabledFeatures = enabledFeatures;
|
||||
+
|
||||
+ // Paper start - Perf: Use array for gamerule storage
|
||||
+ int arraySize = GameRules.Key.lastGameRuleIndex + 1;
|
||||
+ GameRules.Value<?>[] values = new GameRules.Value[arraySize];
|
||||
+
|
||||
+ for (Entry<GameRules.Key<?>, GameRules.Value<?>> entry : rules.entrySet()) {
|
||||
+ values[entry.getKey().gameRuleIndex] = entry.getValue();
|
||||
+ }
|
||||
+
|
||||
+ this.gameruleArray = values;
|
||||
+ // Paper end - Perf: Use array for gamerule storage
|
||||
}
|
||||
|
||||
public <T extends GameRules.Value<T>> T getRule(GameRules.Key<T> key) {
|
||||
- T t0 = (GameRules.Value) this.rules.get(key);
|
||||
+ T t0 = key == null ? null : (T) this.gameruleArray[key.gameRuleIndex]; // Paper - Perf: Use array for gamerule storage
|
||||
|
||||
if (t0 == null) {
|
||||
throw new IllegalArgumentException("Tried to access invalid game rule");
|
||||
@@ -184,7 +204,7 @@
|
||||
|
||||
private void loadFromTag(DynamicLike<?> values) {
|
||||
this.rules.forEach((gamerules_gamerulekey, gamerules_gamerulevalue) -> {
|
||||
- DataResult dataresult = values.get(gamerules_gamerulekey.id).asString();
|
||||
+ DataResult<String> dataresult = values.get(gamerules_gamerulekey.id).asString(); // CraftBukkit - decompile error
|
||||
|
||||
Objects.requireNonNull(gamerules_gamerulevalue);
|
||||
dataresult.ifSuccess(gamerules_gamerulevalue::deserialize);
|
||||
@@ -205,22 +225,22 @@
|
||||
|
||||
private <T extends GameRules.Value<T>> void callVisitorCap(GameRules.GameRuleTypeVisitor visitor, GameRules.Key<?> key, GameRules.Type<?> type) {
|
||||
if (type.requiredFeatures.isSubsetOf(this.enabledFeatures)) {
|
||||
- visitor.visit(key, type);
|
||||
- type.callVisitor(visitor, key);
|
||||
+ visitor.visit((GameRules.Key<T>) key, (GameRules.Type<T>) type); // CraftBukkit - decompile error
|
||||
+ ((GameRules.Type<T>) type).callVisitor(visitor, (GameRules.Key<T>) key); // CraftBukkit - decompile error
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- public void assignFrom(GameRules rules, @Nullable MinecraftServer server) {
|
||||
- rules.rules.keySet().forEach((gamerules_gamerulekey) -> {
|
||||
- this.assignCap(gamerules_gamerulekey, rules, server);
|
||||
+ public void assignFrom(GameRules gamerules, @Nullable ServerLevel minecraftserver) { // CraftBukkit - per-world
|
||||
+ gamerules.rules.keySet().forEach((gamerules_gamerulekey) -> {
|
||||
+ this.assignCap(gamerules_gamerulekey, gamerules, minecraftserver);
|
||||
});
|
||||
}
|
||||
|
||||
- private <T extends GameRules.Value<T>> void assignCap(GameRules.Key<T> key, GameRules rules, @Nullable MinecraftServer server) {
|
||||
- T t0 = rules.getRule(key);
|
||||
+ private <T extends GameRules.Value<T>> void assignCap(GameRules.Key<T> gamerules_gamerulekey, GameRules gamerules, @Nullable ServerLevel minecraftserver) { // CraftBukkit - per-world
|
||||
+ T t0 = gamerules.getRule(gamerules_gamerulekey);
|
||||
|
||||
- this.getRule(key).setFrom(t0, server);
|
||||
+ this.getRule(gamerules_gamerulekey).setFrom(t0, minecraftserver);
|
||||
}
|
||||
|
||||
public boolean getBoolean(GameRules.Key<GameRules.BooleanValue> rule) {
|
||||
@@ -232,6 +252,10 @@
|
||||
}
|
||||
|
||||
public static final class Key<T extends GameRules.Value<T>> {
|
||||
+ // Paper start - Perf: Use array for gamerule storage
|
||||
+ public static int lastGameRuleIndex = 0;
|
||||
+ public final int gameRuleIndex = lastGameRuleIndex++;
|
||||
+ // Paper end - Perf: Use array for gamerule storage
|
||||
|
||||
final String id;
|
||||
private final GameRules.Category category;
|
||||
@@ -285,11 +309,11 @@
|
||||
|
||||
final Supplier<ArgumentType<?>> argument;
|
||||
private final Function<GameRules.Type<T>, T> constructor;
|
||||
- final BiConsumer<MinecraftServer, T> callback;
|
||||
+ final BiConsumer<ServerLevel, T> callback; // CraftBukkit - per-world
|
||||
private final GameRules.VisitorCaller<T> visitorCaller;
|
||||
final FeatureFlagSet requiredFeatures;
|
||||
|
||||
- Type(Supplier<ArgumentType<?>> argumentType, Function<GameRules.Type<T>, T> ruleFactory, BiConsumer<MinecraftServer, T> changeCallback, GameRules.VisitorCaller<T> ruleAcceptor, FeatureFlagSet requiredFeatures) {
|
||||
+ Type(Supplier<ArgumentType<?>> argumentType, Function<GameRules.Type<T>, T> ruleFactory, BiConsumer<ServerLevel, T> changeCallback, GameRules.VisitorCaller<T> ruleAcceptor, FeatureFlagSet requiredFeatures) { // CraftBukkit - per-world
|
||||
this.argument = argumentType;
|
||||
this.constructor = ruleFactory;
|
||||
this.callback = changeCallback;
|
||||
@@ -302,7 +326,7 @@
|
||||
}
|
||||
|
||||
public T createRule() {
|
||||
- return (GameRules.Value) this.constructor.apply(this);
|
||||
+ return this.constructor.apply(this); // CraftBukkit - decompile error
|
||||
}
|
||||
|
||||
public void callVisitor(GameRules.GameRuleTypeVisitor consumer, GameRules.Key<T> key) {
|
||||
@@ -322,21 +346,21 @@
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
- protected abstract void updateFromArgument(CommandContext<CommandSourceStack> context, String name);
|
||||
+ protected abstract void updateFromArgument(CommandContext<CommandSourceStack> context, String name, GameRules.Key<T> gameRuleKey); // Paper - Add WorldGameRuleChangeEvent
|
||||
|
||||
- public void setFromArgument(CommandContext<CommandSourceStack> context, String name) {
|
||||
- this.updateFromArgument(context, name);
|
||||
- this.onChanged(((CommandSourceStack) context.getSource()).getServer());
|
||||
+ public void setFromArgument(CommandContext<CommandSourceStack> context, String name, GameRules.Key<T> gameRuleKey) { // Paper - Add WorldGameRuleChangeEvent
|
||||
+ this.updateFromArgument(context, name, gameRuleKey); // Paper - Add WorldGameRuleChangeEvent
|
||||
+ this.onChanged(((CommandSourceStack) context.getSource()).getLevel()); // CraftBukkit - per-world
|
||||
}
|
||||
|
||||
- public void onChanged(@Nullable MinecraftServer server) {
|
||||
- if (server != null) {
|
||||
- this.type.callback.accept(server, this.getSelf());
|
||||
+ public void onChanged(@Nullable ServerLevel minecraftserver) { // CraftBukkit - per-world
|
||||
+ if (minecraftserver != null) {
|
||||
+ this.type.callback.accept(minecraftserver, this.getSelf());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- protected abstract void deserialize(String value);
|
||||
+ public abstract void deserialize(String value); // PAIL - private->public
|
||||
|
||||
public abstract String serialize();
|
||||
|
||||
@@ -350,7 +374,7 @@
|
||||
|
||||
protected abstract T copy();
|
||||
|
||||
- public abstract void setFrom(T rule, @Nullable MinecraftServer server);
|
||||
+ public abstract void setFrom(T t0, @Nullable ServerLevel minecraftserver); // CraftBukkit - per-world
|
||||
}
|
||||
|
||||
public interface GameRuleTypeVisitor {
|
||||
@@ -366,7 +390,7 @@
|
||||
|
||||
private boolean value;
|
||||
|
||||
- static GameRules.Type<GameRules.BooleanValue> create(boolean initialValue, BiConsumer<MinecraftServer, GameRules.BooleanValue> changeCallback) {
|
||||
+ static GameRules.Type<GameRules.BooleanValue> create(boolean initialValue, BiConsumer<ServerLevel, GameRules.BooleanValue> changeCallback) { // CraftBukkit - per-world
|
||||
return new GameRules.Type<>(BoolArgumentType::bool, (gamerules_gameruledefinition) -> {
|
||||
return new GameRules.BooleanValue(gamerules_gameruledefinition, initialValue);
|
||||
}, changeCallback, GameRules.GameRuleTypeVisitor::visitBoolean, FeatureFlagSet.of());
|
||||
@@ -383,17 +407,20 @@
|
||||
}
|
||||
|
||||
@Override
|
||||
- protected void updateFromArgument(CommandContext<CommandSourceStack> context, String name) {
|
||||
- this.value = BoolArgumentType.getBool(context, name);
|
||||
+ protected void updateFromArgument(CommandContext<CommandSourceStack> context, String name, GameRules.Key<BooleanValue> gameRuleKey) { // Paper start - Add WorldGameRuleChangeEvent
|
||||
+ io.papermc.paper.event.world.WorldGameRuleChangeEvent event = new io.papermc.paper.event.world.WorldGameRuleChangeEvent(context.getSource().getBukkitWorld(), context.getSource().getBukkitSender(), (org.bukkit.GameRule<Boolean>) org.bukkit.GameRule.getByName(gameRuleKey.toString()), String.valueOf(BoolArgumentType.getBool(context, name)));
|
||||
+ if (!event.callEvent()) return;
|
||||
+ this.value = Boolean.parseBoolean(event.getValue());
|
||||
+ // Paper end - Add WorldGameRuleChangeEvent
|
||||
}
|
||||
|
||||
public boolean get() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
- public void set(boolean value, @Nullable MinecraftServer server) {
|
||||
- this.value = value;
|
||||
- this.onChanged(server);
|
||||
+ public void set(boolean flag, @Nullable ServerLevel minecraftserver) { // CraftBukkit - per-world
|
||||
+ this.value = flag;
|
||||
+ this.onChanged(minecraftserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -402,7 +429,7 @@
|
||||
}
|
||||
|
||||
@Override
|
||||
- protected void deserialize(String value) {
|
||||
+ public void deserialize(String value) { // PAIL - protected->public
|
||||
this.value = Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
@@ -421,9 +448,9 @@
|
||||
return new GameRules.BooleanValue(this.type, this.value);
|
||||
}
|
||||
|
||||
- public void setFrom(GameRules.BooleanValue rule, @Nullable MinecraftServer server) {
|
||||
- this.value = rule.value;
|
||||
- this.onChanged(server);
|
||||
+ public void setFrom(GameRules.BooleanValue gamerules_gameruleboolean, @Nullable ServerLevel minecraftserver) { // CraftBukkit - per-world
|
||||
+ this.value = gamerules_gameruleboolean.value;
|
||||
+ this.onChanged(minecraftserver);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,13 +458,13 @@
|
||||
|
||||
private int value;
|
||||
|
||||
- private static GameRules.Type<GameRules.IntegerValue> create(int initialValue, BiConsumer<MinecraftServer, GameRules.IntegerValue> changeCallback) {
|
||||
+ private static GameRules.Type<GameRules.IntegerValue> create(int initialValue, BiConsumer<ServerLevel, GameRules.IntegerValue> changeCallback) { // CraftBukkit - per-world
|
||||
return new GameRules.Type<>(IntegerArgumentType::integer, (gamerules_gameruledefinition) -> {
|
||||
return new GameRules.IntegerValue(gamerules_gameruledefinition, initialValue);
|
||||
}, changeCallback, GameRules.GameRuleTypeVisitor::visitInteger, FeatureFlagSet.of());
|
||||
}
|
||||
|
||||
- static GameRules.Type<GameRules.IntegerValue> create(int initialValue, int min, int max, FeatureFlagSet requiredFeatures, BiConsumer<MinecraftServer, GameRules.IntegerValue> changeCallback) {
|
||||
+ static GameRules.Type<GameRules.IntegerValue> create(int initialValue, int min, int max, FeatureFlagSet requiredFeatures, BiConsumer<ServerLevel, GameRules.IntegerValue> changeCallback) { // CraftBukkit - per-world
|
||||
return new GameRules.Type<>(() -> {
|
||||
return IntegerArgumentType.integer(min, max);
|
||||
}, (gamerules_gameruledefinition) -> {
|
||||
@@ -456,17 +483,20 @@
|
||||
}
|
||||
|
||||
@Override
|
||||
- protected void updateFromArgument(CommandContext<CommandSourceStack> context, String name) {
|
||||
- this.value = IntegerArgumentType.getInteger(context, name);
|
||||
+ protected void updateFromArgument(CommandContext<CommandSourceStack> context, String name, GameRules.Key<IntegerValue> gameRuleKey) { // Paper start - Add WorldGameRuleChangeEvent
|
||||
+ io.papermc.paper.event.world.WorldGameRuleChangeEvent event = new io.papermc.paper.event.world.WorldGameRuleChangeEvent(context.getSource().getBukkitWorld(), context.getSource().getBukkitSender(), (org.bukkit.GameRule<Integer>) org.bukkit.GameRule.getByName(gameRuleKey.toString()), String.valueOf(IntegerArgumentType.getInteger(context, name)));
|
||||
+ if (!event.callEvent()) return;
|
||||
+ this.value = Integer.parseInt(event.getValue());
|
||||
+ // Paper end - Add WorldGameRuleChangeEvent
|
||||
}
|
||||
|
||||
public int get() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
- public void set(int value, @Nullable MinecraftServer server) {
|
||||
- this.value = value;
|
||||
- this.onChanged(server);
|
||||
+ public void set(int i, @Nullable ServerLevel minecraftserver) { // CraftBukkit - per-world
|
||||
+ this.value = i;
|
||||
+ this.onChanged(minecraftserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -475,7 +505,7 @@
|
||||
}
|
||||
|
||||
@Override
|
||||
- protected void deserialize(String value) {
|
||||
+ public void deserialize(String value) { // PAIL - protected->public
|
||||
this.value = IntegerValue.safeParse(value);
|
||||
}
|
||||
|
||||
@@ -517,9 +547,9 @@
|
||||
return new GameRules.IntegerValue(this.type, this.value);
|
||||
}
|
||||
|
||||
- public void setFrom(GameRules.IntegerValue rule, @Nullable MinecraftServer server) {
|
||||
- this.value = rule.value;
|
||||
- this.onChanged(server);
|
||||
+ public void setFrom(GameRules.IntegerValue gamerules_gameruleint, @Nullable ServerLevel minecraftserver) { // CraftBukkit - per-world
|
||||
+ this.value = gamerules_gameruleint.value;
|
||||
+ this.onChanged(minecraftserver);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,710 +0,0 @@
|
||||
--- a/net/minecraft/world/level/Level.java
|
||||
+++ b/net/minecraft/world/level/Level.java
|
||||
@@ -25,8 +25,10 @@
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
+import io.papermc.paper.util.MCUtil;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
+import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
@@ -43,6 +45,7 @@
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.boss.EnderDragonPart;
|
||||
import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
|
||||
+import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.alchemy.PotionBrewing;
|
||||
@@ -57,12 +60,14 @@
|
||||
import net.minecraft.world.level.block.entity.FuelValues;
|
||||
import net.minecraft.world.level.block.entity.TickingBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
+import net.minecraft.world.level.border.BorderChangeListener;
|
||||
import net.minecraft.world.level.border.WorldBorder;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
+import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.entity.EntityTypeTest;
|
||||
import net.minecraft.world.level.entity.LevelEntityGetter;
|
||||
import net.minecraft.world.level.gameevent.GameEvent;
|
||||
@@ -81,6 +86,25 @@
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.scores.Scoreboard;
|
||||
|
||||
+// CraftBukkit start
|
||||
+import java.util.HashMap;
|
||||
+import java.util.Map;
|
||||
+import net.minecraft.network.protocol.game.ClientboundSetBorderCenterPacket;
|
||||
+import net.minecraft.network.protocol.game.ClientboundSetBorderLerpSizePacket;
|
||||
+import net.minecraft.network.protocol.game.ClientboundSetBorderSizePacket;
|
||||
+import net.minecraft.network.protocol.game.ClientboundSetBorderWarningDelayPacket;
|
||||
+import net.minecraft.network.protocol.game.ClientboundSetBorderWarningDistancePacket;
|
||||
+import org.bukkit.Bukkit;
|
||||
+import org.bukkit.craftbukkit.CraftServer;
|
||||
+import org.bukkit.craftbukkit.CraftWorld;
|
||||
+import org.bukkit.craftbukkit.block.CapturedBlockState;
|
||||
+import org.bukkit.craftbukkit.block.CraftBlockState;
|
||||
+import org.bukkit.craftbukkit.block.data.CraftBlockData;
|
||||
+import org.bukkit.craftbukkit.util.CraftSpawnCategory;
|
||||
+import org.bukkit.entity.SpawnCategory;
|
||||
+import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
+// CraftBukkit end
|
||||
+
|
||||
public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
|
||||
public static final Codec<ResourceKey<Level>> RESOURCE_KEY_CODEC = ResourceKey.codec(Registries.DIMENSION);
|
||||
@@ -94,7 +118,7 @@
|
||||
public static final int TICKS_PER_DAY = 24000;
|
||||
public static final int MAX_ENTITY_SPAWN_Y = 20000000;
|
||||
public static final int MIN_ENTITY_SPAWN_Y = -20000000;
|
||||
- protected final List<TickingBlockEntity> blockEntityTickers = Lists.newArrayList();
|
||||
+ public final List<TickingBlockEntity> blockEntityTickers = Lists.newArrayList(); // Paper - public
|
||||
protected final NeighborUpdater neighborUpdater;
|
||||
private final List<TickingBlockEntity> pendingBlockEntityTickers = Lists.newArrayList();
|
||||
private boolean tickingBlockEntities;
|
||||
@@ -121,23 +145,91 @@
|
||||
private final DamageSources damageSources;
|
||||
private long subTickCount;
|
||||
|
||||
- protected Level(WritableLevelData properties, ResourceKey<Level> registryRef, RegistryAccess registryManager, Holder<DimensionType> dimensionEntry, boolean isClient, boolean debugWorld, long seed, int maxChainedNeighborUpdates) {
|
||||
- this.levelData = properties;
|
||||
- this.dimensionTypeRegistration = dimensionEntry;
|
||||
- final DimensionType dimensionmanager = (DimensionType) dimensionEntry.value();
|
||||
+ // CraftBukkit start Added the following
|
||||
+ private final CraftWorld world;
|
||||
+ public boolean pvpMode;
|
||||
+ public org.bukkit.generator.ChunkGenerator generator;
|
||||
|
||||
- this.dimension = registryRef;
|
||||
- this.isClientSide = isClient;
|
||||
+ public boolean preventPoiUpdated = false; // CraftBukkit - SPIGOT-5710
|
||||
+ public boolean captureBlockStates = false;
|
||||
+ public boolean captureTreeGeneration = false;
|
||||
+ public boolean isBlockPlaceCancelled = false; // Paper - prevent calling cleanup logic when undoing a block place upon a cancelled BlockPlaceEvent
|
||||
+ public Map<BlockPos, org.bukkit.craftbukkit.block.CraftBlockState> capturedBlockStates = new java.util.LinkedHashMap<>(); // Paper
|
||||
+ public Map<BlockPos, BlockEntity> capturedTileEntities = new java.util.LinkedHashMap<>(); // Paper - Retain block place order when capturing blockstates
|
||||
+ public List<ItemEntity> captureDrops;
|
||||
+ public final it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap<SpawnCategory> ticksPerSpawnCategory = new it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap<>();
|
||||
+ public boolean populating;
|
||||
+ public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot
|
||||
+ // Paper start - add paper world config
|
||||
+ private final io.papermc.paper.configuration.WorldConfiguration paperConfig;
|
||||
+ public io.papermc.paper.configuration.WorldConfiguration paperConfig() {
|
||||
+ return this.paperConfig;
|
||||
+ }
|
||||
+ // Paper end - add paper world config
|
||||
+
|
||||
+ public static BlockPos lastPhysicsProblem; // Spigot
|
||||
+ private org.spigotmc.TickLimiter entityLimiter;
|
||||
+ private org.spigotmc.TickLimiter tileLimiter;
|
||||
+ private int tileTickPosition;
|
||||
+ public final Map<ServerExplosion.CacheKey, Float> explosionDensityCache = new HashMap<>(); // Paper - Optimize explosions
|
||||
+ public java.util.ArrayDeque<net.minecraft.world.level.block.RedstoneTorchBlock.Toggle> redstoneUpdateInfos; // Paper - Faster redstone torch rapid clock removal; Move from Map in BlockRedstoneTorch to here
|
||||
+
|
||||
+ public CraftWorld getWorld() {
|
||||
+ return this.world;
|
||||
+ }
|
||||
+
|
||||
+ public CraftServer getCraftServer() {
|
||||
+ return (CraftServer) Bukkit.getServer();
|
||||
+ }
|
||||
+ // Paper start - Use getChunkIfLoadedImmediately
|
||||
+ @Override
|
||||
+ public boolean hasChunk(int chunkX, int chunkZ) {
|
||||
+ return this.getChunkIfLoaded(chunkX, chunkZ) != null;
|
||||
+ }
|
||||
+ // Paper end - Use getChunkIfLoadedImmediately
|
||||
+ // Paper start - per world ticks per spawn
|
||||
+ private int getTicksPerSpawn(SpawnCategory spawnCategory) {
|
||||
+ final int perWorld = this.paperConfig().entities.spawning.ticksPerSpawn.getInt(CraftSpawnCategory.toNMS(spawnCategory));
|
||||
+ if (perWorld >= 0) {
|
||||
+ return perWorld;
|
||||
+ }
|
||||
+ return this.getCraftServer().getTicksPerSpawns(spawnCategory);
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
+
|
||||
+ public abstract ResourceKey<LevelStem> getTypeKey();
|
||||
+
|
||||
+ protected Level(WritableLevelData worlddatamutable, ResourceKey<Level> resourcekey, RegistryAccess iregistrycustom, Holder<DimensionType> holder, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function<org.spigotmc.SpigotWorldConfig, io.papermc.paper.configuration.WorldConfiguration> paperWorldConfigCreator) { // Paper - create paper world config
|
||||
+ this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot
|
||||
+ this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config
|
||||
+ this.generator = gen;
|
||||
+ this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env);
|
||||
+
|
||||
+ // CraftBukkit Ticks things
|
||||
+ for (SpawnCategory spawnCategory : SpawnCategory.values()) {
|
||||
+ if (CraftSpawnCategory.isValidForLimits(spawnCategory)) {
|
||||
+ this.ticksPerSpawnCategory.put(spawnCategory, this.getTicksPerSpawn(spawnCategory)); // Paper
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // CraftBukkit end
|
||||
+ this.levelData = worlddatamutable;
|
||||
+ this.dimensionTypeRegistration = holder;
|
||||
+ final DimensionType dimensionmanager = (DimensionType) holder.value();
|
||||
+
|
||||
+ this.dimension = resourcekey;
|
||||
+ this.isClientSide = flag;
|
||||
if (dimensionmanager.coordinateScale() != 1.0D) {
|
||||
- this.worldBorder = new WorldBorder(this) {
|
||||
+ this.worldBorder = new WorldBorder() { // CraftBukkit - decompile error
|
||||
@Override
|
||||
public double getCenterX() {
|
||||
- return super.getCenterX() / dimensionmanager.coordinateScale();
|
||||
+ return super.getCenterX(); // CraftBukkit
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getCenterZ() {
|
||||
- return super.getCenterZ() / dimensionmanager.coordinateScale();
|
||||
+ return super.getCenterZ(); // CraftBukkit
|
||||
}
|
||||
};
|
||||
} else {
|
||||
@@ -145,13 +237,90 @@
|
||||
}
|
||||
|
||||
this.thread = Thread.currentThread();
|
||||
- this.biomeManager = new BiomeManager(this, seed);
|
||||
- this.isDebug = debugWorld;
|
||||
- this.neighborUpdater = new CollectingNeighborUpdater(this, maxChainedNeighborUpdates);
|
||||
- this.registryAccess = registryManager;
|
||||
- this.damageSources = new DamageSources(registryManager);
|
||||
+ this.biomeManager = new BiomeManager(this, i);
|
||||
+ this.isDebug = flag1;
|
||||
+ this.neighborUpdater = new CollectingNeighborUpdater(this, j);
|
||||
+ this.registryAccess = iregistrycustom;
|
||||
+ this.damageSources = new DamageSources(iregistrycustom);
|
||||
+ // CraftBukkit start
|
||||
+ this.getWorldBorder().world = (ServerLevel) this;
|
||||
+ // From PlayerList.setPlayerFileData
|
||||
+ this.getWorldBorder().addListener(new BorderChangeListener() {
|
||||
+ @Override
|
||||
+ public void onBorderSizeSet(WorldBorder border, double size) {
|
||||
+ Level.this.getCraftServer().getHandle().broadcastAll(new ClientboundSetBorderSizePacket(border), border.world);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void onBorderSizeLerping(WorldBorder border, double fromSize, double toSize, long time) {
|
||||
+ Level.this.getCraftServer().getHandle().broadcastAll(new ClientboundSetBorderLerpSizePacket(border), border.world);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void onBorderCenterSet(WorldBorder border, double centerX, double centerZ) {
|
||||
+ Level.this.getCraftServer().getHandle().broadcastAll(new ClientboundSetBorderCenterPacket(border), border.world);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void onBorderSetWarningTime(WorldBorder border, int warningTime) {
|
||||
+ Level.this.getCraftServer().getHandle().broadcastAll(new ClientboundSetBorderWarningDelayPacket(border), border.world);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void onBorderSetWarningBlocks(WorldBorder border, int warningBlockDistance) {
|
||||
+ Level.this.getCraftServer().getHandle().broadcastAll(new ClientboundSetBorderWarningDistancePacket(border), border.world);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void onBorderSetDamagePerBlock(WorldBorder border, double damagePerBlock) {}
|
||||
+
|
||||
+ @Override
|
||||
+ public void onBorderSetDamageSafeZOne(WorldBorder border, double safeZoneRadius) {}
|
||||
+ });
|
||||
+ // CraftBukkit end
|
||||
+ this.entityLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.entityMaxTickTime);
|
||||
+ this.tileLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.tileMaxTickTime);
|
||||
}
|
||||
|
||||
+ // Paper start - Cancel hit for vanished players
|
||||
+ // ret true if no collision
|
||||
+ public final boolean checkEntityCollision(BlockState data, Entity source, net.minecraft.world.phys.shapes.CollisionContext voxelshapedcollision,
|
||||
+ BlockPos position, boolean checkCanSee) {
|
||||
+ // Copied from IWorldReader#a(IBlockData, BlockPosition, VoxelShapeCollision) & EntityAccess#a(Entity, VoxelShape)
|
||||
+ net.minecraft.world.phys.shapes.VoxelShape voxelshape = data.getCollisionShape(this, position, voxelshapedcollision);
|
||||
+ if (voxelshape.isEmpty()) {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ voxelshape = voxelshape.move((double) position.getX(), (double) position.getY(), (double) position.getZ());
|
||||
+ if (voxelshape.isEmpty()) {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ List<Entity> entities = this.getEntities(null, voxelshape.bounds());
|
||||
+ for (int i = 0, len = entities.size(); i < len; ++i) {
|
||||
+ Entity entity = entities.get(i);
|
||||
+
|
||||
+ if (checkCanSee && source instanceof net.minecraft.server.level.ServerPlayer && entity instanceof net.minecraft.server.level.ServerPlayer
|
||||
+ && !((net.minecraft.server.level.ServerPlayer) source).getBukkitEntity().canSee(((net.minecraft.server.level.ServerPlayer) entity).getBukkitEntity())) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ // !entity1.dead && entity1.i && (entity == null || !entity1.x(entity));
|
||||
+ // elide the last check since vanilla calls with entity = null
|
||||
+ // only we care about the source for the canSee check
|
||||
+ if (entity.isRemoved() || !entity.blocksBuilding) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (net.minecraft.world.phys.shapes.Shapes.joinIsNotEmpty(voxelshape, net.minecraft.world.phys.shapes.Shapes.create(entity.getBoundingBox()), net.minecraft.world.phys.shapes.BooleanOp.AND)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+ }
|
||||
+ // Paper end - Cancel hit for vanished players
|
||||
@Override
|
||||
public boolean isClientSide() {
|
||||
return this.isClientSide;
|
||||
@@ -163,6 +332,13 @@
|
||||
return null;
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ public net.minecraft.world.phys.BlockHitResult.Type clipDirect(Vec3 start, Vec3 end, net.minecraft.world.phys.shapes.CollisionContext context) {
|
||||
+ // To be patched over
|
||||
+ return this.clip(new ClipContext(start, end, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, context)).getType();
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public boolean isInWorldBounds(BlockPos pos) {
|
||||
return !this.isOutsideBuildHeight(pos) && Level.isInWorldBoundsHorizontal(pos);
|
||||
}
|
||||
@@ -172,25 +348,87 @@
|
||||
}
|
||||
|
||||
private static boolean isInWorldBoundsHorizontal(BlockPos pos) {
|
||||
- return pos.getX() >= -30000000 && pos.getZ() >= -30000000 && pos.getX() < 30000000 && pos.getZ() < 30000000;
|
||||
+ return pos.getX() >= -30000000 && pos.getZ() >= -30000000 && pos.getX() < 30000000 && pos.getZ() < 30000000; // Diff on change warnUnsafeChunk()
|
||||
}
|
||||
|
||||
private static boolean isOutsideSpawnableHeight(int y) {
|
||||
return y < -20000000 || y >= 20000000;
|
||||
}
|
||||
|
||||
- public LevelChunk getChunkAt(BlockPos pos) {
|
||||
+ public final LevelChunk getChunkAt(BlockPos pos) { // Paper - help inline
|
||||
return this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()));
|
||||
}
|
||||
|
||||
@Override
|
||||
- public LevelChunk getChunk(int chunkX, int chunkZ) {
|
||||
- return (LevelChunk) this.getChunk(chunkX, chunkZ, ChunkStatus.FULL);
|
||||
+ public final LevelChunk getChunk(int chunkX, int chunkZ) { // Paper - final to help inline
|
||||
+ // Paper start - Perf: make sure loaded chunks get the inlined variant of this function
|
||||
+ net.minecraft.server.level.ServerChunkCache cps = ((ServerLevel)this).getChunkSource();
|
||||
+ LevelChunk ifLoaded = cps.getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
||||
+ if (ifLoaded != null) {
|
||||
+ return ifLoaded;
|
||||
+ }
|
||||
+ return (LevelChunk) cps.getChunk(chunkX, chunkZ, ChunkStatus.FULL, true); // Paper - avoid a method jump
|
||||
+ // Paper end - Perf: make sure loaded chunks get the inlined variant of this function
|
||||
}
|
||||
|
||||
+ // Paper start - if loaded
|
||||
@Nullable
|
||||
@Override
|
||||
+ public final ChunkAccess getChunkIfLoadedImmediately(int x, int z) {
|
||||
+ return ((ServerLevel)this).chunkSource.getChunkAtIfLoadedImmediately(x, z);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ @Nullable
|
||||
+ public final BlockState getBlockStateIfLoaded(BlockPos pos) {
|
||||
+ // CraftBukkit start - tree generation
|
||||
+ if (this.captureTreeGeneration) {
|
||||
+ CraftBlockState previous = this.capturedBlockStates.get(pos);
|
||||
+ if (previous != null) {
|
||||
+ return previous.getHandle();
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+ if (this.isOutsideBuildHeight(pos)) {
|
||||
+ return Blocks.VOID_AIR.defaultBlockState();
|
||||
+ } else {
|
||||
+ ChunkAccess chunk = this.getChunkIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4);
|
||||
+
|
||||
+ return chunk == null ? null : chunk.getBlockState(pos);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final FluidState getFluidIfLoaded(BlockPos blockposition) {
|
||||
+ ChunkAccess chunk = this.getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4);
|
||||
+
|
||||
+ return chunk == null ? null : chunk.getFluidState(blockposition);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final boolean hasChunkAt(BlockPos pos) {
|
||||
+ return getChunkIfLoaded(pos.getX() >> 4, pos.getZ() >> 4) != null; // Paper - Perf: Optimize Level.hasChunkAt(BlockPosition)Z
|
||||
+ }
|
||||
+
|
||||
+ public final boolean isLoadedAndInBounds(BlockPos blockposition) { // Paper - final for inline
|
||||
+ return getWorldBorder().isWithinBounds(blockposition) && getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4) != null;
|
||||
+ }
|
||||
+
|
||||
+ public @Nullable LevelChunk getChunkIfLoaded(int x, int z) { // Overridden in WorldServer for ABI compat which has final
|
||||
+ return ((ServerLevel) this).getChunkSource().getChunkAtIfLoadedImmediately(x, z);
|
||||
+ }
|
||||
+ public final @Nullable LevelChunk getChunkIfLoaded(BlockPos blockposition) {
|
||||
+ return ((ServerLevel) this).getChunkSource().getChunkAtIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4);
|
||||
+ }
|
||||
+
|
||||
+ // reduces need to do isLoaded before getType
|
||||
+ public final @Nullable BlockState getBlockStateIfLoadedAndInBounds(BlockPos blockposition) {
|
||||
+ return getWorldBorder().isWithinBounds(blockposition) ? getBlockStateIfLoaded(blockposition) : null;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
public ChunkAccess getChunk(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create) {
|
||||
+ // Paper end
|
||||
ChunkAccess ichunkaccess = this.getChunkSource().getChunk(chunkX, chunkZ, leastStatus, create);
|
||||
|
||||
if (ichunkaccess == null && create) {
|
||||
@@ -207,6 +445,22 @@
|
||||
|
||||
@Override
|
||||
public boolean setBlock(BlockPos pos, BlockState state, int flags, int maxUpdateDepth) {
|
||||
+ // CraftBukkit start - tree generation
|
||||
+ if (this.captureTreeGeneration) {
|
||||
+ // Paper start - Protect Bedrock and End Portal/Frames from being destroyed
|
||||
+ BlockState type = getBlockState(pos);
|
||||
+ if (!type.isDestroyable()) return false;
|
||||
+ // Paper end - Protect Bedrock and End Portal/Frames from being destroyed
|
||||
+ CraftBlockState blockstate = this.capturedBlockStates.get(pos);
|
||||
+ if (blockstate == null) {
|
||||
+ blockstate = CapturedBlockState.getTreeBlockState(this, pos, flags);
|
||||
+ this.capturedBlockStates.put(pos.immutable(), blockstate);
|
||||
+ }
|
||||
+ blockstate.setData(state);
|
||||
+ blockstate.setFlag(flags);
|
||||
+ return true;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
if (this.isOutsideBuildHeight(pos)) {
|
||||
return false;
|
||||
} else if (!this.isClientSide && this.isDebug()) {
|
||||
@@ -214,44 +468,125 @@
|
||||
} else {
|
||||
LevelChunk chunk = this.getChunkAt(pos);
|
||||
Block block = state.getBlock();
|
||||
- BlockState iblockdata1 = chunk.setBlockState(pos, state, (flags & 64) != 0);
|
||||
|
||||
+ // CraftBukkit start - capture blockstates
|
||||
+ boolean captured = false;
|
||||
+ if (this.captureBlockStates && !this.capturedBlockStates.containsKey(pos)) {
|
||||
+ CraftBlockState blockstate = (CraftBlockState) world.getBlockAt(pos.getX(), pos.getY(), pos.getZ()).getState(); // Paper - use CB getState to get a suitable snapshot
|
||||
+ blockstate.setFlag(flags); // Paper - set flag
|
||||
+ this.capturedBlockStates.put(pos.immutable(), blockstate);
|
||||
+ captured = true;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
+ BlockState iblockdata1 = chunk.setBlockState(pos, state, (flags & 64) != 0, (flags & 1024) == 0); // CraftBukkit custom NO_PLACE flag
|
||||
+
|
||||
if (iblockdata1 == null) {
|
||||
+ // CraftBukkit start - remove blockstate if failed (or the same)
|
||||
+ if (this.captureBlockStates && captured) {
|
||||
+ this.capturedBlockStates.remove(pos);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
return false;
|
||||
} else {
|
||||
BlockState iblockdata2 = this.getBlockState(pos);
|
||||
|
||||
- if (iblockdata2 == state) {
|
||||
+ /*
|
||||
+ if (iblockdata2 == iblockdata) {
|
||||
if (iblockdata1 != iblockdata2) {
|
||||
- this.setBlocksDirty(pos, iblockdata1, iblockdata2);
|
||||
+ this.setBlocksDirty(blockposition, iblockdata1, iblockdata2);
|
||||
}
|
||||
|
||||
- if ((flags & 2) != 0 && (!this.isClientSide || (flags & 4) == 0) && (this.isClientSide || chunk.getFullStatus() != null && chunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING))) {
|
||||
- this.sendBlockUpdated(pos, iblockdata1, state, flags);
|
||||
+ if ((i & 2) != 0 && (!this.isClientSide || (i & 4) == 0) && (this.isClientSide || chunk.getFullStatus() != null && chunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING))) {
|
||||
+ this.sendBlockUpdated(blockposition, iblockdata1, iblockdata, i);
|
||||
}
|
||||
|
||||
- if ((flags & 1) != 0) {
|
||||
- this.blockUpdated(pos, iblockdata1.getBlock());
|
||||
- if (!this.isClientSide && state.hasAnalogOutputSignal()) {
|
||||
- this.updateNeighbourForOutputSignal(pos, block);
|
||||
+ if ((i & 1) != 0) {
|
||||
+ this.blockUpdated(blockposition, iblockdata1.getBlock());
|
||||
+ if (!this.isClientSide && iblockdata.hasAnalogOutputSignal()) {
|
||||
+ this.updateNeighbourForOutputSignal(blockposition, block);
|
||||
}
|
||||
}
|
||||
|
||||
- if ((flags & 16) == 0 && maxUpdateDepth > 0) {
|
||||
- int k = flags & -34;
|
||||
+ if ((i & 16) == 0 && j > 0) {
|
||||
+ int k = i & -34;
|
||||
|
||||
- iblockdata1.updateIndirectNeighbourShapes(this, pos, k, maxUpdateDepth - 1);
|
||||
- state.updateNeighbourShapes(this, pos, k, maxUpdateDepth - 1);
|
||||
- state.updateIndirectNeighbourShapes(this, pos, k, maxUpdateDepth - 1);
|
||||
+ iblockdata1.updateIndirectNeighbourShapes(this, blockposition, k, j - 1);
|
||||
+ iblockdata.updateNeighbourShapes(this, blockposition, k, j - 1);
|
||||
+ iblockdata.updateIndirectNeighbourShapes(this, blockposition, k, j - 1);
|
||||
}
|
||||
|
||||
- this.onBlockStateChange(pos, iblockdata1, iblockdata2);
|
||||
+ this.onBlockStateChange(blockposition, iblockdata1, iblockdata2);
|
||||
}
|
||||
+ */
|
||||
|
||||
+ // CraftBukkit start
|
||||
+ if (!this.captureBlockStates) { // Don't notify clients or update physics while capturing blockstates
|
||||
+ // Modularize client and physic updates
|
||||
+ // Spigot start
|
||||
+ try {
|
||||
+ this.notifyAndUpdatePhysics(pos, chunk, iblockdata1, state, iblockdata2, flags, maxUpdateDepth);
|
||||
+ } catch (StackOverflowError ex) {
|
||||
+ Level.lastPhysicsProblem = new BlockPos(pos);
|
||||
+ }
|
||||
+ // Spigot end
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
return true;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // CraftBukkit start - Split off from above in order to directly send client and physic updates
|
||||
+ public void notifyAndUpdatePhysics(BlockPos blockposition, LevelChunk chunk, BlockState oldBlock, BlockState newBlock, BlockState actualBlock, int i, int j) {
|
||||
+ BlockState iblockdata = newBlock;
|
||||
+ BlockState iblockdata1 = oldBlock;
|
||||
+ BlockState iblockdata2 = actualBlock;
|
||||
+ if (iblockdata2 == iblockdata) {
|
||||
+ if (iblockdata1 != iblockdata2) {
|
||||
+ this.setBlocksDirty(blockposition, iblockdata1, iblockdata2);
|
||||
+ }
|
||||
+
|
||||
+ if ((i & 2) != 0 && (!this.isClientSide || (i & 4) == 0) && (this.isClientSide || chunk == null || (chunk.getFullStatus() != null && chunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING)))) { // allow chunk to be null here as chunk.isReady() is false when we send our notification during block placement
|
||||
+ this.sendBlockUpdated(blockposition, iblockdata1, iblockdata, i);
|
||||
+ }
|
||||
+
|
||||
+ if ((i & 1) != 0) {
|
||||
+ this.blockUpdated(blockposition, iblockdata1.getBlock());
|
||||
+ if (!this.isClientSide && iblockdata.hasAnalogOutputSignal()) {
|
||||
+ this.updateNeighbourForOutputSignal(blockposition, newBlock.getBlock());
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if ((i & 16) == 0 && j > 0) {
|
||||
+ int k = i & -34;
|
||||
+
|
||||
+ // CraftBukkit start
|
||||
+ iblockdata1.updateIndirectNeighbourShapes(this, blockposition, k, j - 1); // Don't call an event for the old block to limit event spam
|
||||
+ CraftWorld world = ((ServerLevel) this).getWorld();
|
||||
+ boolean cancelledUpdates = false; // Paper - Fix block place logic
|
||||
+ if (world != null && ((ServerLevel)this).hasPhysicsEvent) { // Paper - BlockPhysicsEvent
|
||||
+ BlockPhysicsEvent event = new BlockPhysicsEvent(world.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), CraftBlockData.fromData(iblockdata));
|
||||
+ this.getCraftServer().getPluginManager().callEvent(event);
|
||||
+
|
||||
+ cancelledUpdates = event.isCancelled(); // Paper - Fix block place logic
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+ if (!cancelledUpdates) { // Paper - Fix block place logic
|
||||
+ iblockdata.updateNeighbourShapes(this, blockposition, k, j - 1);
|
||||
+ iblockdata.updateIndirectNeighbourShapes(this, blockposition, k, j - 1);
|
||||
+ } // Paper - Fix block place logic
|
||||
}
|
||||
+
|
||||
+ // CraftBukkit start - SPIGOT-5710
|
||||
+ if (!this.preventPoiUpdated) {
|
||||
+ this.onBlockStateChange(blockposition, iblockdata1, iblockdata2);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
}
|
||||
+ // CraftBukkit end
|
||||
|
||||
public void onBlockStateChange(BlockPos pos, BlockState oldBlock, BlockState newBlock) {}
|
||||
|
||||
@@ -270,15 +605,33 @@
|
||||
return false;
|
||||
} else {
|
||||
FluidState fluid = this.getFluidState(pos);
|
||||
+ // Paper start - BlockDestroyEvent; while the above setAir method is named same and looks very similar
|
||||
+ // they are NOT used with same intent and the above should not fire this event. The above method is more of a BlockSetToAirEvent,
|
||||
+ // it doesn't imply destruction of a block that plays a sound effect / drops an item.
|
||||
+ boolean playEffect = true;
|
||||
+ BlockState effectType = iblockdata;
|
||||
+ int xp = iblockdata.getBlock().getExpDrop(iblockdata, (ServerLevel) this, pos, ItemStack.EMPTY, true);
|
||||
+ if (com.destroystokyo.paper.event.block.BlockDestroyEvent.getHandlerList().getRegisteredListeners().length > 0) {
|
||||
+ com.destroystokyo.paper.event.block.BlockDestroyEvent event = new com.destroystokyo.paper.event.block.BlockDestroyEvent(org.bukkit.craftbukkit.block.CraftBlock.at(this, pos), fluid.createLegacyBlock().createCraftBlockData(), effectType.createCraftBlockData(), xp, drop);
|
||||
+ if (!event.callEvent()) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ effectType = ((CraftBlockData) event.getEffectBlock()).getState();
|
||||
+ playEffect = event.playEffect();
|
||||
+ drop = event.willDrop();
|
||||
+ xp = event.getExpToDrop();
|
||||
+ }
|
||||
+ // Paper end - BlockDestroyEvent
|
||||
|
||||
- if (!(iblockdata.getBlock() instanceof BaseFireBlock)) {
|
||||
- this.levelEvent(2001, pos, Block.getId(iblockdata));
|
||||
+ if (playEffect && !(effectType.getBlock() instanceof BaseFireBlock)) { // Paper - BlockDestroyEvent
|
||||
+ this.levelEvent(2001, pos, Block.getId(effectType)); // Paper - BlockDestroyEvent
|
||||
}
|
||||
|
||||
if (drop) {
|
||||
BlockEntity tileentity = iblockdata.hasBlockEntity() ? this.getBlockEntity(pos) : null;
|
||||
|
||||
- Block.dropResources(iblockdata, this, pos, tileentity, breakingEntity, ItemStack.EMPTY);
|
||||
+ Block.dropResources(iblockdata, this, pos, tileentity, breakingEntity, ItemStack.EMPTY, false); // Paper - Properly handle xp dropping
|
||||
+ iblockdata.getBlock().popExperience((ServerLevel) this, pos, xp, breakingEntity); // Paper - Properly handle xp dropping; custom amount
|
||||
}
|
||||
|
||||
boolean flag1 = this.setBlock(pos, fluid.createLegacyBlock(), 3, maxUpdateDepth);
|
||||
@@ -340,10 +693,18 @@
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState(BlockPos pos) {
|
||||
+ // CraftBukkit start - tree generation
|
||||
+ if (this.captureTreeGeneration) {
|
||||
+ CraftBlockState previous = this.capturedBlockStates.get(pos); // Paper
|
||||
+ if (previous != null) {
|
||||
+ return previous.getHandle();
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
if (this.isOutsideBuildHeight(pos)) {
|
||||
return Blocks.VOID_AIR.defaultBlockState();
|
||||
} else {
|
||||
- LevelChunk chunk = this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()));
|
||||
+ ChunkAccess chunk = this.getChunk(pos.getX() >> 4, pos.getZ() >> 4, ChunkStatus.FULL, true); // Paper - manually inline to reduce hops and avoid unnecessary null check to reduce total byte code size, this should never return null and if it does we will see it the next line but the real stack trace will matter in the chunk engine
|
||||
|
||||
return chunk.getBlockState(pos);
|
||||
}
|
||||
@@ -446,34 +807,53 @@
|
||||
this.pendingBlockEntityTickers.clear();
|
||||
}
|
||||
|
||||
- Iterator<TickingBlockEntity> iterator = this.blockEntityTickers.iterator();
|
||||
+ // Spigot start
|
||||
+ // Iterator<TickingBlockEntity> iterator = this.blockEntityTickers.iterator();
|
||||
boolean flag = this.tickRateManager().runsNormally();
|
||||
|
||||
- while (iterator.hasNext()) {
|
||||
- TickingBlockEntity tickingblockentity = (TickingBlockEntity) iterator.next();
|
||||
+ int tilesThisCycle = 0;
|
||||
+ var toRemove = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<TickingBlockEntity>(); // Paper - Fix MC-117075; use removeAll
|
||||
+ toRemove.add(null); // Paper - Fix MC-117075
|
||||
+ for (tileTickPosition = 0; tileTickPosition < this.blockEntityTickers.size(); tileTickPosition++) { // Paper - Disable tick limiters
|
||||
+ this.tileTickPosition = (this.tileTickPosition < this.blockEntityTickers.size()) ? this.tileTickPosition : 0;
|
||||
+ TickingBlockEntity tickingblockentity = (TickingBlockEntity) this.blockEntityTickers.get(this.tileTickPosition);
|
||||
+ // Spigot end
|
||||
|
||||
if (tickingblockentity.isRemoved()) {
|
||||
- iterator.remove();
|
||||
+ // Spigot start
|
||||
+ tilesThisCycle--;
|
||||
+ toRemove.add(tickingblockentity); // Paper - Fix MC-117075; use removeAll
|
||||
+ // Spigot end
|
||||
} else if (flag && this.shouldTickBlocksAt(tickingblockentity.getPos())) {
|
||||
tickingblockentity.tick();
|
||||
}
|
||||
}
|
||||
+ this.blockEntityTickers.removeAll(toRemove); // Paper - Fix MC-117075
|
||||
|
||||
this.tickingBlockEntities = false;
|
||||
gameprofilerfiller.pop();
|
||||
+ this.spigotConfig.currentPrimedTnt = 0; // Spigot
|
||||
}
|
||||
|
||||
public <T extends Entity> void guardEntityTick(Consumer<T> tickConsumer, T entity) {
|
||||
try {
|
||||
tickConsumer.accept(entity);
|
||||
} catch (Throwable throwable) {
|
||||
- CrashReport crashreport = CrashReport.forThrowable(throwable, "Ticking entity");
|
||||
- CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Entity being ticked");
|
||||
-
|
||||
- entity.fillCrashReportCategory(crashreportsystemdetails);
|
||||
- throw new ReportedException(crashreport);
|
||||
+ // Paper start - Prevent block entity and entity crashes
|
||||
+ final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level().getWorld().getName(), entity.getX(), entity.getY(), entity.getZ());
|
||||
+ MinecraftServer.LOGGER.error(msg, throwable);
|
||||
+ getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(msg, throwable))); // Paper - ServerExceptionEvent
|
||||
+ entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
|
||||
+ // Paper end - Prevent block entity and entity crashes
|
||||
}
|
||||
+ }
|
||||
+ // Paper start - Option to prevent armor stands from doing entity lookups
|
||||
+ @Override
|
||||
+ public boolean noCollision(@Nullable Entity entity, AABB box) {
|
||||
+ if (entity instanceof net.minecraft.world.entity.decoration.ArmorStand && !entity.level().paperConfig().entities.armorStands.doCollisionEntityLookups) return false;
|
||||
+ return LevelAccessor.super.noCollision(entity, box);
|
||||
}
|
||||
+ // Paper end - Option to prevent armor stands from doing entity lookups
|
||||
|
||||
public boolean shouldTickDeath(Entity entity) {
|
||||
return true;
|
||||
@@ -510,13 +890,32 @@
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity getBlockEntity(BlockPos pos) {
|
||||
- return this.isOutsideBuildHeight(pos) ? null : (!this.isClientSide && Thread.currentThread() != this.thread ? null : this.getChunkAt(pos).getBlockEntity(pos, LevelChunk.EntityCreationType.IMMEDIATE));
|
||||
+ // CraftBukkit start
|
||||
+ return this.getBlockEntity(pos, true);
|
||||
}
|
||||
|
||||
+ @Nullable
|
||||
+ public BlockEntity getBlockEntity(BlockPos blockposition, boolean validate) {
|
||||
+ // Paper start - Perf: Optimize capturedTileEntities lookup
|
||||
+ net.minecraft.world.level.block.entity.BlockEntity blockEntity;
|
||||
+ if (!this.capturedTileEntities.isEmpty() && (blockEntity = this.capturedTileEntities.get(blockposition)) != null) {
|
||||
+ return blockEntity;
|
||||
+ }
|
||||
+ // Paper end - Perf: Optimize capturedTileEntities lookup
|
||||
+ // CraftBukkit end
|
||||
+ return this.isOutsideBuildHeight(blockposition) ? null : (!this.isClientSide && Thread.currentThread() != this.thread ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, LevelChunk.EntityCreationType.IMMEDIATE));
|
||||
+ }
|
||||
+
|
||||
public void setBlockEntity(BlockEntity blockEntity) {
|
||||
BlockPos blockposition = blockEntity.getBlockPos();
|
||||
|
||||
if (!this.isOutsideBuildHeight(blockposition)) {
|
||||
+ // CraftBukkit start
|
||||
+ if (this.captureBlockStates) {
|
||||
+ this.capturedTileEntities.put(blockposition.immutable(), blockEntity);
|
||||
+ return;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
this.getChunkAt(blockposition).addAndRegisterBlockEntity(blockEntity);
|
||||
}
|
||||
}
|
||||
@@ -643,7 +1042,7 @@
|
||||
|
||||
for (int k = 0; k < j; ++k) {
|
||||
EnderDragonPart entitycomplexpart = aentitycomplexpart[k];
|
||||
- T t0 = (Entity) filter.tryCast(entitycomplexpart);
|
||||
+ T t0 = filter.tryCast(entitycomplexpart); // CraftBukkit - decompile error
|
||||
|
||||
if (t0 != null && predicate.test(t0)) {
|
||||
result.add(t0);
|
||||
@@ -912,7 +1311,7 @@
|
||||
|
||||
public static enum ExplosionInteraction implements StringRepresentable {
|
||||
|
||||
- NONE("none"), BLOCK("block"), MOB("mob"), TNT("tnt"), TRIGGER("trigger");
|
||||
+ NONE("none"), BLOCK("block"), MOB("mob"), TNT("tnt"), TRIGGER("trigger"), STANDARD("standard"); // CraftBukkit - Add STANDARD which will always use Explosion.Effect.DESTROY
|
||||
|
||||
public static final Codec<Level.ExplosionInteraction> CODEC = StringRepresentable.fromEnum(Level.ExplosionInteraction::values);
|
||||
private final String id;
|
||||
@@ -1,9 +0,0 @@
|
||||
--- a/net/minecraft/world/level/LevelAccessor.java
|
||||
+++ b/net/minecraft/world/level/LevelAccessor.java
|
||||
@@ -101,4 +101,6 @@
|
||||
default void gameEvent(ResourceKey<GameEvent> event, BlockPos pos, GameEvent.Context emitter) {
|
||||
this.gameEvent((Holder) this.registryAccess().lookupOrThrow(Registries.GAME_EVENT).getOrThrow(event), pos, emitter);
|
||||
}
|
||||
+
|
||||
+ net.minecraft.server.level.ServerLevel getMinecraftWorld(); // CraftBukkit
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
--- a/net/minecraft/world/level/LevelReader.java
|
||||
+++ b/net/minecraft/world/level/LevelReader.java
|
||||
@@ -26,6 +26,9 @@
|
||||
@Nullable
|
||||
ChunkAccess getChunk(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create);
|
||||
|
||||
+ @Nullable ChunkAccess getChunkIfLoadedImmediately(int x, int z); // Paper - ifLoaded api (we need this since current impl blocks if the chunk is loading)
|
||||
+ @Nullable default ChunkAccess getChunkIfLoadedImmediately(BlockPos pos) { return this.getChunkIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4);}
|
||||
+
|
||||
@Deprecated
|
||||
boolean hasChunk(int chunkX, int chunkZ);
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
--- a/net/minecraft/world/level/LevelWriter.java
|
||||
+++ b/net/minecraft/world/level/LevelWriter.java
|
||||
@@ -28,4 +28,10 @@
|
||||
default boolean addFreshEntity(Entity entity) {
|
||||
return false;
|
||||
}
|
||||
+
|
||||
+ // CraftBukkit start
|
||||
+ default boolean addFreshEntity(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
--- a/net/minecraft/world/level/NaturalSpawner.java
|
||||
+++ b/net/minecraft/world/level/NaturalSpawner.java
|
||||
@@ -47,8 +47,13 @@
|
||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||
import net.minecraft.world.level.levelgen.structure.structures.NetherFortressStructure;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
+import net.minecraft.world.level.storage.LevelData;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.slf4j.Logger;
|
||||
+import org.bukkit.craftbukkit.util.CraftSpawnCategory;
|
||||
+import org.bukkit.entity.SpawnCategory;
|
||||
+import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
|
||||
+// CraftBukkit end
|
||||
|
||||
public final class NaturalSpawner {
|
||||
|
||||
@@ -82,6 +87,13 @@
|
||||
MobCategory enumcreaturetype = entity.getType().getCategory();
|
||||
|
||||
if (enumcreaturetype != MobCategory.MISC) {
|
||||
+ // Paper start - Only count natural spawns
|
||||
+ if (!entity.level().paperConfig().entities.spawning.countAllMobsForSpawning &&
|
||||
+ !(entity.spawnReason == org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL ||
|
||||
+ entity.spawnReason == org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ // Paper end - Only count natural spawns
|
||||
BlockPos blockposition = entity.blockPosition();
|
||||
|
||||
chunkSource.query(ChunkPos.asLong(blockposition), (chunk) -> {
|
||||
@@ -107,15 +119,31 @@
|
||||
return (Biome) chunk.getNoiseBiome(QuartPos.fromBlock(pos.getX()), QuartPos.fromBlock(pos.getY()), QuartPos.fromBlock(pos.getZ())).value();
|
||||
}
|
||||
|
||||
- public static List<MobCategory> getFilteredSpawningCategories(NaturalSpawner.SpawnState info, boolean spawnAnimals, boolean spawnMonsters, boolean rare) {
|
||||
+ // CraftBukkit start - add server
|
||||
+ public static List<MobCategory> getFilteredSpawningCategories(NaturalSpawner.SpawnState spawnercreature_d, boolean flag, boolean flag1, boolean flag2, ServerLevel worldserver) {
|
||||
+ LevelData worlddata = worldserver.getLevelData(); // CraftBukkit - Other mob type spawn tick rate
|
||||
+ // CraftBukkit end
|
||||
List<MobCategory> list = new ArrayList(NaturalSpawner.SPAWNING_CATEGORIES.length);
|
||||
MobCategory[] aenumcreaturetype = NaturalSpawner.SPAWNING_CATEGORIES;
|
||||
int i = aenumcreaturetype.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
MobCategory enumcreaturetype = aenumcreaturetype[j];
|
||||
+ // CraftBukkit start - Use per-world spawn limits
|
||||
+ boolean spawnThisTick = true;
|
||||
+ int limit = enumcreaturetype.getMaxInstancesPerChunk();
|
||||
+ SpawnCategory spawnCategory = CraftSpawnCategory.toBukkit(enumcreaturetype);
|
||||
+ if (CraftSpawnCategory.isValidForLimits(spawnCategory)) {
|
||||
+ spawnThisTick = worldserver.ticksPerSpawnCategory.getLong(spawnCategory) != 0 && worlddata.getGameTime() % worldserver.ticksPerSpawnCategory.getLong(spawnCategory) == 0;
|
||||
+ limit = worldserver.getWorld().getSpawnLimit(spawnCategory);
|
||||
+ }
|
||||
|
||||
- if ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (rare || !enumcreaturetype.isPersistent()) && info.canSpawnForCategoryGlobal(enumcreaturetype)) {
|
||||
+ if (!spawnThisTick || limit == 0) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if ((flag || !enumcreaturetype.isFriendly()) && (flag1 || enumcreaturetype.isFriendly()) && (flag2 || !enumcreaturetype.isPersistent()) && spawnercreature_d.canSpawnForCategoryGlobal(enumcreaturetype, limit)) {
|
||||
+ // CraftBukkit end
|
||||
list.add(enumcreaturetype);
|
||||
}
|
||||
}
|
||||
@@ -144,6 +172,16 @@
|
||||
gameprofilerfiller.pop();
|
||||
}
|
||||
|
||||
+ // Paper start - Add mobcaps commands
|
||||
+ public static int globalLimitForCategory(final ServerLevel level, final MobCategory category, final int spawnableChunkCount) {
|
||||
+ final int categoryLimit = level.getWorld().getSpawnLimitUnsafe(CraftSpawnCategory.toBukkit(category));
|
||||
+ if (categoryLimit < 1) {
|
||||
+ return categoryLimit;
|
||||
+ }
|
||||
+ return categoryLimit * spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
|
||||
+ }
|
||||
+ // Paper end - Add mobcaps commands
|
||||
+
|
||||
public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) {
|
||||
BlockPos blockposition = NaturalSpawner.getRandomPosWithin(world, chunk);
|
||||
|
||||
@@ -164,9 +202,9 @@
|
||||
StructureManager structuremanager = world.structureManager();
|
||||
ChunkGenerator chunkgenerator = world.getChunkSource().getGenerator();
|
||||
int i = pos.getY();
|
||||
- BlockState iblockdata = chunk.getBlockState(pos);
|
||||
+ BlockState iblockdata = world.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn
|
||||
|
||||
- if (!iblockdata.isRedstoneConductor(chunk, pos)) {
|
||||
+ if (iblockdata != null && !iblockdata.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn
|
||||
BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
|
||||
int j = 0;
|
||||
int k = 0;
|
||||
@@ -195,7 +233,7 @@
|
||||
if (entityhuman != null) {
|
||||
double d2 = entityhuman.distanceToSqr(d0, (double) i, d1);
|
||||
|
||||
- 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) {
|
||||
Optional<MobSpawnSettings.SpawnerData> optional = NaturalSpawner.getRandomSpawnMobAt(world, structuremanager, chunkgenerator, group, world.random, blockposition_mutableblockposition);
|
||||
|
||||
@@ -207,7 +245,13 @@
|
||||
j1 = biomesettingsmobs_c.minCount + world.random.nextInt(1 + biomesettingsmobs_c.maxCount - biomesettingsmobs_c.minCount);
|
||||
}
|
||||
|
||||
- if (NaturalSpawner.isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2) && checker.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) {
|
||||
+ // Paper start - PreCreatureSpawnEvent
|
||||
+ PreSpawnStatus doSpawning = isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2);
|
||||
+ if (doSpawning == PreSpawnStatus.ABORT) {
|
||||
+ return;
|
||||
+ }
|
||||
+ if (doSpawning == PreSpawnStatus.SUCCESS && checker.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) {
|
||||
+ // Paper end - PreCreatureSpawnEvent
|
||||
Mob entityinsentient = NaturalSpawner.getMobForSpawn(world, biomesettingsmobs_c.type);
|
||||
|
||||
if (entityinsentient == null) {
|
||||
@@ -217,10 +261,15 @@
|
||||
entityinsentient.moveTo(d0, (double) i, d1, world.random.nextFloat() * 360.0F, 0.0F);
|
||||
if (NaturalSpawner.isValidPositionForMob(world, entityinsentient, d2)) {
|
||||
groupdataentity = entityinsentient.finalizeSpawn(world, world.getCurrentDifficultyAt(entityinsentient.blockPosition()), EntitySpawnReason.NATURAL, groupdataentity);
|
||||
- ++j;
|
||||
- ++k1;
|
||||
- world.addFreshEntityWithPassengers(entityinsentient);
|
||||
- runner.run(entityinsentient, chunk);
|
||||
+ // CraftBukkit start
|
||||
+ // SPIGOT-7045: Give ocelot babies back their special spawn reason. Note: This is the only modification required as ocelots count as monsters which means they only spawn during normal chunk ticking and do not spawn during chunk generation as starter mobs.
|
||||
+ world.addFreshEntityWithPassengers(entityinsentient, (entityinsentient instanceof net.minecraft.world.entity.animal.Ocelot && !((org.bukkit.entity.Ageable) entityinsentient.getBukkitEntity()).isAdult()) ? SpawnReason.OCELOT_BABY : SpawnReason.NATURAL);
|
||||
+ if (!entityinsentient.isRemoved()) {
|
||||
+ ++j;
|
||||
+ ++k1;
|
||||
+ runner.run(entityinsentient, chunk);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
if (j >= entityinsentient.getMaxSpawnClusterSize()) {
|
||||
return;
|
||||
}
|
||||
@@ -250,10 +299,31 @@
|
||||
return squaredDistance <= 576.0D ? false : (world.getSharedSpawnPos().closerToCenterThan(new Vec3((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D), 24.0D) ? false : Objects.equals(new ChunkPos(pos), chunk.getPos()) || world.isNaturalSpawningAllowed((BlockPos) pos));
|
||||
}
|
||||
|
||||
- private static boolean isValidSpawnPostitionForType(ServerLevel world, MobCategory group, StructureManager structureAccessor, ChunkGenerator chunkGenerator, MobSpawnSettings.SpawnerData spawnEntry, BlockPos.MutableBlockPos pos, double squaredDistance) {
|
||||
+ // Paper start - PreCreatureSpawnEvent
|
||||
+ private enum PreSpawnStatus {
|
||||
+ FAIL,
|
||||
+ SUCCESS,
|
||||
+ CANCELLED,
|
||||
+ ABORT
|
||||
+ }
|
||||
+ private static PreSpawnStatus isValidSpawnPostitionForType(ServerLevel world, MobCategory group, StructureManager structureAccessor, ChunkGenerator chunkGenerator, MobSpawnSettings.SpawnerData spawnEntry, BlockPos.MutableBlockPos pos, double squaredDistance) {
|
||||
+ // Paper end - PreCreatureSpawnEvent
|
||||
EntityType<?> entitytypes = spawnEntry.type;
|
||||
|
||||
- return entitytypes.getCategory() == MobCategory.MISC ? false : (!entitytypes.canSpawnFarFromPlayer() && squaredDistance > (double) (entitytypes.getCategory().getDespawnDistance() * entitytypes.getCategory().getDespawnDistance()) ? false : (entitytypes.canSummon() && NaturalSpawner.canSpawnMobAt(world, structureAccessor, chunkGenerator, group, spawnEntry, pos) ? (!SpawnPlacements.isSpawnPositionOk(entitytypes, world, pos) ? false : (!SpawnPlacements.checkSpawnRules(entitytypes, world, EntitySpawnReason.NATURAL, pos, world.random) ? false : world.noCollision(entitytypes.getSpawnAABB((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D)))) : false));
|
||||
+ // Paper start - PreCreatureSpawnEvent
|
||||
+ com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent(
|
||||
+ io.papermc.paper.util.MCUtil.toLocation(world, pos),
|
||||
+ org.bukkit.craftbukkit.entity.CraftEntityType.minecraftToBukkit(entitytypes), SpawnReason.NATURAL
|
||||
+ );
|
||||
+ if (!event.callEvent()) {
|
||||
+ if (event.shouldAbortSpawn()) {
|
||||
+ return PreSpawnStatus.ABORT;
|
||||
+ }
|
||||
+ return PreSpawnStatus.CANCELLED;
|
||||
+ }
|
||||
+ // Paper end - PreCreatureSpawnEvent
|
||||
+
|
||||
+ return entitytypes.getCategory() == MobCategory.MISC ? PreSpawnStatus.FAIL : (!entitytypes.canSpawnFarFromPlayer() && squaredDistance > (double) (entitytypes.getCategory().getDespawnDistance() * entitytypes.getCategory().getDespawnDistance()) ? PreSpawnStatus.FAIL : (entitytypes.canSummon() && NaturalSpawner.canSpawnMobAt(world, structureAccessor, chunkGenerator, group, spawnEntry, pos) ? (!SpawnPlacements.isSpawnPositionOk(entitytypes, world, pos) ? PreSpawnStatus.FAIL : (!SpawnPlacements.checkSpawnRules(entitytypes, world, EntitySpawnReason.NATURAL, pos, world.random) ? PreSpawnStatus.FAIL : world.noCollision(entitytypes.getSpawnAABB((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D)) ? PreSpawnStatus.SUCCESS : PreSpawnStatus.FAIL)) : PreSpawnStatus.FAIL)); // Paper - PreCreatureSpawnEvent
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -268,6 +338,7 @@
|
||||
NaturalSpawner.LOGGER.warn("Can't spawn entity of type: {}", BuiltInRegistries.ENTITY_TYPE.getKey(type));
|
||||
} catch (Exception exception) {
|
||||
NaturalSpawner.LOGGER.warn("Failed to create mob", exception);
|
||||
+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(exception); // Paper - ServerExceptionEvent
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -356,6 +427,7 @@
|
||||
entity = biomesettingsmobs_c.type.create(world.getLevel(), EntitySpawnReason.NATURAL);
|
||||
} catch (Exception exception) {
|
||||
NaturalSpawner.LOGGER.warn("Failed to create mob", exception);
|
||||
+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(exception); // Paper - ServerExceptionEvent
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -369,7 +441,7 @@
|
||||
|
||||
if (entityinsentient.checkSpawnRules(world, EntitySpawnReason.CHUNK_GENERATION) && entityinsentient.checkSpawnObstruction(world)) {
|
||||
groupdataentity = entityinsentient.finalizeSpawn(world, world.getCurrentDifficultyAt(entityinsentient.blockPosition()), EntitySpawnReason.CHUNK_GENERATION, groupdataentity);
|
||||
- world.addFreshEntityWithPassengers(entityinsentient);
|
||||
+ world.addFreshEntityWithPassengers(entityinsentient, SpawnReason.CHUNK_GEN); // CraftBukkit
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
@@ -482,10 +554,12 @@
|
||||
return this.unmodifiableMobCategoryCounts;
|
||||
}
|
||||
|
||||
- boolean canSpawnForCategoryGlobal(MobCategory group) {
|
||||
- int i = group.getMaxInstancesPerChunk() * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
|
||||
+ // CraftBukkit start
|
||||
+ boolean canSpawnForCategoryGlobal(MobCategory enumcreaturetype, int limit) {
|
||||
+ int i = limit * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
|
||||
+ // CraftBukkit end
|
||||
|
||||
- return this.mobCategoryCounts.getInt(group) < i;
|
||||
+ return this.mobCategoryCounts.getInt(enumcreaturetype) < i;
|
||||
}
|
||||
|
||||
boolean canSpawnForCategoryLocal(MobCategory group, ChunkPos chunkPos) {
|
||||
@@ -1,51 +0,0 @@
|
||||
--- a/net/minecraft/world/level/PathNavigationRegion.java
|
||||
+++ b/net/minecraft/world/level/PathNavigationRegion.java
|
||||
@@ -8,6 +8,7 @@
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
+import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.Biomes;
|
||||
@@ -66,7 +67,7 @@
|
||||
private ChunkAccess getChunk(int chunkX, int chunkZ) {
|
||||
int i = chunkX - this.centerX;
|
||||
int j = chunkZ - this.centerZ;
|
||||
- if (i >= 0 && i < this.chunks.length && j >= 0 && j < this.chunks[i].length) {
|
||||
+ if (i >= 0 && i < this.chunks.length && j >= 0 && j < this.chunks[i].length) { // Paper - if this changes, update getChunkIfLoaded below
|
||||
ChunkAccess chunkAccess = this.chunks[i][j];
|
||||
return (ChunkAccess)(chunkAccess != null ? chunkAccess : new EmptyLevelChunk(this.level, new ChunkPos(chunkX, chunkZ), this.plains.get()));
|
||||
} else {
|
||||
@@ -74,7 +75,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start - if loaded util
|
||||
+ private @Nullable ChunkAccess getChunkIfLoaded(int x, int z) {
|
||||
+ // Based on getChunk(int, int)
|
||||
+ int xx = x - this.centerX;
|
||||
+ int zz = z - this.centerZ;
|
||||
+
|
||||
+ if (xx >= 0 && xx < this.chunks.length && zz >= 0 && zz < this.chunks[xx].length) {
|
||||
+ return this.chunks[xx][zz];
|
||||
+ }
|
||||
+ return null;
|
||||
+ }
|
||||
@Override
|
||||
+ public final FluidState getFluidIfLoaded(BlockPos blockposition) {
|
||||
+ ChunkAccess chunk = getChunkIfLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4);
|
||||
+ return chunk == null ? null : chunk.getFluidState(blockposition);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final BlockState getBlockStateIfLoaded(BlockPos blockposition) {
|
||||
+ ChunkAccess chunk = getChunkIfLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4);
|
||||
+ return chunk == null ? null : chunk.getBlockState(blockposition);
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
+ @Override
|
||||
public WorldBorder getWorldBorder() {
|
||||
return this.level.getWorldBorder();
|
||||
}
|
||||
@@ -1,342 +0,0 @@
|
||||
--- a/net/minecraft/world/level/ServerExplosion.java
|
||||
+++ b/net/minecraft/world/level/ServerExplosion.java
|
||||
@@ -22,18 +22,27 @@
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
+import net.minecraft.world.entity.boss.EnderDragonPart;
|
||||
+import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.entity.item.PrimedTnt;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.BaseFireBlock;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
-import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.gameevent.GameEvent;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
+import net.minecraft.world.level.block.Blocks;
|
||||
+import net.minecraft.world.level.block.state.BlockState;
|
||||
+import org.bukkit.craftbukkit.event.CraftEventFactory;
|
||||
+import org.bukkit.craftbukkit.util.CraftLocation;
|
||||
+import org.bukkit.event.entity.EntityExplodeEvent;
|
||||
+import org.bukkit.Location;
|
||||
+import org.bukkit.event.block.BlockExplodeEvent;
|
||||
+// CraftBukkit end
|
||||
|
||||
public class ServerExplosion implements Explosion {
|
||||
|
||||
@@ -50,16 +59,22 @@
|
||||
private final DamageSource damageSource;
|
||||
private final ExplosionDamageCalculator damageCalculator;
|
||||
private final Map<Player, Vec3> hitPlayers = new HashMap();
|
||||
+ // CraftBukkit - add field
|
||||
+ public boolean wasCanceled = false;
|
||||
+ public float yield;
|
||||
+ // CraftBukkit end
|
||||
+ public boolean excludeSourceFromDamage = true; // Paper - Allow explosions to damage source
|
||||
|
||||
public ServerExplosion(ServerLevel world, @Nullable Entity entity, @Nullable DamageSource damageSource, @Nullable ExplosionDamageCalculator behavior, Vec3 pos, float power, boolean createFire, Explosion.BlockInteraction destructionType) {
|
||||
this.level = world;
|
||||
this.source = entity;
|
||||
- this.radius = power;
|
||||
+ this.radius = (float) Math.max(power, 0.0); // CraftBukkit - clamp bad values
|
||||
this.center = pos;
|
||||
this.fire = createFire;
|
||||
this.blockInteraction = destructionType;
|
||||
this.damageSource = damageSource == null ? world.damageSources().explosion(this) : damageSource;
|
||||
this.damageCalculator = behavior == null ? this.makeDamageCalculator(entity) : behavior;
|
||||
+ this.yield = this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F; // CraftBukkit
|
||||
}
|
||||
|
||||
private ExplosionDamageCalculator makeDamageCalculator(@Nullable Entity entity) {
|
||||
@@ -135,7 +150,8 @@
|
||||
for (float f1 = 0.3F; f > 0.0F; f -= 0.22500001F) {
|
||||
BlockPos blockposition = BlockPos.containing(d4, d5, d6);
|
||||
BlockState iblockdata = this.level.getBlockState(blockposition);
|
||||
- FluidState fluid = this.level.getFluidState(blockposition);
|
||||
+ if (!iblockdata.isDestroyable()) continue; // Paper - Protect Bedrock and End Portal/Frames from being destroyed
|
||||
+ FluidState fluid = iblockdata.getFluidState(); // Paper - Perf: Optimize call to getFluid for explosions
|
||||
|
||||
if (!this.level.isInWorldBounds(blockposition)) {
|
||||
break;
|
||||
@@ -149,6 +165,15 @@
|
||||
|
||||
if (f > 0.0F && this.damageCalculator.shouldBlockExplode(this, this.level, blockposition, iblockdata, f)) {
|
||||
set.add(blockposition);
|
||||
+ // Paper start - prevent headless pistons from forming
|
||||
+ if (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowHeadlessPistons && iblockdata.getBlock() == Blocks.MOVING_PISTON) {
|
||||
+ net.minecraft.world.level.block.entity.BlockEntity extension = this.level.getBlockEntity(blockposition);
|
||||
+ if (extension instanceof net.minecraft.world.level.block.piston.PistonMovingBlockEntity blockEntity && blockEntity.isSourcePiston()) {
|
||||
+ net.minecraft.core.Direction direction = iblockdata.getValue(net.minecraft.world.level.block.piston.PistonHeadBlock.FACING);
|
||||
+ set.add(blockposition.relative(direction.getOpposite()));
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - prevent headless pistons from forming
|
||||
}
|
||||
|
||||
d4 += d0 * 0.30000001192092896D;
|
||||
@@ -171,7 +196,7 @@
|
||||
int l = Mth.floor(this.center.y + (double) f + 1.0D);
|
||||
int i1 = Mth.floor(this.center.z - (double) f - 1.0D);
|
||||
int j1 = Mth.floor(this.center.z + (double) f + 1.0D);
|
||||
- List<Entity> list = this.level.getEntities(this.source, new AABB((double) i, (double) k, (double) i1, (double) j, (double) l, (double) j1));
|
||||
+ List<Entity> list = this.level.getEntities(excludeSourceFromDamage ? this.source : null, new AABB((double) i, (double) k, (double) i1, (double) j, (double) l, (double) j1), (com.google.common.base.Predicate<Entity>) entity -> entity.isAlive() && !entity.isSpectator()); // Paper - Fix lag from explosions processing dead entities, Allow explosions to damage source
|
||||
Iterator iterator = list.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
@@ -192,10 +217,38 @@
|
||||
d3 /= d4;
|
||||
boolean flag = this.damageCalculator.shouldDamageEntity(this, entity);
|
||||
float f1 = this.damageCalculator.getKnockbackMultiplier(entity);
|
||||
- float f2 = !flag && f1 == 0.0F ? 0.0F : ServerExplosion.getSeenPercent(this.center, entity);
|
||||
+ float f2 = !flag && f1 == 0.0F ? 0.0F : this.getBlockDensity(this.center, entity); // Paper - Optimize explosions
|
||||
|
||||
if (flag) {
|
||||
- entity.hurtServer(this.level, this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, f2));
|
||||
+ // CraftBukkit start
|
||||
+
|
||||
+ // Special case ender dragon only give knockback if no damage is cancelled
|
||||
+ // Thinks to note:
|
||||
+ // - Setting a velocity to a ComplexEntityPart is ignored (and therefore not needed)
|
||||
+ // - Damaging ComplexEntityPart while forward the damage to EntityEnderDragon
|
||||
+ // - Damaging EntityEnderDragon does nothing
|
||||
+ // - EntityEnderDragon hitbock always covers the other parts and is therefore always present
|
||||
+ if (entity instanceof EnderDragonPart) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ entity.lastDamageCancelled = false;
|
||||
+
|
||||
+ if (entity instanceof EnderDragon) {
|
||||
+ for (EnderDragonPart entityComplexPart : ((EnderDragon) entity).subEntities) {
|
||||
+ // Calculate damage separately for each EntityComplexPart
|
||||
+ if (list.contains(entityComplexPart)) {
|
||||
+ entityComplexPart.hurtServer(this.level, this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, f2));
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
+ entity.hurtServer(this.level, this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, f2));
|
||||
+ }
|
||||
+
|
||||
+ if (entity.lastDamageCancelled) { // SPIGOT-5339, SPIGOT-6252, SPIGOT-6777: Skip entity if damage event was cancelled
|
||||
+ continue;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
|
||||
double d5 = (1.0D - d0) * (double) f2 * (double) f1;
|
||||
@@ -204,7 +257,7 @@
|
||||
if (entity instanceof LivingEntity) {
|
||||
LivingEntity entityliving = (LivingEntity) entity;
|
||||
|
||||
- d6 = d5 * (1.0D - entityliving.getAttributeValue(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE));
|
||||
+ d6 = entity instanceof Player && this.level.paperConfig().environment.disableExplosionKnockback ? 0 : d5 * (1.0D - entityliving.getAttributeValue(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE)); // Paper
|
||||
} else {
|
||||
d6 = d5;
|
||||
}
|
||||
@@ -214,11 +267,19 @@
|
||||
d3 *= d6;
|
||||
Vec3 vec3d = new Vec3(d1, d2, d3);
|
||||
|
||||
+ // CraftBukkit start - Call EntityKnockbackEvent
|
||||
+ if (entity instanceof LivingEntity) {
|
||||
+ // Paper start - knockback events
|
||||
+ io.papermc.paper.event.entity.EntityKnockbackEvent event = CraftEventFactory.callEntityKnockbackEvent((org.bukkit.craftbukkit.entity.CraftLivingEntity) entity.getBukkitEntity(), this.source, this.damageSource.getEntity() != null ? this.damageSource.getEntity() : this.source, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.EXPLOSION, d6, vec3d);
|
||||
+ vec3d = event.isCancelled() ? Vec3.ZERO : org.bukkit.craftbukkit.util.CraftVector.toNMS(event.getKnockback());
|
||||
+ // Paper end - knockback events
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
entity.push(vec3d);
|
||||
if (entity instanceof Player) {
|
||||
Player entityhuman = (Player) entity;
|
||||
|
||||
- if (!entityhuman.isSpectator() && (!entityhuman.isCreative() || !entityhuman.getAbilities().flying)) {
|
||||
+ if (!entityhuman.isSpectator() && (!entityhuman.isCreative() || !entityhuman.getAbilities().flying) && !level.paperConfig().environment.disableExplosionKnockback) { // Paper - Option to disable explosion knockback
|
||||
this.hitPlayers.put(entityhuman, vec3d);
|
||||
}
|
||||
}
|
||||
@@ -235,10 +296,62 @@
|
||||
List<ServerExplosion.StackCollector> list1 = new ArrayList();
|
||||
|
||||
Util.shuffle(positions, this.level.random);
|
||||
+ // CraftBukkit start
|
||||
+ org.bukkit.World bworld = this.level.getWorld();
|
||||
+ Location location = CraftLocation.toBukkit(this.center, bworld);
|
||||
+
|
||||
+ List<org.bukkit.block.Block> blockList = new ObjectArrayList<>();
|
||||
+ for (int i1 = positions.size() - 1; i1 >= 0; i1--) {
|
||||
+ BlockPos cpos = positions.get(i1);
|
||||
+ org.bukkit.block.Block bblock = bworld.getBlockAt(cpos.getX(), cpos.getY(), cpos.getZ());
|
||||
+ if (!bblock.getType().isAir()) {
|
||||
+ blockList.add(bblock);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ List<org.bukkit.block.Block> bukkitBlocks;
|
||||
+
|
||||
+ if (this.source != null) {
|
||||
+ EntityExplodeEvent event = CraftEventFactory.callEntityExplodeEvent(this.source, blockList, this.yield, this.getBlockInteraction());
|
||||
+ this.wasCanceled = event.isCancelled();
|
||||
+ bukkitBlocks = event.blockList();
|
||||
+ this.yield = event.getYield();
|
||||
+ } else {
|
||||
+ org.bukkit.block.Block block = location.getBlock();
|
||||
+ org.bukkit.block.BlockState blockState = (this.damageSource.getDirectBlockState() != null) ? this.damageSource.getDirectBlockState() : block.getState();
|
||||
+ BlockExplodeEvent event = CraftEventFactory.callBlockExplodeEvent(block, blockState, blockList, this.yield, this.getBlockInteraction());
|
||||
+ this.wasCanceled = event.isCancelled();
|
||||
+ bukkitBlocks = event.blockList();
|
||||
+ this.yield = event.getYield();
|
||||
+ }
|
||||
+
|
||||
+ positions.clear();
|
||||
+
|
||||
+ for (org.bukkit.block.Block bblock : bukkitBlocks) {
|
||||
+ BlockPos coords = new BlockPos(bblock.getX(), bblock.getY(), bblock.getZ());
|
||||
+ positions.add(coords);
|
||||
+ }
|
||||
+
|
||||
+ if (this.wasCanceled) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
Iterator iterator = positions.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
BlockPos blockposition = (BlockPos) iterator.next();
|
||||
+ // CraftBukkit start - TNTPrimeEvent
|
||||
+ BlockState iblockdata = this.level.getBlockState(blockposition);
|
||||
+ Block block = iblockdata.getBlock();
|
||||
+ if (block instanceof net.minecraft.world.level.block.TntBlock) {
|
||||
+ Entity sourceEntity = this.source == null ? null : this.source;
|
||||
+ BlockPos sourceBlock = sourceEntity == null ? BlockPos.containing(this.center) : null;
|
||||
+ if (!CraftEventFactory.callTNTPrimeEvent(this.level, blockposition, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.EXPLOSION, sourceEntity, sourceBlock)) {
|
||||
+ this.level.sendBlockUpdated(blockposition, Blocks.AIR.defaultBlockState(), iblockdata, 3); // Update the block on the client
|
||||
+ continue;
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
|
||||
this.level.getBlockState(blockposition).onExplosionHit(this.level, blockposition, this, (itemstack, blockposition1) -> {
|
||||
ServerExplosion.addOrAppendStack(list1, itemstack, blockposition1);
|
||||
@@ -262,13 +375,22 @@
|
||||
BlockPos blockposition = (BlockPos) iterator.next();
|
||||
|
||||
if (this.level.random.nextInt(3) == 0 && this.level.getBlockState(blockposition).isAir() && this.level.getBlockState(blockposition.below()).isSolidRender()) {
|
||||
- this.level.setBlockAndUpdate(blockposition, BaseFireBlock.getState(this.level, blockposition));
|
||||
+ // CraftBukkit start - Ignition by explosion
|
||||
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.level, blockposition, this).isCancelled()) {
|
||||
+ this.level.setBlockAndUpdate(blockposition, BaseFireBlock.getState(this.level, blockposition));
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void explode() {
|
||||
+ // CraftBukkit start
|
||||
+ if (this.radius < 0.1F) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
this.level.gameEvent(this.source, (Holder) GameEvent.EXPLODE, this.center);
|
||||
List<BlockPos> list = this.calculateExplodedPositions();
|
||||
|
||||
@@ -288,6 +410,7 @@
|
||||
}
|
||||
|
||||
private static void addOrAppendStack(List<ServerExplosion.StackCollector> droppedItemsOut, ItemStack item, BlockPos pos) {
|
||||
+ if (item.isEmpty()) return; // CraftBukkit - SPIGOT-5425
|
||||
Iterator iterator = droppedItemsOut.iterator();
|
||||
|
||||
do {
|
||||
@@ -372,4 +495,85 @@
|
||||
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // Paper start - Optimize explosions
|
||||
+ private float getBlockDensity(Vec3 vec3d, Entity entity) {
|
||||
+ if (!this.level.paperConfig().environment.optimizeExplosions) {
|
||||
+ return getSeenPercent(vec3d, entity);
|
||||
+ }
|
||||
+ CacheKey key = new CacheKey(this, entity.getBoundingBox());
|
||||
+ Float blockDensity = this.level.explosionDensityCache.get(key);
|
||||
+ if (blockDensity == null) {
|
||||
+ blockDensity = getSeenPercent(vec3d, entity);
|
||||
+ this.level.explosionDensityCache.put(key, blockDensity);
|
||||
+ }
|
||||
+
|
||||
+ return blockDensity;
|
||||
+ }
|
||||
+
|
||||
+ static class CacheKey {
|
||||
+ private final Level world;
|
||||
+ private final double posX, posY, posZ;
|
||||
+ private final double minX, minY, minZ;
|
||||
+ private final double maxX, maxY, maxZ;
|
||||
+
|
||||
+ public CacheKey(Explosion explosion, AABB aabb) {
|
||||
+ this.world = explosion.level();
|
||||
+ this.posX = explosion.center().x;
|
||||
+ this.posY = explosion.center().y;
|
||||
+ this.posZ = explosion.center().z;
|
||||
+ this.minX = aabb.minX;
|
||||
+ this.minY = aabb.minY;
|
||||
+ this.minZ = aabb.minZ;
|
||||
+ this.maxX = aabb.maxX;
|
||||
+ this.maxY = aabb.maxY;
|
||||
+ this.maxZ = aabb.maxZ;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean equals(Object o) {
|
||||
+ if (this == o) return true;
|
||||
+ if (o == null || getClass() != o.getClass()) return false;
|
||||
+
|
||||
+ CacheKey cacheKey = (CacheKey) o;
|
||||
+
|
||||
+ if (Double.compare(cacheKey.posX, posX) != 0) return false;
|
||||
+ if (Double.compare(cacheKey.posY, posY) != 0) return false;
|
||||
+ if (Double.compare(cacheKey.posZ, posZ) != 0) return false;
|
||||
+ if (Double.compare(cacheKey.minX, minX) != 0) return false;
|
||||
+ if (Double.compare(cacheKey.minY, minY) != 0) return false;
|
||||
+ if (Double.compare(cacheKey.minZ, minZ) != 0) return false;
|
||||
+ if (Double.compare(cacheKey.maxX, maxX) != 0) return false;
|
||||
+ if (Double.compare(cacheKey.maxY, maxY) != 0) return false;
|
||||
+ if (Double.compare(cacheKey.maxZ, maxZ) != 0) return false;
|
||||
+ return world.equals(cacheKey.world);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public int hashCode() {
|
||||
+ int result;
|
||||
+ long temp;
|
||||
+ result = world.hashCode();
|
||||
+ temp = Double.doubleToLongBits(posX);
|
||||
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
+ temp = Double.doubleToLongBits(posY);
|
||||
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
+ temp = Double.doubleToLongBits(posZ);
|
||||
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
+ temp = Double.doubleToLongBits(minX);
|
||||
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
+ temp = Double.doubleToLongBits(minY);
|
||||
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
+ temp = Double.doubleToLongBits(minZ);
|
||||
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
+ temp = Double.doubleToLongBits(maxX);
|
||||
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
+ temp = Double.doubleToLongBits(maxY);
|
||||
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
+ temp = Double.doubleToLongBits(maxZ);
|
||||
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
+ return result;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
--- a/net/minecraft/world/level/ServerLevelAccessor.java
|
||||
+++ b/net/minecraft/world/level/ServerLevelAccessor.java
|
||||
@@ -8,6 +8,17 @@
|
||||
ServerLevel getLevel();
|
||||
|
||||
default void addFreshEntityWithPassengers(Entity entity) {
|
||||
- entity.getSelfAndPassengers().forEach(this::addFreshEntity);
|
||||
+ // CraftBukkit start
|
||||
+ this.addFreshEntityWithPassengers(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT);
|
||||
}
|
||||
+
|
||||
+ default void addFreshEntityWithPassengers(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) {
|
||||
+ entity.getSelfAndPassengers().forEach((e) -> this.addFreshEntity(e, reason));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ default ServerLevel getMinecraftWorld() {
|
||||
+ return this.getLevel();
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
--- a/net/minecraft/world/level/StructureManager.java
|
||||
+++ b/net/minecraft/world/level/StructureManager.java
|
||||
@@ -48,7 +48,12 @@
|
||||
}
|
||||
|
||||
public List<StructureStart> startsForStructure(ChunkPos pos, Predicate<Structure> predicate) {
|
||||
- Map<Structure, LongSet> map = this.level.getChunk(pos.x, pos.z, ChunkStatus.STRUCTURE_REFERENCES).getAllReferences();
|
||||
+ // Paper start - Fix swamp hut cat generation deadlock
|
||||
+ return this.startsForStructure(pos, predicate, null);
|
||||
+ }
|
||||
+ public List<StructureStart> startsForStructure(ChunkPos pos, Predicate<Structure> predicate, @Nullable ServerLevelAccessor levelAccessor) {
|
||||
+ Map<Structure, LongSet> map = (levelAccessor == null ? this.level : levelAccessor).getChunk(pos.x, pos.z, ChunkStatus.STRUCTURE_REFERENCES).getAllReferences();
|
||||
+ // Paper end - Fix swamp hut cat generation deadlock
|
||||
Builder<StructureStart> builder = ImmutableList.builder();
|
||||
|
||||
for (Entry<Structure, LongSet> entry : map.entrySet()) {
|
||||
@@ -116,10 +121,20 @@
|
||||
}
|
||||
|
||||
public StructureStart getStructureWithPieceAt(BlockPos pos, Predicate<Holder<Structure>> predicate) {
|
||||
+ // Paper start - Fix swamp hut cat generation deadlock
|
||||
+ return this.getStructureWithPieceAt(pos, predicate, null);
|
||||
+ }
|
||||
+
|
||||
+ public StructureStart getStructureWithPieceAt(BlockPos pos, TagKey<Structure> tag, @Nullable ServerLevelAccessor levelAccessor) {
|
||||
+ return this.getStructureWithPieceAt(pos, structure -> structure.is(tag), levelAccessor);
|
||||
+ }
|
||||
+
|
||||
+ public StructureStart getStructureWithPieceAt(BlockPos pos, Predicate<Holder<Structure>> predicate, @Nullable ServerLevelAccessor levelAccessor) {
|
||||
+ // Paper end - Fix swamp hut cat generation deadlock
|
||||
Registry<Structure> registry = this.registryAccess().lookupOrThrow(Registries.STRUCTURE);
|
||||
|
||||
for (StructureStart structureStart : this.startsForStructure(
|
||||
- new ChunkPos(pos), structure -> registry.get(registry.getId(structure)).map(predicate::test).orElse(false)
|
||||
+ new ChunkPos(pos), structure -> registry.get(registry.getId(structure)).map(predicate::test).orElse(false), levelAccessor // Paper - Fix swamp hut cat generation deadlock
|
||||
)) {
|
||||
if (this.structureHasPieceAt(pos, structureStart)) {
|
||||
return structureStart;
|
||||
Reference in New Issue
Block a user