13
0
geforkt von Mirrors/Paper

net/minecraft/world/level

Dieser Commit ist enthalten in:
Shane Freeder 2024-12-14 23:42:27 +00:00
Ursprung 5deb3e9671
Commit 767215bf9b
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: A3F61EA5A085289C
23 geänderte Dateien mit 1110 neuen und 1224 gelöschten Zeilen

Datei anzeigen

@ -1,6 +1,6 @@
--- a/net/minecraft/world/level/BaseCommandBlock.java
+++ b/net/minecraft/world/level/BaseCommandBlock.java
@@ -33,6 +33,10 @@
@@ -32,6 +_,11 @@
private String command = "";
@Nullable
private Component customName;
@ -8,27 +8,28 @@
+ @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 @@
public int getSuccessCount() {
return this.successCount;
@@ -126,7 +_,7 @@
this.successCount++;
}
});
- server.getCommands().performPrefixedCommand(commandSourceStack, this.command);
+ server.getCommands().dispatchServerCommand(commandSourceStack, this.command); // CraftBukkit
} catch (Throwable var6) {
CrashReport crashReport = CrashReport.forThrowable(var6, "Executing command block");
CrashReportCategory crashReportCategory = crashReport.addCategory("Command to be executed");
@@ -162,6 +_,7 @@
@Override
public void sendSystemMessage(Component message) {
public void sendSystemMessage(Component component) {
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 @@
this.lastOutput = Component.literal("[" + TIME_FORMAT.format(new Date()) + "] ").append(component);
this.onUpdated();
}
@@ -184,7 +_,7 @@
}
public InteractionResult usedBy(Player player) {

Datei anzeigen

@ -0,0 +1,156 @@
--- a/net/minecraft/world/level/BaseSpawner.java
+++ b/net/minecraft/world/level/BaseSpawner.java
@@ -44,9 +_,11 @@
public int maxNearbyEntities = 6;
public int requiredPlayerRange = 16;
public int spawnRange = 4;
+ private int tickDelay = 0; // Paper - Configurable mob spawner tick rate
public void setEntityId(EntityType<?> type, @Nullable Level level, RandomSource random, BlockPos pos) {
this.getOrCreateNextSpawnData(level, random, pos).getEntityToSpawn().putString("id", BuiltInRegistries.ENTITY_TYPE.getKey(type).toString());
+ this.spawnPotentials = SimpleWeightedRandomList.empty(); // CraftBukkit - SPIGOT-3496, MC-92282
}
public boolean isNearPlayer(Level level, BlockPos pos) {
@@ -73,13 +_,19 @@
}
public void serverTick(ServerLevel serverLevel, 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 = serverLevel.paperConfig().tickRates.mobSpawner;
+ if (tickDelay == -1) { return; } // If disabled
+ // Paper end - Configurable mob spawner tick rate
if (this.isNearPlayer(serverLevel, pos)) {
- if (this.spawnDelay == -1) {
+ if (this.spawnDelay < -tickDelay) { // Paper - Configurable mob spawner tick rate
this.delay(serverLevel, pos);
}
if (this.spawnDelay > 0) {
- this.spawnDelay--;
+ this.spawnDelay -= tickDelay; // Paper - Configurable mob spawner tick rate
} else {
boolean flag = false;
RandomSource random = serverLevel.getRandom();
@@ -113,6 +_,21 @@
continue;
}
+ // Paper start - PreCreatureSpawnEvent
+ com.destroystokyo.paper.event.entity.PreSpawnerSpawnEvent event = new com.destroystokyo.paper.event.entity.PreSpawnerSpawnEvent(
+ io.papermc.paper.util.MCUtil.toLocation(serverLevel, d, d1, d2),
+ org.bukkit.craftbukkit.entity.CraftEntityType.minecraftToBukkit(optional.get()),
+ io.papermc.paper.util.MCUtil.toLocation(serverLevel, pos)
+ );
+ if (!event.callEvent()) {
+ flag = true;
+ if (event.shouldAbortSpawn()) {
+ break;
+ }
+ continue;
+ }
+ // Paper end - PreCreatureSpawnEvent
+
Entity entity = EntityType.loadEntityRecursive(entityToSpawn, serverLevel, EntitySpawnReason.SPAWNER, entity1 -> {
entity1.moveTo(d, d1, d2, entity1.getYRot(), entity1.getXRot());
return entity1;
@@ -133,6 +_,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(), random.nextFloat() * 360.0F, 0.0F);
if (entity instanceof Mob mob) {
if (nextSpawnData.getCustomSpawnRules().isEmpty() && !mob.checkSpawnRules(serverLevel, EntitySpawnReason.SPAWNER)
@@ -147,9 +_,22 @@
}
nextSpawnData.getEquipment().ifPresent(mob::equip);
+ // Spigot Start
+ if (mob.level().spigotConfig.nerfSpawnerMobs) {
+ mob.aware = false;
+ }
+ // Spigot End
}
- if (!serverLevel.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 (!serverLevel.tryAddFreshEntityWithPassengers(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER)) {
+ // CraftBukkit end
this.delay(serverLevel, pos);
return;
}
@@ -160,7 +_,7 @@
((Mob)entity).spawnAnim();
}
- flag = true;
+ //flag = true; // Paper - moved up above cancellable event
}
}
@@ -184,7 +_,13 @@
}
public void load(@Nullable Level level, BlockPos pos, CompoundTag tag) {
- this.spawnDelay = tag.getShort("Delay");
+ // Paper start - use larger int if set
+ if (tag.contains("Paper.Delay")) {
+ this.spawnDelay = tag.getInt("Paper.Delay");
+ } else {
+ this.spawnDelay = tag.getShort("Delay");
+ }
+ // Paper end
boolean flag = tag.contains("SpawnData", 10);
if (flag) {
SpawnData spawnData = SpawnData.CODEC
@@ -205,9 +_,15 @@
this.spawnPotentials = SimpleWeightedRandomList.single(this.nextSpawnData != null ? this.nextSpawnData : new SpawnData());
}
+ // Paper start - use ints if set
+ if (tag.contains("Paper.MinSpawnDelay", net.minecraft.nbt.Tag.TAG_ANY_NUMERIC)) {
+ this.minSpawnDelay = tag.getInt("Paper.MinSpawnDelay");
+ this.maxSpawnDelay = tag.getInt("Paper.MaxSpawnDelay");
+ this.spawnCount = tag.getShort("SpawnCount");
+ } else // Paper end
if (tag.contains("MinSpawnDelay", 99)) {
- this.minSpawnDelay = tag.getShort("MinSpawnDelay");
- this.maxSpawnDelay = tag.getShort("MaxSpawnDelay");
+ this.minSpawnDelay = tag.getInt("MinSpawnDelay"); // Paper - short -> int
+ this.maxSpawnDelay = tag.getInt("MaxSpawnDelay"); // Paper - short -> int
this.spawnCount = tag.getShort("SpawnCount");
}
@@ -224,9 +_,20 @@
}
public CompoundTag save(CompoundTag tag) {
- tag.putShort("Delay", (short)this.spawnDelay);
- tag.putShort("MinSpawnDelay", (short)this.minSpawnDelay);
- tag.putShort("MaxSpawnDelay", (short)this.maxSpawnDelay);
+ // Paper start
+ if (spawnDelay > Short.MAX_VALUE) {
+ tag.putInt("Paper.Delay", this.spawnDelay);
+ }
+ tag.putShort("Delay", (short) Math.min(Short.MAX_VALUE, this.spawnDelay));
+
+ if (minSpawnDelay > Short.MAX_VALUE || maxSpawnDelay > Short.MAX_VALUE) {
+ tag.putInt("Paper.MinSpawnDelay", this.minSpawnDelay);
+ tag.putInt("Paper.MaxSpawnDelay", this.maxSpawnDelay);
+ }
+
+ tag.putShort("MinSpawnDelay", (short) Math.min(Short.MAX_VALUE, this.minSpawnDelay));
+ tag.putShort("MaxSpawnDelay", (short) Math.min(Short.MAX_VALUE, this.maxSpawnDelay));
+ // Paper end
tag.putShort("SpawnCount", (short)this.spawnCount);
tag.putShort("MaxNearbyEntities", (short)this.maxNearbyEntities);
tag.putShort("RequiredPlayerRange", (short)this.requiredPlayerRange);

Datei anzeigen

@ -0,0 +1,76 @@
--- a/net/minecraft/world/level/BlockGetter.java
+++ b/net/minecraft/world/level/BlockGetter.java
@@ -11,6 +_,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;
@@ -33,6 +_,16 @@
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) {
@@ -66,10 +_,25 @@
);
}
- default BlockHitResult clip(ClipContext context) {
- return traverseBlocks(context.getFrom(), context.getTo(), context, (traverseContext, traversePos) -> {
- BlockState blockState = this.getBlockState(traversePos);
- FluidState fluidState = this.getFluidState(traversePos);
+ // 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 traverseContext, BlockPos traversePos, 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 blockState = this.getBlockStateIfLoaded(traversePos);
+ if (blockState == null) {
+ // copied the last function parameter (listed below)
+ Vec3 vec3d = traverseContext.getFrom().subtract(traverseContext.getTo());
+
+ return BlockHitResult.miss(traverseContext.getTo(), Direction.getApproximateNearest(vec3d.x, vec3d.y, vec3d.z), BlockPos.containing(raytrace1.getTo()));
+ }
+ // Paper end - Prevent raytrace from loading chunks
+ if (blockState.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 fluidState = blockState.getFluidState(); // Paper - Perf: don't need to go to world state again
Vec3 from = traverseContext.getFrom();
Vec3 to = traverseContext.getTo();
VoxelShape blockShape = traverseContext.getBlockShape(blockState, this, traversePos);
@@ -79,6 +_,18 @@
double d = blockHitResult == null ? Double.MAX_VALUE : traverseContext.getFrom().distanceToSqr(blockHitResult.getLocation());
double d1 = blockHitResult1 == null ? Double.MAX_VALUE : traverseContext.getFrom().distanceToSqr(blockHitResult1.getLocation());
return d <= d1 ? blockHitResult : blockHitResult1;
+ }
+ // 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
}, failContext -> {
Vec3 vec3 = failContext.getFrom().subtract(failContext.getTo());
return BlockHitResult.miss(failContext.getTo(), Direction.getApproximateNearest(vec3.x, vec3.y, vec3.z), BlockPos.containing(failContext.getTo()));

Datei anzeigen

@ -1,6 +1,6 @@
--- a/net/minecraft/world/level/ChunkPos.java
+++ b/net/minecraft/world/level/ChunkPos.java
@@ -46,6 +46,7 @@
@@ -46,6 +_,7 @@
public static final int REGION_MAX_INDEX = 31;
public final int x;
public final int z;
@ -8,10 +8,10 @@
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) {
@@ -53,16 +_,19 @@
public ChunkPos(int x, int y) {
this.x = x;
this.z = z;
this.z = y;
+ this.longKey = asLong(this.x, this.z); // Paper
}
@ -21,19 +21,10 @@
+ this.longKey = asLong(this.x, this.z); // Paper
}
public ChunkPos(long pos) {
this.x = (int)pos;
this.z = (int)(pos >> 32);
public ChunkPos(long packedPos) {
this.x = (int)packedPos;
this.z = (int)(packedPos >> 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) {
public static ChunkPos minFromRegion(int chunkX, int chunkZ) {

Datei anzeigen

@ -0,0 +1,11 @@
--- a/net/minecraft/world/level/ClipContext.java
+++ b/net/minecraft/world/level/ClipContext.java
@@ -21,7 +_,7 @@
private final CollisionContext collisionContext;
public ClipContext(Vec3 from, Vec3 to, ClipContext.Block block, ClipContext.Fluid fluid, Entity entity) {
- this(from, to, block, fluid, CollisionContext.of(entity));
+ this(from, to, block, fluid, (entity == null) ? CollisionContext.empty() : CollisionContext.of(entity)); // CraftBukkit
}
public ClipContext(Vec3 from, Vec3 to, ClipContext.Block block, ClipContext.Fluid fluid, CollisionContext collisionContext) {

Datei anzeigen

@ -1,11 +1,11 @@
--- a/net/minecraft/world/level/EmptyBlockGetter.java
+++ b/net/minecraft/world/level/EmptyBlockGetter.java
@@ -17,7 +17,19 @@
@@ -17,6 +_,18 @@
return null;
}
+ // Paper start - If loaded util
@Override
+ @Override
+ public final FluidState getFluidIfLoaded(BlockPos blockposition) {
+ return Fluids.EMPTY.defaultFluidState();
+ }
@ -16,7 +16,6 @@
+ }
+ // Paper end
+
+ @Override
@Override
public BlockState getBlockState(BlockPos pos) {
return Blocks.AIR.defaultBlockState();
}

Datei anzeigen

@ -1,6 +1,6 @@
--- a/net/minecraft/world/level/EntityGetter.java
+++ b/net/minecraft/world/level/EntityGetter.java
@@ -71,6 +71,11 @@
@@ -71,6 +_,12 @@
}
}
@ -9,10 +9,11 @@
+ 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) {
default Player getNearestPlayer(double x, double y, double z, double distance, @Nullable Predicate<Entity> predicate) {
double d = -1.0;
@@ -89,6 +94,28 @@
@@ -89,6 +_,28 @@
return player;
}
@ -39,10 +40,10 @@
+ // 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);
default Player getNearestPlayer(Entity entity, double distance) {
return this.getNearestPlayer(entity.getX(), entity.getY(), entity.getZ(), distance, false);
@@ -100,6 +_,20 @@
return this.getNearestPlayer(x, y, z, distance, predicate);
}
+ // Paper start - Affects Spawning API
@ -59,10 +60,10 @@
+ }
+ // Paper end - Affects Spawning API
+
default boolean hasNearbyAlivePlayer(double x, double y, double z, double range) {
default boolean hasNearbyAlivePlayer(double x, double y, double z, double distance) {
for (Player player : this.players()) {
if (EntitySelector.NO_SPECTATORS.test(player) && EntitySelector.LIVING_ENTITY_STILL_ALIVE.test(player)) {
@@ -124,4 +165,11 @@
@@ -124,4 +_,11 @@
return null;
}

Datei anzeigen

@ -0,0 +1,298 @@
--- a/net/minecraft/world/level/GameRules.java
+++ b/net/minecraft/world/level/GameRules.java
@@ -32,6 +_,14 @@
import org.slf4j.Logger;
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(entry -> entry.id));
@@ -81,10 +_,10 @@
"sendCommandFeedback", GameRules.Category.CHAT, GameRules.BooleanValue.create(true)
);
public static final GameRules.Key<GameRules.BooleanValue> RULE_REDUCEDDEBUGINFO = register(
- "reducedDebugInfo", GameRules.Category.MISC, GameRules.BooleanValue.create(false, (server, value) -> {
+ "reducedDebugInfo", GameRules.Category.MISC, GameRules.BooleanValue.create(false, (level, value) -> { // Paper - rename param to match changes
byte b = (byte)(value.get() ? 22 : 23);
- for (ServerPlayer serverPlayer : server.getPlayerList().getPlayers()) {
+ for (ServerPlayer serverPlayer : level.players()) {
serverPlayer.connection.send(new ClientboundEntityEventPacket(serverPlayer, b));
}
})
@@ -108,8 +_,8 @@
"doWeatherCycle", GameRules.Category.UPDATES, GameRules.BooleanValue.create(true)
);
public static final GameRules.Key<GameRules.BooleanValue> RULE_LIMITED_CRAFTING = register(
- "doLimitedCrafting", GameRules.Category.PLAYER, GameRules.BooleanValue.create(false, (server, value) -> {
- for (ServerPlayer serverPlayer : server.getPlayerList().getPlayers()) {
+ "doLimitedCrafting", GameRules.Category.PLAYER, GameRules.BooleanValue.create(false, (level, value) -> { // Paper - rename param to match changes
+ for (ServerPlayer serverPlayer : level.players()) {
serverPlayer.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.LIMITED_CRAFTING, value.get() ? 1.0F : 0.0F));
}
})
@@ -133,8 +_,8 @@
"doInsomnia", GameRules.Category.SPAWNING, GameRules.BooleanValue.create(true)
);
public static final GameRules.Key<GameRules.BooleanValue> RULE_DO_IMMEDIATE_RESPAWN = register(
- "doImmediateRespawn", GameRules.Category.PLAYER, GameRules.BooleanValue.create(false, (server, value) -> {
- for (ServerPlayer serverPlayer : server.getPlayerList().getPlayers()) {
+ "doImmediateRespawn", GameRules.Category.PLAYER, GameRules.BooleanValue.create(false, (level, value) -> { // Paper - rename param to match changes
+ for (ServerPlayer serverPlayer : level.players()) {
serverPlayer.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.IMMEDIATE_RESPAWN, value.get() ? 1.0F : 0.0F));
}
})
@@ -205,16 +_,17 @@
public static final GameRules.Key<GameRules.IntegerValue> RULE_MINECART_MAX_SPEED = register(
"minecartMaxSpeed",
GameRules.Category.MISC,
- GameRules.IntegerValue.create(8, 1, 1000, FeatureFlagSet.of(FeatureFlags.MINECART_IMPROVEMENTS), (server, value) -> {})
+ GameRules.IntegerValue.create(8, 1, limit(1000, Integer.MAX_VALUE), FeatureFlagSet.of(FeatureFlags.MINECART_IMPROVEMENTS), (server, value) -> {}) // Paper - allow disabling gamerule limits
);
public static final GameRules.Key<GameRules.IntegerValue> RULE_SPAWN_CHUNK_RADIUS = register(
- "spawnChunkRadius", GameRules.Category.MISC, GameRules.IntegerValue.create(2, 0, 32, FeatureFlagSet.of(), (server, value) -> {
- ServerLevel serverLevel = server.overworld();
+ "spawnChunkRadius", GameRules.Category.MISC, GameRules.IntegerValue.create(2, 0, limit(32, Integer.MAX_VALUE), FeatureFlagSet.of(), (level, value) -> { // Paper - allow disabling gamerule limits - also, rename param
+ ServerLevel serverLevel = level; // CraftBukkit - per-world
serverLevel.setDefaultSpawnPos(serverLevel.getSharedSpawnPos(), serverLevel.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> key = new GameRules.Key<>(name, category);
@@ -242,10 +_,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 value = (T)this.rules.get(key);
+ T value = key == null ? null : (T) this.gameruleArray[key.gameRuleIndex]; // Paper - Perf: Use array for gamerule storage
if (value == null) {
throw new IllegalArgumentException("Tried to access invalid game rule");
} else {
@@ -286,13 +_,13 @@
}
}
- public void assignFrom(GameRules rules, @Nullable MinecraftServer server) {
- rules.rules.keySet().forEach(key -> this.assignCap((GameRules.Key<?>)key, rules, server));
+ public void assignFrom(GameRules rules, @Nullable ServerLevel level) { // CraftBukkit - per-world
+ rules.rules.keySet().forEach(key -> this.assignCap((GameRules.Key<?>)key, rules, level)); // CraftBukkit - per-world
}
- private <T extends GameRules.Value<T>> void assignCap(GameRules.Key<T> key, GameRules rules, @Nullable MinecraftServer server) {
+ private <T extends GameRules.Value<T>> void assignCap(GameRules.Key<T> key, GameRules rules, @Nullable ServerLevel level) { // CraftBukkit - per-world
T rule = rules.getRule(key);
- this.<T>getRule(key).setFrom(rule, server);
+ this.<T>getRule(key).setFrom(rule, level); // CraftBukkit - per-world
}
public boolean getBoolean(GameRules.Key<GameRules.BooleanValue> key) {
@@ -306,7 +_,7 @@
public static class BooleanValue extends GameRules.Value<GameRules.BooleanValue> {
private boolean value;
- static GameRules.Type<GameRules.BooleanValue> create(boolean defaultValue, BiConsumer<MinecraftServer, GameRules.BooleanValue> changeListener) {
+ static GameRules.Type<GameRules.BooleanValue> create(boolean defaultValue, BiConsumer<ServerLevel, GameRules.BooleanValue> changeListener) { // CraftBukkit - per-world
return new GameRules.Type<>(
BoolArgumentType::bool,
type -> new GameRules.BooleanValue(type, defaultValue),
@@ -326,17 +_,21 @@
}
@Override
- protected void updateFromArgument(CommandContext<CommandSourceStack> context, String paramName) {
- this.value = BoolArgumentType.getBool(context, paramName);
+ // Paper start - Add WorldGameRuleChangeEvent
+ protected void updateFromArgument(CommandContext<CommandSourceStack> context, String paramName, GameRules.Key<BooleanValue> gameRuleKey) {
+ 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, paramName)));
+ 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) {
+ public void set(boolean value, @Nullable ServerLevel level) { // CraftBukkit - per-world
this.value = value;
- this.onChanged(server);
+ this.onChanged(level); // CraftBukkit - per-world
}
@Override
@@ -345,7 +_,7 @@
}
@Override
- protected void deserialize(String value) {
+ public void deserialize(String value) { // PAIL - protected->public
this.value = Boolean.parseBoolean(value);
}
@@ -365,9 +_,9 @@
}
@Override
- public void setFrom(GameRules.BooleanValue value, @Nullable MinecraftServer server) {
+ public void setFrom(GameRules.BooleanValue value, @Nullable ServerLevel level) { // CraftBukkit - per-world
this.value = value.value;
- this.onChanged(server);
+ this.onChanged(level); // CraftBukkit - per-world
}
}
@@ -405,7 +_,7 @@
public static class IntegerValue extends GameRules.Value<GameRules.IntegerValue> {
private int value;
- private static GameRules.Type<GameRules.IntegerValue> create(int defaultValue, BiConsumer<MinecraftServer, GameRules.IntegerValue> changeListener) {
+ private static GameRules.Type<GameRules.IntegerValue> create(int defaultValue, BiConsumer<ServerLevel, GameRules.IntegerValue> changeListener) { // CraftBukkit - per-world
return new GameRules.Type<>(
IntegerArgumentType::integer,
type -> new GameRules.IntegerValue(type, defaultValue),
@@ -416,7 +_,7 @@
}
static GameRules.Type<GameRules.IntegerValue> create(
- int defaultValue, int min, int max, FeatureFlagSet requiredFeatures, BiConsumer<MinecraftServer, GameRules.IntegerValue> changeListener
+ int defaultValue, int min, int max, FeatureFlagSet requiredFeatures, BiConsumer<ServerLevel, GameRules.IntegerValue> changeListener // CraftBukkit - per-world
) {
return new GameRules.Type<>(
() -> IntegerArgumentType.integer(min, max),
@@ -437,17 +_,21 @@
}
@Override
- protected void updateFromArgument(CommandContext<CommandSourceStack> context, String paramName) {
- this.value = IntegerArgumentType.getInteger(context, paramName);
+ // Paper start - Add WorldGameRuleChangeEvent
+ protected void updateFromArgument(CommandContext<CommandSourceStack> context, String paramName, GameRules.Key<IntegerValue> gameRuleKey) {
+ 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, paramName)));
+ 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) {
+ public void set(int value, @Nullable ServerLevel level) { // CraftBukkit - per-world
this.value = value;
- this.onChanged(server);
+ this.onChanged(level) ;// CraftBukkit - per-world
}
@Override
@@ -456,7 +_,7 @@
}
@Override
- protected void deserialize(String value) {
+ public void deserialize(String value) { // PAIL - protected->public
this.value = safeParse(value);
}
@@ -498,13 +_,17 @@
}
@Override
- public void setFrom(GameRules.IntegerValue value, @Nullable MinecraftServer server) {
+ public void setFrom(GameRules.IntegerValue value, @Nullable ServerLevel level) { // CraftBukkit - per-world
this.value = value.value;
- this.onChanged(server);
+ this.onChanged(level); // CraftBukkit - per-world
}
}
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;
@@ -544,14 +_,14 @@
public static class Type<T extends GameRules.Value<T>> {
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<?>> argument,
Function<GameRules.Type<T>, T> constructor,
- BiConsumer<MinecraftServer, T> callback,
+ BiConsumer<ServerLevel, T> callback, // CraftBukkit - per-world
GameRules.VisitorCaller<T> visitorCaller,
FeatureFlagSet requiredFeature
) {
@@ -586,20 +_,20 @@
this.type = type;
}
- protected abstract void updateFromArgument(CommandContext<CommandSourceStack> context, String paramName);
+ protected abstract void updateFromArgument(CommandContext<CommandSourceStack> context, String paramName, GameRules.Key<T> gameRuleKey); // Paper - Add WorldGameRuleChangeEvent
- public void setFromArgument(CommandContext<CommandSourceStack> context, String paramName) {
- this.updateFromArgument(context, paramName);
- this.onChanged(context.getSource().getServer());
+ public void setFromArgument(CommandContext<CommandSourceStack> context, String paramName, GameRules.Key<T> gameRuleKey) { // Paper - Add WorldGameRuleChangeEvent
+ this.updateFromArgument(context, paramName, gameRuleKey); // Paper - Add WorldGameRuleChangeEvent
+ this.onChanged(context.getSource().getLevel());
}
- public void onChanged(@Nullable MinecraftServer server) {
- if (server != null) {
- this.type.callback.accept(server, this.getSelf());
+ public void onChanged(@Nullable ServerLevel level) { // CraftBukkit - per-world
+ if (level != null) { // CraftBukkit - per-world
+ this.type.callback.accept(level, this.getSelf()); // CraftBukkit - per-world
}
}
- protected abstract void deserialize(String value);
+ public abstract void deserialize(String value); // PAIL - private->public
public abstract String serialize();
@@ -614,7 +_,7 @@
protected abstract T copy();
- public abstract void setFrom(T value, @Nullable MinecraftServer server);
+ public abstract void setFrom(T value, @Nullable ServerLevel level); // CraftBukkit - per-world
}
interface VisitorCaller<T extends GameRules.Value<T>> {

Datei anzeigen

@ -1,6 +1,6 @@
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -25,8 +25,10 @@
@@ -24,8 +_,10 @@
import net.minecraft.network.protocol.Packet;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
@ -11,7 +11,7 @@
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
@@ -43,6 +45,7 @@
@@ -42,6 +_,7 @@
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.boss.EnderDragonPart;
import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
@ -19,22 +19,7 @@
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 @@
@@ -79,6 +_,27 @@
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.scores.Scoreboard;
@ -46,6 +31,8 @@
+import net.minecraft.network.protocol.game.ClientboundSetBorderSizePacket;
+import net.minecraft.network.protocol.game.ClientboundSetBorderWarningDelayPacket;
+import net.minecraft.network.protocol.game.ClientboundSetBorderWarningDistancePacket;
+import net.minecraft.world.level.border.BorderChangeListener;
+import net.minecraft.world.level.dimension.LevelStem;
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.CraftServer;
+import org.bukkit.craftbukkit.CraftWorld;
@ -58,9 +45,9 @@
+// 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 ResourceKey<Level> OVERWORLD = ResourceKey.create(Registries.DIMENSION, ResourceLocation.withDefaultNamespace("overworld"));
@@ -91,7 +_,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;
@ -69,21 +56,15 @@
protected final NeighborUpdater neighborUpdater;
private final List<TickingBlockEntity> pendingBlockEntityTickers = Lists.newArrayList();
private boolean tickingBlockEntities;
@@ -121,23 +145,91 @@
@@ -117,6 +_,61 @@
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;
@ -134,7 +115,21 @@
+
+ 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
protected Level(
WritableLevelData levelData,
ResourceKey<Level> dimension,
@@ -125,8 +_,26 @@
boolean isClientSide,
boolean isDebug,
long biomeZoomSeed,
- int maxChainedNeighborUpdates
+ int maxChainedNeighborUpdates,
+ org.bukkit.generator.ChunkGenerator gen, // CraftBukkit
+ org.bukkit.generator.BiomeProvider biomeProvider, // CraftBukkit
+ org.bukkit.World.Environment env, // CraftBukkit
+ java.util.function.Function<org.spigotmc.SpigotWorldConfig, // Spigot - create per world config
+ 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;
@ -148,42 +143,30 @@
+ }
+
+ // 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
this.levelData = levelData;
this.dimensionTypeRegistration = dimensionTypeRegistration;
final DimensionType dimensionType = dimensionTypeRegistration.value();
@@ -136,12 +_,12 @@
this.worldBorder = new WorldBorder() {
@Override
public double getCenterX() {
- return super.getCenterX() / dimensionmanager.coordinateScale();
- return super.getCenterX() / dimensionType.coordinateScale();
+ return super.getCenterX(); // CraftBukkit
}
@Override
public double getCenterZ() {
- return super.getCenterZ() / dimensionmanager.coordinateScale();
- return super.getCenterZ() / dimensionType.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);
@@ -154,7 +_,86 @@
this.neighborUpdater = new CollectingNeighborUpdater(this, maxChainedNeighborUpdates);
this.registryAccess = registryAccess;
this.damageSources = new DamageSources(registryAccess);
- }
+
+ // CraftBukkit start
+ this.getWorldBorder().world = (ServerLevel) this;
+ // From PlayerList.setPlayerFileData
@ -222,8 +205,8 @@
+ // 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,
@ -263,10 +246,10 @@
+ return true;
+ }
+ // Paper end - Cancel hit for vanished players
@Override
public boolean isClientSide() {
return this.isClientSide;
@@ -163,6 +332,13 @@
@@ -167,6 +_,13 @@
return null;
}
@ -278,9 +261,9 @@
+ // Paper end
+
public boolean isInWorldBounds(BlockPos pos) {
return !this.isOutsideBuildHeight(pos) && Level.isInWorldBoundsHorizontal(pos);
return !this.isOutsideBuildHeight(pos) && isInWorldBoundsHorizontal(pos);
}
@@ -172,25 +348,87 @@
@@ -176,25 +_,88 @@
}
private static boolean isInWorldBoundsHorizontal(BlockPos pos) {
@ -299,7 +282,7 @@
@Override
- public LevelChunk getChunk(int chunkX, int chunkZ) {
- return (LevelChunk) this.getChunk(chunkX, chunkZ, ChunkStatus.FULL);
- 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();
@ -309,11 +292,11 @@
+ }
+ 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
+ @Nullable
+ @Override
+ public final ChunkAccess getChunkIfLoadedImmediately(int x, int z) {
+ return ((ServerLevel)this).chunkSource.getChunkAtIfLoadedImmediately(x, z);
+ }
@ -364,18 +347,19 @@
+ // 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 @@
@Nullable
@Override
public ChunkAccess getChunk(int x, int z, ChunkStatus chunkStatus, boolean requireChunk) {
+ // Paper end
ChunkAccess chunk = this.getChunkSource().getChunk(x, z, chunkStatus, requireChunk);
if (chunk == null && requireChunk) {
throw new IllegalStateException("Should always be able to create a chunk!");
@@ -210,6 +_,22 @@
@Override
public boolean setBlock(BlockPos pos, BlockState state, int flags, int maxUpdateDepth) {
public boolean setBlock(BlockPos pos, BlockState state, int flags, int recursionLeft) {
+ // CraftBukkit start - tree generation
+ if (this.captureTreeGeneration) {
+ // Paper start - Protect Bedrock and End Portal/Frames from being destroyed
@ -395,12 +379,11 @@
if (this.isOutsideBuildHeight(pos)) {
return false;
} else if (!this.isClientSide && this.isDebug()) {
@@ -214,44 +468,125 @@
@@ -217,11 +_,28 @@
} else {
LevelChunk chunk = this.getChunkAt(pos);
LevelChunk chunkAt = this.getChunkAt(pos);
Block block = state.getBlock();
- BlockState iblockdata1 = chunk.setBlockState(pos, state, (flags & 64) != 0);
- BlockState blockState = chunkAt.setBlockState(pos, state, (flags & 64) != 0);
+ // CraftBukkit start - capture blockstates
+ boolean captured = false;
+ if (this.captureBlockStates && !this.capturedBlockStates.containsKey(pos)) {
@ -411,9 +394,9 @@
+ }
+ // CraftBukkit end
+
+ BlockState iblockdata1 = chunk.setBlockState(pos, state, (flags & 64) != 0, (flags & 1024) == 0); // CraftBukkit custom NO_PLACE flag
+ BlockState blockState = chunkAt.setBlockState(pos, state, (flags & 64) != 0, (flags & 1024) == 0); // CraftBukkit custom NO_PLACE flag
+
if (iblockdata1 == null) {
if (blockState == null) {
+ // CraftBukkit start - remove blockstate if failed (or the same)
+ if (this.captureBlockStates && captured) {
+ this.capturedBlockStates.remove(pos);
@ -421,69 +404,35 @@
+ // CraftBukkit end
return false;
} else {
BlockState iblockdata2 = this.getBlockState(pos);
- if (iblockdata2 == state) {
BlockState blockState1 = this.getBlockState(pos);
+ /*
+ if (iblockdata2 == iblockdata) {
if (iblockdata1 != iblockdata2) {
- this.setBlocksDirty(pos, iblockdata1, iblockdata2);
+ this.setBlocksDirty(blockposition, iblockdata1, iblockdata2);
}
if (blockState1 == state) {
if (blockState != blockState1) {
this.setBlocksDirty(pos, blockState, blockState1);
@@ -249,12 +_,76 @@
- 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);
this.onBlockStateChange(pos, blockState, blockState1);
}
+ */
+
+ // 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);
+ this.notifyAndUpdatePhysics(pos, chunkAt, blockState, state, blockState1, flags, recursionLeft);
+ } 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;
@ -520,60 +469,59 @@
+ }
+ // CraftBukkit end
+ if (!cancelledUpdates) { // Paper - Fix block place logic
+ iblockdata.updateNeighbourShapes(this, blockposition, k, j - 1);
+ iblockdata.updateIndirectNeighbourShapes(this, blockposition, k, j - 1);
+ 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 blockState, BlockState newState) {
}
public void onBlockStateChange(BlockPos pos, BlockState oldBlock, BlockState newBlock) {}
@@ -270,15 +605,33 @@
@@ -271,13 +_,31 @@
return false;
} else {
FluidState fluid = this.getFluidState(pos);
FluidState fluidState = this.getFluidState(pos);
- if (!(blockState.getBlock() instanceof BaseFireBlock)) {
- this.levelEvent(2001, pos, Block.getId(blockState));
+ // 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);
+ BlockState effectType = blockState;
+ int xp = blockState.getBlock().getExpDrop(blockState, (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);
+ com.destroystokyo.paper.event.block.BlockDestroyEvent event = new com.destroystokyo.paper.event.block.BlockDestroyEvent(org.bukkit.craftbukkit.block.CraftBlock.at(this, pos), fluidState.createLegacyBlock().createCraftBlockData(), effectType.createCraftBlockData(), xp, dropBlock);
+ if (!event.callEvent()) {
+ return false;
+ }
+ effectType = ((CraftBlockData) event.getEffectBlock()).getState();
+ playEffect = event.playEffect();
+ drop = event.willDrop();
+ dropBlock = 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
+ if (playEffect && !(blockState.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
if (dropBlock) {
BlockEntity blockEntity = blockState.hasBlockEntity() ? this.getBlockEntity(pos) : null;
- Block.dropResources(blockState, this, pos, blockEntity, entity, ItemStack.EMPTY);
+ Block.dropResources(blockState, this, pos, blockEntity, entity, ItemStack.EMPTY, false); // Paper - Properly handle xp dropping
+ blockState.getBlock().popExperience((ServerLevel) this, pos, xp, entity); // Paper - Properly handle xp dropping; custom amount
}
boolean flag1 = this.setBlock(pos, fluid.createLegacyBlock(), 3, maxUpdateDepth);
@@ -340,10 +693,18 @@
boolean flag = this.setBlock(pos, fluidState.createLegacyBlock(), 3, recursionLeft);
@@ -344,10 +_,18 @@
@Override
public BlockState getBlockState(BlockPos pos) {
@ -590,121 +538,112 @@
} 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 @@
}
@@ -454,32 +_,54 @@
this.pendingBlockEntityTickers.clear();
}
- Iterator<TickingBlockEntity> iterator = this.blockEntityTickers.iterator();
+ // Spigot start
+ // Iterator<TickingBlockEntity> iterator = this.blockEntityTickers.iterator();
boolean flag = this.tickRateManager().runsNormally();
boolean runsNormally = this.tickRateManager().runsNormally();
- while (iterator.hasNext()) {
- TickingBlockEntity tickingblockentity = (TickingBlockEntity) iterator.next();
- 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);
+ TickingBlockEntity tickingBlockEntity = (TickingBlockEntity) this.blockEntityTickers.get(this.tileTickPosition);
+ // Spigot end
if (tickingblockentity.isRemoved()) {
if (tickingBlockEntity.isRemoved()) {
- iterator.remove();
+ // Spigot start
+ tilesThisCycle--;
+ toRemove.add(tickingblockentity); // Paper - Fix MC-117075; use removeAll
+ tilesThisCycle--;
+ toRemove.add(tickingBlockEntity); // Paper - Fix MC-117075; use removeAll
+ // Spigot end
} else if (flag && this.shouldTickBlocksAt(tickingblockentity.getPos())) {
tickingblockentity.tick();
} else if (runsNormally && this.shouldTickBlocksAt(tickingBlockEntity.getPos())) {
tickingBlockEntity.tick();
}
}
+ this.blockEntityTickers.removeAll(toRemove); // Paper - Fix MC-117075
this.tickingBlockEntities = false;
gameprofilerfiller.pop();
profilerFiller.pop();
+ this.spigotConfig.currentPrimedTnt = 0; // Spigot
}
public <T extends Entity> void guardEntityTick(Consumer<T> tickConsumer, T entity) {
public <T extends Entity> void guardEntityTick(Consumer<T> consumerEntity, 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);
consumerEntity.accept(entity);
} catch (Throwable var6) {
- CrashReport crashReport = CrashReport.forThrowable(var6, "Ticking entity");
- CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being ticked");
- entity.fillCrashReportCategory(crashReportCategory);
- 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);
+ MinecraftServer.LOGGER.error(msg, var6);
+ 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;
+ 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 @@
@@ -599,6 +_,19 @@
@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) {
+ public BlockEntity getBlockEntity(BlockPos pos, 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) {
+ if (!this.capturedTileEntities.isEmpty() && (blockEntity = this.capturedTileEntities.get(pos)) != 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));
+ }
+
if (this.isOutsideBuildHeight(pos)) {
return null;
} else {
@@ -611,6 +_,12 @@
public void setBlockEntity(BlockEntity blockEntity) {
BlockPos blockposition = blockEntity.getBlockPos();
if (!this.isOutsideBuildHeight(blockposition)) {
BlockPos blockPos = blockEntity.getBlockPos();
if (!this.isOutsideBuildHeight(blockPos)) {
+ // CraftBukkit start
+ if (this.captureBlockStates) {
+ this.capturedTileEntities.put(blockposition.immutable(), blockEntity);
+ this.capturedTileEntities.put(blockPos.immutable(), blockEntity);
+ return;
+ }
+ // CraftBukkit end
this.getChunkAt(blockposition).addAndRegisterBlockEntity(blockEntity);
this.getChunkAt(blockPos).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
@@ -987,7 +_,8 @@
BLOCK("block"),
MOB("mob"),
TNT("tnt"),
- TRIGGER("trigger");
+ 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;

Datei anzeigen

@ -0,0 +1,9 @@
--- a/net/minecraft/world/level/LevelAccessor.java
+++ b/net/minecraft/world/level/LevelAccessor.java
@@ -101,4 +_,6 @@
default void gameEvent(ResourceKey<GameEvent> gameEvent, BlockPos pos, GameEvent.Context context) {
this.gameEvent(this.registryAccess().lookupOrThrow(Registries.GAME_EVENT).getOrThrow(gameEvent), pos, context);
}
+
+ net.minecraft.server.level.ServerLevel getMinecraftWorld(); // CraftBukkit
}

Datei anzeigen

@ -1,8 +1,8 @@
--- a/net/minecraft/world/level/LevelReader.java
+++ b/net/minecraft/world/level/LevelReader.java
@@ -26,6 +26,9 @@
@@ -26,6 +_,9 @@
@Nullable
ChunkAccess getChunk(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create);
ChunkAccess getChunk(int x, int z, ChunkStatus chunkStatus, boolean requireChunk);
+ @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);}

Datei anzeigen

@ -1,6 +1,6 @@
--- a/net/minecraft/world/level/LevelWriter.java
+++ b/net/minecraft/world/level/LevelWriter.java
@@ -28,4 +28,10 @@
@@ -27,4 +_,10 @@
default boolean addFreshEntity(Entity entity) {
return false;
}

Datei anzeigen

@ -0,0 +1,234 @@
--- a/net/minecraft/world/level/NaturalSpawner.java
+++ b/net/minecraft/world/level/NaturalSpawner.java
@@ -49,6 +_,13 @@
import net.minecraft.world.phys.Vec3;
import org.slf4j.Logger;
+// CraftBukkit start
+import net.minecraft.world.level.storage.LevelData;
+import org.bukkit.craftbukkit.util.CraftSpawnCategory;
+import org.bukkit.entity.SpawnCategory;
+import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
+// CraftBukkit end
+
public final class NaturalSpawner {
private static final Logger LOGGER = LogUtils.getLogger();
private static final int MIN_SPAWN_DISTANCE = 24;
@@ -72,6 +_,13 @@
if (!(entity instanceof Mob mob && (mob.isPersistenceRequired() || mob.requiresCustomPersistence()))) {
MobCategory category = entity.getType().getCategory();
if (category != 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 blockPos = entity.blockPosition();
chunkGetter.query(ChunkPos.asLong(blockPos), chunk -> {
MobSpawnSettings.MobSpawnCost mobSpawnCost = getRoughBiome(blockPos, chunk).getMobSettings().getMobSpawnCost(entity.getType());
@@ -96,17 +_,36 @@
return chunk.getNoiseBiome(QuartPos.fromBlock(pos.getX()), QuartPos.fromBlock(pos.getY()), QuartPos.fromBlock(pos.getZ())).value();
}
+ // CraftBukkit start - add server
public static List<MobCategory> getFilteredSpawningCategories(
- NaturalSpawner.SpawnState spawnState, boolean spawnFriendlies, boolean spawnEnemies, boolean spawnPassives
+ NaturalSpawner.SpawnState spawnState, boolean spawnFriendlies, boolean spawnEnemies, boolean spawnPassives, ServerLevel worldserver
) {
+ LevelData worlddata = worldserver.getLevelData(); // CraftBukkit - Other mob type spawn tick rate
+ // CraftBukkit end
List<MobCategory> list = new ArrayList<>(SPAWNING_CATEGORIES.length);
-
- for (MobCategory mobCategory : SPAWNING_CATEGORIES) {
- if ((spawnFriendlies || !mobCategory.isFriendly())
- && (spawnEnemies || mobCategory.isFriendly())
- && (spawnPassives || !mobCategory.isPersistent())
- && spawnState.canSpawnForCategoryGlobal(mobCategory)) {
- list.add(mobCategory);
+ MobCategory[] aenumcreaturetype = NaturalSpawner.SPAWNING_CATEGORIES;
+ int i = aenumcreaturetype.length;
+
+ for (int j = 0; j < i; ++j) {
+ MobCategory enumcreaturetype = SPAWNING_CATEGORIES[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 (!spawnThisTick || limit == 0) {
+ continue;
+ }
+
+ if ((spawnFriendlies || !enumcreaturetype.isFriendly())
+ && (spawnEnemies || enumcreaturetype.isFriendly())
+ && (spawnPassives || !enumcreaturetype.isPersistent())
+ && spawnState.canSpawnForCategoryGlobal(enumcreaturetype)) {
+ list.add(enumcreaturetype);
}
}
@@ -126,6 +_,16 @@
profilerFiller.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 category, ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback
) {
@@ -151,8 +_,8 @@
StructureManager structureManager = level.structureManager();
ChunkGenerator generator = level.getChunkSource().getGenerator();
int y = pos.getY();
- BlockState blockState = chunk.getBlockState(pos);
- if (!blockState.isRedstoneConductor(chunk, pos)) {
+ BlockState blockState = chunk.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn
+ if (blockState != null && !blockState.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
int i = 0;
@@ -174,7 +_,7 @@
Player nearestPlayer = level.getNearestPlayer(d, y, d1, -1.0, false);
if (nearestPlayer != null) {
double d2 = nearestPlayer.distanceToSqr(d, y, d1);
- if (isRightDistanceToPlayerAndSpawnPoint(level, chunk, mutableBlockPos, d2)) {
+ if (level.isLoadedAndInBounds(mutableBlockPos) && isRightDistanceToPlayerAndSpawnPoint(level, chunk, mutableBlockPos, d2)) { // Paper - don't load chunks for mob spawn
if (spawnerData == null) {
Optional<MobSpawnSettings.SpawnerData> randomSpawnMobAt = getRandomSpawnMobAt(
level, structureManager, generator, category, level.random, mutableBlockPos
@@ -187,8 +_,13 @@
ceil = spawnerData.minCount + level.random.nextInt(1 + spawnerData.maxCount - spawnerData.minCount);
}
- if (isValidSpawnPostitionForType(level, category, structureManager, generator, spawnerData, mutableBlockPos, d2)
- && filter.test(spawnerData.type, mutableBlockPos, chunk)) {
+ // Paper start - PreCreatureSpawnEvent
+ PreSpawnStatus doSpawning = isValidSpawnPostitionForType(level, category, structureManager, generator, spawnerData, mutableBlockPos, d2);
+ if (doSpawning == PreSpawnStatus.ABORT) {
+ return;
+ }
+ if (doSpawning == PreSpawnStatus.SUCCESS && filter.test(spawnerData.type, mutableBlockPos, chunk)) {
+ // Paper end - PreCreatureSpawnEvent
Mob mobForSpawn = getMobForSpawn(level, spawnerData.type);
if (mobForSpawn == null) {
return;
@@ -199,10 +_,15 @@
spawnGroupData = mobForSpawn.finalizeSpawn(
level, level.getCurrentDifficultyAt(mobForSpawn.blockPosition()), EntitySpawnReason.NATURAL, spawnGroupData
);
- i++;
- i3++;
- level.addFreshEntityWithPassengers(mobForSpawn);
- callback.run(mobForSpawn, 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.
+ level.addFreshEntityWithPassengers(mobForSpawn, (mobForSpawn instanceof net.minecraft.world.entity.animal.Ocelot && !((org.bukkit.entity.Ageable) mobForSpawn.getBukkitEntity()).isAdult()) ? SpawnReason.OCELOT_BABY : SpawnReason.NATURAL);
+ if (!mobForSpawn.isRemoved()) {
+ ++i;
+ ++i3;
+ callback.run(mobForSpawn, chunk);
+ }
+ // CraftBukkit end
if (i >= mobForSpawn.getMaxSpawnClusterSize()) {
return;
}
@@ -225,7 +_,15 @@
&& (Objects.equals(new ChunkPos(pos), chunk.getPos()) || level.isNaturalSpawningAllowed(pos));
}
- private static boolean isValidSpawnPostitionForType(
+ // Paper start - PreCreatureSpawnEvent
+ private enum PreSpawnStatus {
+ FAIL,
+ SUCCESS,
+ CANCELLED,
+ ABORT
+ }
+ private static PreSpawnStatus isValidSpawnPostitionForType(
+ // Paper end - PreCreatureSpawnEvent
ServerLevel level,
MobCategory category,
StructureManager structureManager,
@@ -235,16 +_,20 @@
double distance
) {
EntityType<?> entityType = data.type;
- return entityType.getCategory() != MobCategory.MISC
- && (
- entityType.canSpawnFarFromPlayer()
- || !(distance > entityType.getCategory().getDespawnDistance() * entityType.getCategory().getDespawnDistance())
- )
- && entityType.canSummon()
- && canSpawnMobAt(level, structureManager, generator, category, data, pos)
- && SpawnPlacements.isSpawnPositionOk(entityType, level, pos)
- && SpawnPlacements.checkSpawnRules(entityType, level, EntitySpawnReason.NATURAL, pos, level.random)
- && level.noCollision(entityType.getSpawnAABB(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5));
+
+ // Paper start - PreCreatureSpawnEvent
+ com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent(
+ io.papermc.paper.util.MCUtil.toLocation(level, pos),
+ org.bukkit.craftbukkit.entity.CraftEntityType.minecraftToBukkit(entityType), SpawnReason.NATURAL
+ );
+ if (!event.callEvent()) {
+ if (event.shouldAbortSpawn()) {
+ return PreSpawnStatus.ABORT;
+ }
+ return PreSpawnStatus.CANCELLED;
+ }
+ // Paper end - PreCreatureSpawnEvent
+ return entityType.getCategory() == MobCategory.MISC ? PreSpawnStatus.FAIL : (!entityType.canSpawnFarFromPlayer() && distance > (double) (entityType.getCategory().getDespawnDistance() * entityType.getCategory().getDespawnDistance()) ? PreSpawnStatus.FAIL : (entityType.canSummon() && NaturalSpawner.canSpawnMobAt(level, structureManager, generator, category, data, pos) ? (!SpawnPlacements.isSpawnPositionOk(entityType, level, pos) ? PreSpawnStatus.FAIL : (!SpawnPlacements.checkSpawnRules(entityType, level, EntitySpawnReason.NATURAL, pos, level.random) ? PreSpawnStatus.FAIL : level.noCollision(entityType.getSpawnAABB((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D)) ? PreSpawnStatus.SUCCESS : PreSpawnStatus.FAIL)) : PreSpawnStatus.FAIL)); // Paper - PreCreatureSpawnEvent
}
@Nullable
@@ -258,6 +_,7 @@
LOGGER.warn("Can't spawn entity of type: {}", BuiltInRegistries.ENTITY_TYPE.getKey(entityType));
} catch (Exception var4) {
LOGGER.warn("Failed to create mob", (Throwable)var4);
+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(var4); // Paper - ServerExceptionEvent
}
return null;
@@ -364,6 +_,7 @@
entity = spawnerData.type.create(levelAccessor.getLevel(), EntitySpawnReason.NATURAL);
} catch (Exception var27) {
LOGGER.warn("Failed to create mob", (Throwable)var27);
+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(var27); // Paper - ServerExceptionEvent
continue;
}
@@ -381,7 +_,7 @@
EntitySpawnReason.CHUNK_GENERATION,
spawnGroupData
);
- levelAccessor.addFreshEntityWithPassengers(mob);
+ levelAccessor.addFreshEntityWithPassengers(mob, SpawnReason.CHUNK_GEN); // CraftBukkit
flag = true;
}
}
@@ -501,8 +_,10 @@
return this.unmodifiableMobCategoryCounts;
}
- boolean canSpawnForCategoryGlobal(MobCategory category) {
- int i = category.getMaxInstancesPerChunk() * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
+ // CraftBukkit start
+ boolean canSpawnForCategoryGlobal(MobCategory category, int limit) {
+ int i = limit * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
+ // CraftBukkit end
return this.mobCategoryCounts.getInt(category) < i;
}

Datei anzeigen

@ -1,6 +1,6 @@
--- a/net/minecraft/world/level/PathNavigationRegion.java
+++ b/net/minecraft/world/level/PathNavigationRegion.java
@@ -8,6 +8,7 @@
@@ -8,6 +_,7 @@
import net.minecraft.core.Holder;
import net.minecraft.core.SectionPos;
import net.minecraft.core.registries.Registries;
@ -8,19 +8,19 @@
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()));
@@ -66,13 +_,37 @@
private ChunkAccess getChunk(int x, int z) {
int i = x - this.centerX;
int i1 = z - this.centerZ;
- if (i >= 0 && i < this.chunks.length && i1 >= 0 && i1 < this.chunks[i].length) {
+ if (i >= 0 && i < this.chunks.length && i1 >= 0 && i1 < this.chunks[i].length) { // Paper - if this changes, update getChunkIfLoaded below
ChunkAccess chunkAccess = this.chunks[i][i1];
return (ChunkAccess)(chunkAccess != null ? chunkAccess : new EmptyLevelChunk(this.level, new ChunkPos(x, z), this.plains.get()));
} else {
@@ -74,7 +75,31 @@
return new EmptyLevelChunk(this.level, new ChunkPos(x, z), this.plains.get());
}
}
+
+ // Paper start - if loaded util
+ private @Nullable ChunkAccess getChunkIfLoaded(int x, int z) {
+ // Based on getChunk(int, int)
@ -32,7 +32,7 @@
+ }
+ return null;
+ }
@Override
+ @Override
+ public final FluidState getFluidIfLoaded(BlockPos blockposition) {
+ ChunkAccess chunk = getChunkIfLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4);
+ return chunk == null ? null : chunk.getFluidState(blockposition);
@ -44,8 +44,6 @@
+ return chunk == null ? null : chunk.getBlockState(blockposition);
+ }
+ // Paper end
+
+ @Override
@Override
public WorldBorder getWorldBorder() {
return this.level.getWorldBorder();
}

Datei anzeigen

@ -1,23 +1,12 @@
--- 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;
@@ -33,6 +_,18 @@
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
+// CraftBukkit start
+import net.minecraft.world.entity.boss.EnderDragonPart;
+import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.state.BlockState;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
@ -26,77 +15,82 @@
+import org.bukkit.Location;
+import org.bukkit.event.block.BlockExplodeEvent;
+// CraftBukkit end
+
public class ServerExplosion implements Explosion {
@@ -50,16 +59,22 @@
private static final ExplosionDamageCalculator EXPLOSION_DAMAGE_CALCULATOR = new ExplosionDamageCalculator();
private static final int MAX_DROPS_PER_COMBINED_STACK = 16;
@@ -47,6 +_,11 @@
private final DamageSource damageSource;
private final ExplosionDamageCalculator damageCalculator;
private final Map<Player, Vec3> hitPlayers = new HashMap();
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;
public ServerExplosion(
ServerLevel level,
@@ -60,12 +_,13 @@
) {
this.level = level;
this.source = source;
- this.radius = radius;
+ this.radius = (float) Math.max(radius, 0.0); // CraftBukkit - clamp bad values
this.center = center;
this.fire = fire;
this.blockInteraction = blockInteraction;
this.damageSource = damageSource == null ? level.damageSources().explosion(this) : damageSource;
this.damageCalculator = damageCalculator == null ? this.makeDamageCalculator(source) : damageCalculator;
+ 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 @@
@@ -139,7 +_,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)) {
BlockPos blockPos = BlockPos.containing(d3, d4, d5);
BlockState blockState = this.level.getBlockState(blockPos);
- FluidState fluidState = this.level.getFluidState(blockPos);
+ if (!blockState.isDestroyable()) continue; // Paper - Protect Bedrock and End Portal/Frames from being destroyed
+ FluidState fluidState = blockState.getFluidState(); // Paper - Perf: Optimize call to getFluid for explosions
if (!this.level.isInWorldBounds(blockPos)) {
break;
@@ -149,6 +165,15 @@
}
@@ -152,6 +_,15 @@
if (f > 0.0F && this.damageCalculator.shouldBlockExplode(this, this.level, blockposition, iblockdata, f)) {
set.add(blockposition);
if (f > 0.0F && this.damageCalculator.shouldBlockExplode(this, this.level, blockPos, blockState, f)) {
set.add(blockPos);
+ // 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 (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowHeadlessPistons && blockState.getBlock() == Blocks.MOVING_PISTON) {
+ net.minecraft.world.level.block.entity.BlockEntity extension = this.level.getBlockEntity(blockPos);
+ 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()));
+ net.minecraft.core.Direction direction = blockState.getValue(net.minecraft.world.level.block.piston.PistonHeadBlock.FACING);
+ set.add(blockPos.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));
d3 += d * 0.3F;
@@ -174,8 +_,8 @@
int floor3 = Mth.floor(this.center.y + f + 1.0);
int floor4 = Mth.floor(this.center.z - f - 1.0);
int floor5 = Mth.floor(this.center.z + f + 1.0);
-
- for (Entity entity : this.level.getEntities(this.source, new AABB(floor, floor2, floor4, floor1, floor3, floor5))) {
+ List <Entity> list = this.level.getEntities(excludeSourceFromDamage ? this.source : null, new AABB(floor, floor2, floor4, floor1, floor3, floor5), (com.google.common.base.Predicate<Entity>) entity -> entity.isAlive() && !entity.isSpectator()); // Paper - Fix lag from explosions processing dead entities, Allow explosions to damage source
+ for (Entity entity : list) { // Paper - used in loop
if (!entity.ignoreExplosion(this)) {
double d = Math.sqrt(entity.distanceToSqr(this.center)) / f;
if (d <= 1.0) {
@@ -189,15 +_,43 @@
d3 /= squareRoot;
boolean shouldDamageEntity = this.damageCalculator.shouldDamageEntity(this, entity);
float knockbackMultiplier = this.damageCalculator.getKnockbackMultiplier(entity);
- float f1 = !shouldDamageEntity && knockbackMultiplier == 0.0F ? 0.0F : getSeenPercent(this.center, entity);
+ float f1 = !shouldDamageEntity && knockbackMultiplier == 0.0F ? 0.0F : this.getBlockDensity(this.center, entity); // Paper - Optimize explosions
if (shouldDamageEntity) {
- entity.hurtServer(this.level, this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, f1));
+ // CraftBukkit start
+
+ // Special case ender dragon only give knockback if no damage is cancelled
@ -115,11 +109,11 @@
+ 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));
+ entityComplexPart.hurtServer(this.level, this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, f1));
+ }
+ }
+ } else {
+ entity.hurtServer(this.level, this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, f2));
+ entity.hurtServer(this.level, this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, f1));
+ }
+
+ if (entity.lastDamageCancelled) { // SPIGOT-5339, SPIGOT-6252, SPIGOT-6777: Skip entity if damage event was cancelled
@ -128,48 +122,45 @@
+ // 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
double d4 = (1.0 - d) * f1 * knockbackMultiplier;
double d5;
if (entity instanceof LivingEntity livingEntity) {
- d5 = d4 * (1.0 - livingEntity.getAttributeValue(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE));
+ d5 = entity instanceof Player && this.level.paperConfig().environment.disableExplosionKnockback ? 0 : d4 * (1.0 - livingEntity.getAttributeValue(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE)); // Paper
} else {
d6 = d5;
d5 = d4;
}
@@ -214,11 +267,19 @@
d3 *= d6;
Vec3 vec3d = new Vec3(d1, d2, d3);
@@ -206,10 +_,18 @@
d2 *= d5;
d3 *= d5;
Vec3 vec3 = 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
+ // 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, d5, vec3);
+ vec3 = event.isCancelled() ? Vec3.ZERO : org.bukkit.craftbukkit.util.CraftVector.toNMS(event.getKnockback());
+ // Paper end - knockback events
+ }
+ // CraftBukkit end
entity.push(vec3d);
entity.push(vec3);
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);
Player player = (Player)entity;
- if (!player.isSpectator() && (!player.isCreative() || !player.getAbilities().flying)) {
+ if (!player.isSpectator() && (!player.isCreative() || !player.getAbilities().flying) && !level.paperConfig().environment.disableExplosionKnockback) { // Paper - Option to disable explosion knockback
this.hitPlayers.put(player, vec3);
}
}
@@ -235,10 +296,62 @@
List<ServerExplosion.StackCollector> list1 = new ArrayList();
@@ -225,7 +_,61 @@
List<ServerExplosion.StackCollector> list = new ArrayList<>();
Util.shuffle(blocks, this.level.random);
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);
+ for (int i1 = blocks.size() - 1; i1 >= 0; i1--) {
+ BlockPos cpos = blocks.get(i1);
+ org.bukkit.block.Block bblock = bworld.getBlockAt(cpos.getX(), cpos.getY(), cpos.getZ());
+ if (!bblock.getType().isAir()) {
+ blockList.add(bblock);
@ -192,49 +183,47 @@
+ this.yield = event.getYield();
+ }
+
+ positions.clear();
+ blocks.clear();
+
+ for (org.bukkit.block.Block bblock : bukkitBlocks) {
+ BlockPos coords = new BlockPos(bblock.getX(), bblock.getY(), bblock.getZ());
+ positions.add(coords);
+ blocks.add(coords);
+ }
+
+ if (this.wasCanceled) {
+ return;
+ }
+ // CraftBukkit end
Iterator iterator = positions.iterator();
while (iterator.hasNext()) {
BlockPos blockposition = (BlockPos) iterator.next();
+
for (BlockPos blockPos : blocks) {
+ // CraftBukkit start - TNTPrimeEvent
+ BlockState iblockdata = this.level.getBlockState(blockposition);
+ BlockState iblockdata = this.level.getBlockState(blockPos);
+ 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
+ if (!CraftEventFactory.callTNTPrimeEvent(this.level, blockPos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.EXPLOSION, sourceEntity, sourceBlock)) {
+ this.level.sendBlockUpdated(blockPos, 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));
+
this.level
.getBlockState(blockPos)
.onExplosionHit(this.level, blockPos, this, (itemStack, blockPos1) -> addOrAppendStack(list, itemStack, blockPos1));
@@ -239,12 +_,21 @@
private void createFire(List<BlockPos> blocks) {
for (BlockPos blockPos : blocks) {
if (this.level.random.nextInt(3) == 0 && this.level.getBlockState(blockPos).isAir() && this.level.getBlockState(blockPos.below()).isSolidRender()) {
- this.level.setBlockAndUpdate(blockPos, BaseFireBlock.getState(this.level, blockPos));
+ // 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));
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.level, blockPos, this).isCancelled()) {
+ this.level.setBlockAndUpdate(blockPos, BaseFireBlock.getState(this.level, blockPos));
+ }
+ // CraftBukkit end
}
}
}
public void explode() {
@ -243,22 +232,23 @@
+ return;
+ }
+ // CraftBukkit end
this.level.gameEvent(this.source, (Holder) GameEvent.EXPLODE, this.center);
this.level.gameEvent(this.source, GameEvent.EXPLODE, this.center);
List<BlockPos> list = this.calculateExplodedPositions();
@@ -288,6 +410,7 @@
this.hurtEntities();
@@ -261,6 +_,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 @@
private static void addOrAppendStack(List<ServerExplosion.StackCollector> stackCollectors, ItemStack stack, BlockPos pos) {
+ if (stack.isEmpty()) return; // CraftBukkit - SPIGOT-5425
for (ServerExplosion.StackCollector stackCollector : stackCollectors) {
stackCollector.tryMerge(stack);
if (stack.isEmpty()) {
@@ -342,4 +_,86 @@
}
}
}
+
+
+ // Paper start - Optimize explosions
+ private float getBlockDensity(Vec3 vec3d, Entity entity) {
+ if (!this.level.paperConfig().environment.optimizeExplosions) {

Datei anzeigen

@ -1,13 +1,14 @@
--- a/net/minecraft/world/level/ServerLevelAccessor.java
+++ b/net/minecraft/world/level/ServerLevelAccessor.java
@@ -8,6 +8,17 @@
@@ -7,6 +_,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));

Datei anzeigen

@ -1,20 +1,21 @@
--- a/net/minecraft/world/level/StructureManager.java
+++ b/net/minecraft/world/level/StructureManager.java
@@ -48,7 +48,12 @@
@@ -48,7 +_,13 @@
}
public List<StructureStart> startsForStructure(ChunkPos pos, Predicate<Structure> predicate) {
- Map<Structure, LongSet> map = this.level.getChunk(pos.x, pos.z, ChunkStatus.STRUCTURE_REFERENCES).getAllReferences();
public List<StructureStart> startsForStructure(ChunkPos chunkPos, Predicate<Structure> structurePredicate) {
- Map<Structure, LongSet> allReferences = this.level.getChunk(chunkPos.x, chunkPos.z, ChunkStatus.STRUCTURE_REFERENCES).getAllReferences();
+ // Paper start - Fix swamp hut cat generation deadlock
+ return this.startsForStructure(pos, predicate, null);
+ return this.startsForStructure(chunkPos, structurePredicate, 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();
+
+ public List<StructureStart> startsForStructure(ChunkPos chunkPos, Predicate<Structure> structurePredicate, @Nullable ServerLevelAccessor levelAccessor) {
+ Map<Structure, LongSet> allReferences = (levelAccessor == null ? this.level : levelAccessor).getChunk(chunkPos.x, chunkPos.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 @@
for (Entry<Structure, LongSet> entry : allReferences.entrySet()) {
@@ -118,10 +_,20 @@
}
public StructureStart getStructureWithPieceAt(BlockPos pos, Predicate<Holder<Structure>> predicate) {

Datei anzeigen

@ -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);

Datei anzeigen

@ -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) {

Datei anzeigen

@ -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;
}

Datei anzeigen

@ -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);
}
}

Datei anzeigen

@ -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
}

Datei anzeigen

@ -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) {