mirror of
https://github.com/PaperMC/Paper.git
synced 2025-01-08 19:34:09 +01:00
ac79088eb6
Suspected case would be around the technique used in .stopRiding Stack will identify any causer of this and warn instead of crashing.
1029 lines
54 KiB
Diff
1029 lines
54 KiB
Diff
--- a/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/net/minecraft/server/level/ServerLevel.java
|
|
@@ -58,7 +58,6 @@
|
|
import net.minecraft.network.protocol.game.ClientboundDamageEventPacket;
|
|
import net.minecraft.network.protocol.game.ClientboundEntityEventPacket;
|
|
import net.minecraft.network.protocol.game.ClientboundExplodePacket;
|
|
-import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
|
|
import net.minecraft.network.protocol.game.ClientboundLevelEventPacket;
|
|
import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket;
|
|
import net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket;
|
|
@@ -124,6 +123,7 @@
|
|
import net.minecraft.world.level.StructureManager;
|
|
import net.minecraft.world.level.WorldGenLevel;
|
|
import net.minecraft.world.level.biome.Biome;
|
|
+import net.minecraft.world.level.biome.BiomeSource;
|
|
import net.minecraft.world.level.block.Block;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.SnowLayerBlock;
|
|
@@ -149,7 +149,9 @@
|
|
import net.minecraft.world.level.gameevent.DynamicGameEventListener;
|
|
import net.minecraft.world.level.gameevent.GameEvent;
|
|
import net.minecraft.world.level.gameevent.GameEventDispatcher;
|
|
+import net.minecraft.world.level.levelgen.FlatLevelSource;
|
|
import net.minecraft.world.level.levelgen.Heightmap;
|
|
+import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
|
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
|
import net.minecraft.world.level.levelgen.structure.Structure;
|
|
import net.minecraft.world.level.levelgen.structure.StructureCheck;
|
|
@@ -165,7 +167,7 @@
|
|
import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
|
|
import net.minecraft.world.level.storage.DimensionDataStorage;
|
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
|
-import net.minecraft.world.level.storage.ServerLevelData;
|
|
+import net.minecraft.world.level.storage.PrimaryLevelData;
|
|
import net.minecraft.world.phys.AABB;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import net.minecraft.world.phys.shapes.BooleanOp;
|
|
@@ -173,6 +175,16 @@
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
import net.minecraft.world.ticks.LevelTicks;
|
|
import org.slf4j.Logger;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.WeatherType;
|
|
+import org.bukkit.craftbukkit.event.CraftEventFactory;
|
|
+import org.bukkit.craftbukkit.generator.CustomWorldChunkManager;
|
|
+import org.bukkit.craftbukkit.util.WorldUUID;
|
|
+import org.bukkit.event.entity.CreatureSpawnEvent;
|
|
+import org.bukkit.event.server.MapInitializeEvent;
|
|
+import org.bukkit.event.weather.LightningStrikeEvent;
|
|
+import org.bukkit.event.world.TimeSkipEvent;
|
|
+// CraftBukkit end
|
|
|
|
public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLevel {
|
|
|
|
@@ -187,7 +199,7 @@
|
|
final List<ServerPlayer> players = Lists.newArrayList();
|
|
public final ServerChunkCache chunkSource;
|
|
private final MinecraftServer server;
|
|
- public final ServerLevelData serverLevelData;
|
|
+ public final PrimaryLevelData serverLevelData; // CraftBukkit - type
|
|
private int lastSpawnChunkRadius;
|
|
final EntityTickList entityTickList = new EntityTickList();
|
|
public final PersistentEntitySectionManager<Entity> entityManager;
|
|
@@ -214,54 +226,203 @@
|
|
private final boolean tickTime;
|
|
private final RandomSequences randomSequences;
|
|
|
|
- public ServerLevel(MinecraftServer server, Executor workerExecutor, LevelStorageSource.LevelStorageAccess session, ServerLevelData properties, ResourceKey<Level> worldKey, LevelStem dimensionOptions, ChunkProgressListener worldGenerationProgressListener, boolean debugWorld, long seed, List<CustomSpawner> spawners, boolean shouldTickTime, @Nullable RandomSequences randomSequencesState) {
|
|
- super(properties, worldKey, server.registryAccess(), dimensionOptions.type(), false, debugWorld, seed, server.getMaxChainedNeighborUpdates());
|
|
- this.tickTime = shouldTickTime;
|
|
- this.server = server;
|
|
- this.customSpawners = spawners;
|
|
- this.serverLevelData = properties;
|
|
- ChunkGenerator chunkgenerator = dimensionOptions.generator();
|
|
- boolean flag2 = server.forceSynchronousWrites();
|
|
- DataFixer datafixer = server.getFixerUpper();
|
|
- EntityPersistentStorage<Entity> entitypersistentstorage = new EntityStorage(new SimpleRegionStorage(new RegionStorageInfo(session.getLevelId(), worldKey, "entities"), session.getDimensionPath(worldKey).resolve("entities"), datafixer, flag2, DataFixTypes.ENTITY_CHUNK), this, server);
|
|
+ // CraftBukkit start
|
|
+ public final LevelStorageSource.LevelStorageAccess convertable;
|
|
+ public final UUID uuid;
|
|
+ public boolean hasPhysicsEvent = true; // Paper - BlockPhysicsEvent
|
|
|
|
+ public LevelChunk getChunkIfLoaded(int x, int z) {
|
|
+ return this.chunkSource.getChunk(x, z, false);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ResourceKey<LevelStem> getTypeKey() {
|
|
+ return this.convertable.dimensionType;
|
|
+ }
|
|
+
|
|
+ // Paper start
|
|
+ public final boolean areChunksLoadedForMove(AABB axisalignedbb) {
|
|
+ // copied code from collision methods, so that we can guarantee that they wont load chunks (we don't override
|
|
+ // ICollisionAccess methods for VoxelShapes)
|
|
+ // be more strict too, add a block (dumb plugins in move events?)
|
|
+ int minBlockX = Mth.floor(axisalignedbb.minX - 1.0E-7D) - 3;
|
|
+ int maxBlockX = Mth.floor(axisalignedbb.maxX + 1.0E-7D) + 3;
|
|
+
|
|
+ int minBlockZ = Mth.floor(axisalignedbb.minZ - 1.0E-7D) - 3;
|
|
+ int maxBlockZ = Mth.floor(axisalignedbb.maxZ + 1.0E-7D) + 3;
|
|
+
|
|
+ int minChunkX = minBlockX >> 4;
|
|
+ int maxChunkX = maxBlockX >> 4;
|
|
+
|
|
+ int minChunkZ = minBlockZ >> 4;
|
|
+ int maxChunkZ = maxBlockZ >> 4;
|
|
+
|
|
+ ServerChunkCache chunkProvider = this.getChunkSource();
|
|
+
|
|
+ for (int cx = minChunkX; cx <= maxChunkX; ++cx) {
|
|
+ for (int cz = minChunkZ; cz <= maxChunkZ; ++cz) {
|
|
+ if (chunkProvider.getChunkAtIfLoadedImmediately(cx, cz) == null) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ public final void loadChunksForMoveAsync(AABB axisalignedbb, ca.spottedleaf.concurrentutil.util.Priority priority,
|
|
+ java.util.function.Consumer<List<net.minecraft.world.level.chunk.ChunkAccess>> onLoad) {
|
|
+ if (Thread.currentThread() != this.thread) {
|
|
+ this.getChunkSource().mainThreadProcessor.execute(() -> {
|
|
+ this.loadChunksForMoveAsync(axisalignedbb, priority, onLoad);
|
|
+ });
|
|
+ return;
|
|
+ }
|
|
+ int minBlockX = Mth.floor(axisalignedbb.minX - 1.0E-7D) - 3;
|
|
+ int minBlockZ = Mth.floor(axisalignedbb.minZ - 1.0E-7D) - 3;
|
|
+
|
|
+ int maxBlockX = Mth.floor(axisalignedbb.maxX + 1.0E-7D) + 3;
|
|
+ int maxBlockZ = Mth.floor(axisalignedbb.maxZ + 1.0E-7D) + 3;
|
|
+
|
|
+ int minChunkX = minBlockX >> 4;
|
|
+ int minChunkZ = minBlockZ >> 4;
|
|
+
|
|
+ int maxChunkX = maxBlockX >> 4;
|
|
+ int maxChunkZ = maxBlockZ >> 4;
|
|
+
|
|
+ this.loadChunks(minChunkX, minChunkZ, maxChunkX, maxChunkZ, priority, onLoad);
|
|
+ }
|
|
+
|
|
+ public final void loadChunks(int minChunkX, int minChunkZ, int maxChunkX, int maxChunkZ,
|
|
+ ca.spottedleaf.concurrentutil.util.Priority priority,
|
|
+ java.util.function.Consumer<List<net.minecraft.world.level.chunk.ChunkAccess>> onLoad) {
|
|
+ List<net.minecraft.world.level.chunk.ChunkAccess> ret = new java.util.ArrayList<>();
|
|
+ it.unimi.dsi.fastutil.ints.IntArrayList ticketLevels = new it.unimi.dsi.fastutil.ints.IntArrayList();
|
|
+ ServerChunkCache chunkProvider = this.getChunkSource();
|
|
+
|
|
+ int requiredChunks = (maxChunkX - minChunkX + 1) * (maxChunkZ - minChunkZ + 1);
|
|
+ int[] loadedChunks = new int[1];
|
|
+
|
|
+ Long holderIdentifier = Long.valueOf(chunkProvider.chunkFutureAwaitCounter++);
|
|
+
|
|
+ java.util.function.Consumer<net.minecraft.world.level.chunk.ChunkAccess> consumer = (net.minecraft.world.level.chunk.ChunkAccess chunk) -> {
|
|
+ if (chunk != null) {
|
|
+ int ticketLevel = Math.max(33, chunkProvider.chunkMap.getUpdatingChunkIfPresent(chunk.getPos().toLong()).getTicketLevel());
|
|
+ ret.add(chunk);
|
|
+ ticketLevels.add(ticketLevel);
|
|
+ chunkProvider.addTicketAtLevel(TicketType.FUTURE_AWAIT, chunk.getPos(), ticketLevel, holderIdentifier);
|
|
+ }
|
|
+ if (++loadedChunks[0] == requiredChunks) {
|
|
+ try {
|
|
+ onLoad.accept(java.util.Collections.unmodifiableList(ret));
|
|
+ } finally {
|
|
+ for (int i = 0, len = ret.size(); i < len; ++i) {
|
|
+ ChunkPos chunkPos = ret.get(i).getPos();
|
|
+ int ticketLevel = ticketLevels.getInt(i);
|
|
+
|
|
+ chunkProvider.addTicketAtLevel(TicketType.UNKNOWN, chunkPos, ticketLevel, chunkPos);
|
|
+ chunkProvider.removeTicketAtLevel(TicketType.FUTURE_AWAIT, chunkPos, ticketLevel, holderIdentifier);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+
|
|
+ for (int cx = minChunkX; cx <= maxChunkX; ++cx) {
|
|
+ for (int cz = minChunkZ; cz <= maxChunkZ; ++cz) {
|
|
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.scheduleChunkLoad(
|
|
+ this, cx, cz, net.minecraft.world.level.chunk.status.ChunkStatus.FULL, true, priority, consumer
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
+ // Paper start - optimise getPlayerByUUID
|
|
+ @Nullable
|
|
+ @Override
|
|
+ public Player getPlayerByUUID(UUID uuid) {
|
|
+ final Player player = this.getServer().getPlayerList().getPlayer(uuid);
|
|
+ return player != null && player.level() == this ? player : null;
|
|
+ }
|
|
+ // Paper end - optimise getPlayerByUUID
|
|
+
|
|
+ // Add env and gen to constructor, IWorldDataServer -> WorldDataServer
|
|
+ public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PrimaryLevelData iworlddataserver, ResourceKey<Level> resourcekey, LevelStem worlddimension, ChunkProgressListener worldloadlistener, boolean flag, long i, List<CustomSpawner> list, boolean flag1, @Nullable RandomSequences randomsequences, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) {
|
|
+ super(iworlddataserver, resourcekey, minecraftserver.registryAccess(), worlddimension.type(), false, flag, i, minecraftserver.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> minecraftserver.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(convertable_conversionsession.levelDirectory.path(), iworlddataserver.getLevelName(), resourcekey.location(), spigotConfig, minecraftserver.registryAccess(), iworlddataserver.getGameRules()))); // Paper - create paper world configs
|
|
+ this.pvpMode = minecraftserver.isPvpAllowed();
|
|
+ this.convertable = convertable_conversionsession;
|
|
+ this.uuid = WorldUUID.getUUID(convertable_conversionsession.levelDirectory.path().toFile());
|
|
+ // CraftBukkit end
|
|
+ this.tickTime = flag1;
|
|
+ this.server = minecraftserver;
|
|
+ this.customSpawners = list;
|
|
+ this.serverLevelData = iworlddataserver;
|
|
+ ChunkGenerator chunkgenerator = worlddimension.generator();
|
|
+ // CraftBukkit start
|
|
+ this.serverLevelData.setWorld(this);
|
|
+
|
|
+ if (biomeProvider != null) {
|
|
+ BiomeSource worldChunkManager = new CustomWorldChunkManager(this.getWorld(), biomeProvider, this.server.registryAccess().lookupOrThrow(Registries.BIOME));
|
|
+ if (chunkgenerator instanceof NoiseBasedChunkGenerator cga) {
|
|
+ chunkgenerator = new NoiseBasedChunkGenerator(worldChunkManager, cga.settings);
|
|
+ } else if (chunkgenerator instanceof FlatLevelSource cpf) {
|
|
+ chunkgenerator = new FlatLevelSource(cpf.settings(), worldChunkManager);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (gen != null) {
|
|
+ chunkgenerator = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, chunkgenerator, gen);
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+ boolean flag2 = minecraftserver.forceSynchronousWrites();
|
|
+ DataFixer datafixer = minecraftserver.getFixerUpper();
|
|
+ EntityPersistentStorage<Entity> entitypersistentstorage = new EntityStorage(new SimpleRegionStorage(new RegionStorageInfo(convertable_conversionsession.getLevelId(), resourcekey, "entities"), convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), datafixer, flag2, DataFixTypes.ENTITY_CHUNK), this, minecraftserver);
|
|
+
|
|
this.entityManager = new PersistentEntitySectionManager<>(Entity.class, new ServerLevel.EntityCallbacks(), entitypersistentstorage);
|
|
- StructureTemplateManager structuretemplatemanager = server.getStructureManager();
|
|
- int j = server.getPlayerList().getViewDistance();
|
|
- int k = server.getPlayerList().getSimulationDistance();
|
|
+ StructureTemplateManager structuretemplatemanager = minecraftserver.getStructureManager();
|
|
+ int j = this.spigotConfig.viewDistance; // Spigot
|
|
+ int k = this.spigotConfig.simulationDistance; // Spigot
|
|
PersistentEntitySectionManager persistententitysectionmanager = this.entityManager;
|
|
|
|
Objects.requireNonNull(this.entityManager);
|
|
- this.chunkSource = new ServerChunkCache(this, session, datafixer, structuretemplatemanager, workerExecutor, chunkgenerator, j, k, flag2, worldGenerationProgressListener, persistententitysectionmanager::updateChunkStatus, () -> {
|
|
- return server.overworld().getDataStorage();
|
|
+ this.chunkSource = new ServerChunkCache(this, convertable_conversionsession, datafixer, structuretemplatemanager, executor, chunkgenerator, j, k, flag2, worldloadlistener, persistententitysectionmanager::updateChunkStatus, () -> {
|
|
+ return minecraftserver.overworld().getDataStorage();
|
|
});
|
|
this.chunkSource.getGeneratorState().ensureStructuresGenerated();
|
|
this.portalForcer = new PortalForcer(this);
|
|
this.updateSkyBrightness();
|
|
this.prepareWeather();
|
|
- this.getWorldBorder().setAbsoluteMaxSize(server.getAbsoluteMaxWorldSize());
|
|
+ this.getWorldBorder().setAbsoluteMaxSize(minecraftserver.getAbsoluteMaxWorldSize());
|
|
this.raids = (Raids) this.getDataStorage().computeIfAbsent(Raids.factory(this), Raids.getFileId(this.dimensionTypeRegistration()));
|
|
- if (!server.isSingleplayer()) {
|
|
- properties.setGameType(server.getDefaultGameType());
|
|
+ if (!minecraftserver.isSingleplayer()) {
|
|
+ iworlddataserver.setGameType(minecraftserver.getDefaultGameType());
|
|
}
|
|
|
|
- long l = server.getWorldData().worldGenOptions().seed();
|
|
+ long l = minecraftserver.getWorldData().worldGenOptions().seed();
|
|
|
|
- this.structureCheck = new StructureCheck(this.chunkSource.chunkScanner(), this.registryAccess(), server.getStructureManager(), worldKey, chunkgenerator, this.chunkSource.randomState(), this, chunkgenerator.getBiomeSource(), l, datafixer);
|
|
- this.structureManager = new StructureManager(this, server.getWorldData().worldGenOptions(), this.structureCheck);
|
|
- if (this.dimension() == Level.END && this.dimensionTypeRegistration().is(BuiltinDimensionTypes.END)) {
|
|
- this.dragonFight = new EndDragonFight(this, l, server.getWorldData().endDragonFightData());
|
|
+ this.structureCheck = new StructureCheck(this.chunkSource.chunkScanner(), this.registryAccess(), minecraftserver.getStructureManager(), this.getTypeKey(), chunkgenerator, this.chunkSource.randomState(), this, chunkgenerator.getBiomeSource(), l, datafixer); // Paper - Fix missing CB diff
|
|
+ this.structureManager = new StructureManager(this, this.serverLevelData.worldGenOptions(), this.structureCheck); // CraftBukkit
|
|
+ if ((this.dimension() == Level.END && this.dimensionTypeRegistration().is(BuiltinDimensionTypes.END)) || env == org.bukkit.World.Environment.THE_END) { // CraftBukkit - Allow to create EnderDragonBattle in default and custom END
|
|
+ this.dragonFight = new EndDragonFight(this, this.serverLevelData.worldGenOptions().seed(), this.serverLevelData.endDragonFightData()); // CraftBukkit
|
|
} else {
|
|
this.dragonFight = null;
|
|
}
|
|
|
|
this.sleepStatus = new SleepStatus();
|
|
this.gameEventDispatcher = new GameEventDispatcher(this);
|
|
- this.randomSequences = (RandomSequences) Objects.requireNonNullElseGet(randomSequencesState, () -> {
|
|
+ this.randomSequences = (RandomSequences) Objects.requireNonNullElseGet(randomsequences, () -> {
|
|
return (RandomSequences) this.getDataStorage().computeIfAbsent(RandomSequences.factory(l), "random_sequences");
|
|
});
|
|
+ this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit
|
|
}
|
|
|
|
+ // Paper start
|
|
+ @Override
|
|
+ public boolean hasChunk(int chunkX, int chunkZ) {
|
|
+ return this.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ) != null;
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
/** @deprecated */
|
|
@Deprecated
|
|
@VisibleForTesting
|
|
@@ -305,12 +466,20 @@
|
|
long j;
|
|
|
|
if (this.sleepStatus.areEnoughSleeping(i) && this.sleepStatus.areEnoughDeepSleeping(i, this.players)) {
|
|
+ // CraftBukkit start
|
|
+ j = this.levelData.getDayTime() + 24000L;
|
|
+ TimeSkipEvent event = new TimeSkipEvent(this.getWorld(), TimeSkipEvent.SkipReason.NIGHT_SKIP, (j - j % 24000L) - this.getDayTime());
|
|
if (this.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)) {
|
|
- j = this.levelData.getDayTime() + 24000L;
|
|
- this.setDayTime(j - j % 24000L);
|
|
+ this.getCraftServer().getPluginManager().callEvent(event);
|
|
+ if (!event.isCancelled()) {
|
|
+ this.setDayTime(this.getDayTime() + event.getSkipAmount());
|
|
+ }
|
|
}
|
|
|
|
- this.wakeUpAllPlayers();
|
|
+ if (!event.isCancelled()) {
|
|
+ this.wakeUpAllPlayers();
|
|
+ }
|
|
+ // CraftBukkit end
|
|
if (this.getGameRules().getBoolean(GameRules.RULE_WEATHER_CYCLE) && this.isRaining()) {
|
|
this.resetWeatherCycle();
|
|
}
|
|
@@ -345,7 +514,7 @@
|
|
|
|
this.handlingTick = false;
|
|
gameprofilerfiller.pop();
|
|
- boolean flag1 = !this.players.isEmpty() || !this.getForcedChunks().isEmpty();
|
|
+ boolean flag1 = true || !this.players.isEmpty() || !this.getForcedChunks().isEmpty(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players
|
|
|
|
if (flag1) {
|
|
this.resetEmptyTime();
|
|
@@ -359,6 +528,7 @@
|
|
gameprofilerfiller.pop();
|
|
}
|
|
|
|
+ org.spigotmc.ActivationRange.activateEntities(this); // Spigot
|
|
this.entityTickList.forEach((entity) -> {
|
|
if (!entity.isRemoved()) {
|
|
if (!tickratemanager.isEntityFrozen(entity)) {
|
|
@@ -429,7 +599,7 @@
|
|
|
|
private void wakeUpAllPlayers() {
|
|
this.sleepStatus.removeAllSleepers();
|
|
- ((List) this.players.stream().filter(LivingEntity::isSleeping).collect(Collectors.toList())).forEach((entityplayer) -> {
|
|
+ (this.players.stream().filter(LivingEntity::isSleeping).collect(Collectors.toList())).forEach((entityplayer) -> { // CraftBukkit - decompile error
|
|
entityplayer.stopSleepInBed(false, false);
|
|
});
|
|
}
|
|
@@ -442,12 +612,12 @@
|
|
ProfilerFiller gameprofilerfiller = Profiler.get();
|
|
|
|
gameprofilerfiller.push("thunder");
|
|
- if (flag && this.isThundering() && this.random.nextInt(100000) == 0) {
|
|
+ if (!this.paperConfig().environment.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && this.random.nextInt(this.spigotConfig.thunderChance) == 0) { // Spigot // Paper - Option to disable thunder
|
|
BlockPos blockposition = this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15));
|
|
|
|
if (this.isRainingAt(blockposition)) {
|
|
DifficultyInstance difficultydamagescaler = this.getCurrentDifficultyAt(blockposition);
|
|
- boolean flag1 = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && this.random.nextDouble() < (double) difficultydamagescaler.getEffectiveDifficulty() * 0.01D && !this.getBlockState(blockposition.below()).is(Blocks.LIGHTNING_ROD);
|
|
+ boolean flag1 = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && this.random.nextDouble() < (double) difficultydamagescaler.getEffectiveDifficulty() * this.paperConfig().entities.spawning.skeletonHorseThunderSpawnChance.or(0.01D) && !this.getBlockState(blockposition.below()).is(Blocks.LIGHTNING_ROD); // Paper - Configurable spawn chances for skeleton horses
|
|
|
|
if (flag1) {
|
|
SkeletonHorse entityhorseskeleton = (SkeletonHorse) EntityType.SKELETON_HORSE.create(this, EntitySpawnReason.EVENT);
|
|
@@ -456,7 +626,7 @@
|
|
entityhorseskeleton.setTrap(true);
|
|
entityhorseskeleton.setAge(0);
|
|
entityhorseskeleton.setPos((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ());
|
|
- this.addFreshEntity(entityhorseskeleton);
|
|
+ this.addFreshEntity(entityhorseskeleton, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); // CraftBukkit
|
|
}
|
|
}
|
|
|
|
@@ -465,18 +635,20 @@
|
|
if (entitylightning != null) {
|
|
entitylightning.moveTo(Vec3.atBottomCenterOf(blockposition));
|
|
entitylightning.setVisualOnly(flag1);
|
|
- this.addFreshEntity(entitylightning);
|
|
+ this.strikeLightning(entitylightning, org.bukkit.event.weather.LightningStrikeEvent.Cause.WEATHER); // CraftBukkit
|
|
}
|
|
}
|
|
}
|
|
|
|
gameprofilerfiller.popPush("iceandsnow");
|
|
|
|
+ if (!this.paperConfig().environment.disableIceAndSnow) { // Paper - Option to disable ice and snow
|
|
for (int l = 0; l < randomTickSpeed; ++l) {
|
|
if (this.random.nextInt(48) == 0) {
|
|
this.tickPrecipitation(this.getBlockRandomPos(j, 0, k, 15));
|
|
}
|
|
}
|
|
+ } // Paper - Option to disable ice and snow
|
|
|
|
gameprofilerfiller.popPush("tickBlocks");
|
|
if (randomTickSpeed > 0) {
|
|
@@ -521,7 +693,7 @@
|
|
Biome biomebase = (Biome) this.getBiome(blockposition1).value();
|
|
|
|
if (biomebase.shouldFreeze(this, blockposition2)) {
|
|
- this.setBlockAndUpdate(blockposition2, Blocks.ICE.defaultBlockState());
|
|
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition2, Blocks.ICE.defaultBlockState(), null); // CraftBukkit
|
|
}
|
|
|
|
if (this.isRaining()) {
|
|
@@ -537,10 +709,10 @@
|
|
BlockState iblockdata1 = (BlockState) iblockdata.setValue(SnowLayerBlock.LAYERS, j + 1);
|
|
|
|
Block.pushEntitiesUp(iblockdata, iblockdata1, this, blockposition1);
|
|
- this.setBlockAndUpdate(blockposition1, iblockdata1);
|
|
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition1, iblockdata1, null); // CraftBukkit
|
|
}
|
|
} else {
|
|
- this.setBlockAndUpdate(blockposition1, Blocks.SNOW.defaultBlockState());
|
|
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition1, Blocks.SNOW.defaultBlockState(), null); // CraftBukkit
|
|
}
|
|
}
|
|
|
|
@@ -701,33 +873,67 @@
|
|
this.rainLevel = Mth.clamp(this.rainLevel, 0.0F, 1.0F);
|
|
}
|
|
|
|
+ /* CraftBukkit start
|
|
if (this.oRainLevel != this.rainLevel) {
|
|
- this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.RAIN_LEVEL_CHANGE, this.rainLevel), this.dimension());
|
|
+ this.server.getPlayerList().broadcastAll(new PacketPlayOutGameStateChange(PacketPlayOutGameStateChange.RAIN_LEVEL_CHANGE, this.rainLevel), this.dimension());
|
|
}
|
|
|
|
if (this.oThunderLevel != this.thunderLevel) {
|
|
- this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE, this.thunderLevel), this.dimension());
|
|
+ this.server.getPlayerList().broadcastAll(new PacketPlayOutGameStateChange(PacketPlayOutGameStateChange.THUNDER_LEVEL_CHANGE, this.thunderLevel), this.dimension());
|
|
}
|
|
|
|
if (flag != this.isRaining()) {
|
|
if (flag) {
|
|
- this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.STOP_RAINING, 0.0F));
|
|
+ this.server.getPlayerList().broadcastAll(new PacketPlayOutGameStateChange(PacketPlayOutGameStateChange.STOP_RAINING, 0.0F));
|
|
} else {
|
|
- this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.START_RAINING, 0.0F));
|
|
+ this.server.getPlayerList().broadcastAll(new PacketPlayOutGameStateChange(PacketPlayOutGameStateChange.START_RAINING, 0.0F));
|
|
}
|
|
|
|
- this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.RAIN_LEVEL_CHANGE, this.rainLevel));
|
|
- this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE, this.thunderLevel));
|
|
+ this.server.getPlayerList().broadcastAll(new PacketPlayOutGameStateChange(PacketPlayOutGameStateChange.RAIN_LEVEL_CHANGE, this.rainLevel));
|
|
+ this.server.getPlayerList().broadcastAll(new PacketPlayOutGameStateChange(PacketPlayOutGameStateChange.THUNDER_LEVEL_CHANGE, this.thunderLevel));
|
|
}
|
|
+ // */
|
|
+ for (int idx = 0; idx < this.players.size(); ++idx) {
|
|
+ if (((ServerPlayer) this.players.get(idx)).level() == this) {
|
|
+ ((ServerPlayer) this.players.get(idx)).tickWeather();
|
|
+ }
|
|
+ }
|
|
|
|
+ if (flag != this.isRaining()) {
|
|
+ // Only send weather packets to those affected
|
|
+ for (int idx = 0; idx < this.players.size(); ++idx) {
|
|
+ if (((ServerPlayer) this.players.get(idx)).level() == this) {
|
|
+ ((ServerPlayer) this.players.get(idx)).setPlayerWeather((!flag ? WeatherType.DOWNFALL : WeatherType.CLEAR), false);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ for (int idx = 0; idx < this.players.size(); ++idx) {
|
|
+ if (((ServerPlayer) this.players.get(idx)).level() == this) {
|
|
+ ((ServerPlayer) this.players.get(idx)).updateWeather(this.oRainLevel, this.rainLevel, this.oThunderLevel, this.thunderLevel);
|
|
+ }
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
}
|
|
|
|
@VisibleForTesting
|
|
public void resetWeatherCycle() {
|
|
- this.serverLevelData.setRainTime(0);
|
|
+ // CraftBukkit start
|
|
this.serverLevelData.setRaining(false);
|
|
- this.serverLevelData.setThunderTime(0);
|
|
+ // If we stop due to everyone sleeping we should reset the weather duration to some other random value.
|
|
+ // Not that everyone ever manages to get the whole server to sleep at the same time....
|
|
+ if (!this.serverLevelData.isRaining()) {
|
|
+ this.serverLevelData.setRainTime(0);
|
|
+ }
|
|
+ // CraftBukkit end
|
|
this.serverLevelData.setThundering(false);
|
|
+ // CraftBukkit start
|
|
+ // If we stop due to everyone sleeping we should reset the weather duration to some other random value.
|
|
+ // Not that everyone ever manages to get the whole server to sleep at the same time....
|
|
+ if (!this.serverLevelData.isThundering()) {
|
|
+ this.serverLevelData.setThunderTime(0);
|
|
+ }
|
|
+ // CraftBukkit end
|
|
}
|
|
|
|
public void resetEmptyTime() {
|
|
@@ -754,6 +960,13 @@
|
|
}
|
|
|
|
public void tickNonPassenger(Entity entity) {
|
|
+ // Spigot start
|
|
+ if (!org.spigotmc.ActivationRange.checkIfActive(entity)) {
|
|
+ entity.tickCount++;
|
|
+ entity.inactiveTick();
|
|
+ return;
|
|
+ }
|
|
+ // Spigot end
|
|
entity.setOldPosAndRot();
|
|
ProfilerFiller gameprofilerfiller = Profiler.get();
|
|
|
|
@@ -763,6 +976,7 @@
|
|
});
|
|
gameprofilerfiller.incrementCounter("tickNonPassenger");
|
|
entity.tick();
|
|
+ entity.postTick(); // CraftBukkit
|
|
gameprofilerfiller.pop();
|
|
Iterator iterator = entity.getPassengers().iterator();
|
|
|
|
@@ -786,6 +1000,7 @@
|
|
});
|
|
gameprofilerfiller.incrementCounter("tickPassenger");
|
|
passenger.rideTick();
|
|
+ passenger.postTick(); // CraftBukkit
|
|
gameprofilerfiller.pop();
|
|
Iterator iterator = passenger.getPassengers().iterator();
|
|
|
|
@@ -810,6 +1025,7 @@
|
|
ServerChunkCache chunkproviderserver = this.getChunkSource();
|
|
|
|
if (!savingDisabled) {
|
|
+ org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(this.getWorld())); // CraftBukkit
|
|
if (progressListener != null) {
|
|
progressListener.progressStartNoAbort(Component.translatable("menu.savingLevel"));
|
|
}
|
|
@@ -827,11 +1043,19 @@
|
|
}
|
|
|
|
}
|
|
+
|
|
+ // CraftBukkit start - moved from MinecraftServer.saveChunks
|
|
+ ServerLevel worldserver1 = this;
|
|
+
|
|
+ this.serverLevelData.setWorldBorder(worldserver1.getWorldBorder().createSettings());
|
|
+ this.serverLevelData.setCustomBossEvents(this.server.getCustomBossEvents().save(this.registryAccess()));
|
|
+ this.convertable.saveDataTag(this.server.registryAccess(), this.serverLevelData, this.server.getPlayerList().getSingleplayerData());
|
|
+ // CraftBukkit end
|
|
}
|
|
|
|
private void saveLevelData(boolean flush) {
|
|
if (this.dragonFight != null) {
|
|
- this.server.getWorldData().setEndDragonFightData(this.dragonFight.saveData());
|
|
+ this.serverLevelData.setEndDragonFightData(this.dragonFight.saveData()); // CraftBukkit
|
|
}
|
|
|
|
DimensionDataStorage worldpersistentdata = this.getChunkSource().getDataStorage();
|
|
@@ -903,18 +1127,40 @@
|
|
|
|
@Override
|
|
public boolean addFreshEntity(Entity entity) {
|
|
- return this.addEntity(entity);
|
|
+ // CraftBukkit start
|
|
+ return this.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.DEFAULT);
|
|
}
|
|
|
|
+ @Override
|
|
+ public boolean addFreshEntity(Entity entity, CreatureSpawnEvent.SpawnReason reason) {
|
|
+ return this.addEntity(entity, reason);
|
|
+ // CraftBukkit end
|
|
+ }
|
|
+
|
|
public boolean addWithUUID(Entity entity) {
|
|
- return this.addEntity(entity);
|
|
+ // CraftBukkit start
|
|
+ return this.addWithUUID(entity, CreatureSpawnEvent.SpawnReason.DEFAULT);
|
|
}
|
|
|
|
+ public boolean addWithUUID(Entity entity, CreatureSpawnEvent.SpawnReason reason) {
|
|
+ return this.addEntity(entity, reason);
|
|
+ // CraftBukkit end
|
|
+ }
|
|
+
|
|
public void addDuringTeleport(Entity entity) {
|
|
+ // CraftBukkit start
|
|
+ // SPIGOT-6415: Don't call spawn event for entities which travel trough worlds,
|
|
+ // since it is only an implementation detail, that a new entity is created when
|
|
+ // they are traveling between worlds.
|
|
+ this.addDuringTeleport(entity, null);
|
|
+ }
|
|
+
|
|
+ public void addDuringTeleport(Entity entity, CreatureSpawnEvent.SpawnReason reason) {
|
|
+ // CraftBukkit end
|
|
if (entity instanceof ServerPlayer entityplayer) {
|
|
this.addPlayer(entityplayer);
|
|
} else {
|
|
- this.addEntity(entity);
|
|
+ this.addEntity(entity, reason); // CraftBukkit
|
|
}
|
|
|
|
}
|
|
@@ -939,41 +1185,93 @@
|
|
this.entityManager.addNewEntity(player);
|
|
}
|
|
|
|
- private boolean addEntity(Entity entity) {
|
|
+ // CraftBukkit start
|
|
+ private boolean addEntity(Entity entity, CreatureSpawnEvent.SpawnReason spawnReason) {
|
|
+ org.spigotmc.AsyncCatcher.catchOp("entity add"); // Spigot
|
|
+ // Paper start - extra debug info
|
|
+ if (entity.valid) {
|
|
+ MinecraftServer.LOGGER.error("Attempted Double World add on {}", entity, new Throwable());
|
|
+ return true;
|
|
+ }
|
|
+ // Paper end - extra debug info
|
|
+ if (entity.spawnReason == null) entity.spawnReason = spawnReason; // Paper - Entity#getEntitySpawnReason
|
|
if (entity.isRemoved()) {
|
|
- ServerLevel.LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityType.getKey(entity.getType()));
|
|
+ // WorldServer.LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityTypes.getKey(entity.getType())); // CraftBukkit
|
|
return false;
|
|
} else {
|
|
+ // SPIGOT-6415: Don't call spawn event when reason is null. For example when an entity teleports to a new world.
|
|
+ if (spawnReason != null && !CraftEventFactory.doEntityAddEventCalling(this, entity, spawnReason)) {
|
|
+ return false;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
return this.entityManager.addNewEntity(entity);
|
|
}
|
|
}
|
|
|
|
public boolean tryAddFreshEntityWithPassengers(Entity entity) {
|
|
- Stream stream = entity.getSelfAndPassengers().map(Entity::getUUID);
|
|
+ // CraftBukkit start
|
|
+ return this.tryAddFreshEntityWithPassengers(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT);
|
|
+ }
|
|
+
|
|
+ public boolean tryAddFreshEntityWithPassengers(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) {
|
|
+ // CraftBukkit end
|
|
+ Stream<UUID> stream = entity.getSelfAndPassengers().map(Entity::getUUID); // CraftBukkit - decompile error
|
|
PersistentEntitySectionManager persistententitysectionmanager = this.entityManager;
|
|
|
|
Objects.requireNonNull(this.entityManager);
|
|
if (stream.anyMatch(persistententitysectionmanager::isLoaded)) {
|
|
return false;
|
|
} else {
|
|
- this.addFreshEntityWithPassengers(entity);
|
|
+ this.addFreshEntityWithPassengers(entity, reason); // CraftBukkit
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public void unload(LevelChunk chunk) {
|
|
+ // Spigot Start
|
|
+ for (net.minecraft.world.level.block.entity.BlockEntity tileentity : chunk.getBlockEntities().values()) {
|
|
+ if (tileentity instanceof net.minecraft.world.Container) {
|
|
+ for (org.bukkit.entity.HumanEntity h : Lists.newArrayList(((net.minecraft.world.Container) tileentity).getViewers())) {
|
|
+ h.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED); // Paper - Inventory close reason
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Spigot End
|
|
chunk.clearAllBlockEntities();
|
|
chunk.unregisterTickContainerFromLevel(this);
|
|
}
|
|
|
|
public void removePlayerImmediately(ServerPlayer player, Entity.RemovalReason reason) {
|
|
- player.remove(reason);
|
|
+ player.remove(reason, null); // CraftBukkit - add Bukkit remove cause
|
|
}
|
|
|
|
+ // CraftBukkit start
|
|
+ public boolean strikeLightning(Entity entitylightning) {
|
|
+ return this.strikeLightning(entitylightning, LightningStrikeEvent.Cause.UNKNOWN);
|
|
+ }
|
|
+
|
|
+ public boolean strikeLightning(Entity entitylightning, LightningStrikeEvent.Cause cause) {
|
|
+ LightningStrikeEvent lightning = CraftEventFactory.callLightningStrikeEvent((org.bukkit.entity.LightningStrike) entitylightning.getBukkitEntity(), cause);
|
|
+
|
|
+ if (lightning.isCancelled()) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return this.addFreshEntity(entitylightning);
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
@Override
|
|
public void destroyBlockProgress(int entityId, BlockPos pos, int progress) {
|
|
Iterator iterator = this.server.getPlayerList().getPlayers().iterator();
|
|
|
|
+ // CraftBukkit start
|
|
+ Player entityhuman = null;
|
|
+ Entity entity = this.getEntity(entityId);
|
|
+ if (entity instanceof Player) entityhuman = (Player) entity;
|
|
+ // CraftBukkit end
|
|
+
|
|
while (iterator.hasNext()) {
|
|
ServerPlayer entityplayer = (ServerPlayer) iterator.next();
|
|
|
|
@@ -982,6 +1280,12 @@
|
|
double d1 = (double) pos.getY() - entityplayer.getY();
|
|
double d2 = (double) pos.getZ() - entityplayer.getZ();
|
|
|
|
+ // CraftBukkit start
|
|
+ if (entityhuman != null && !entityplayer.getBukkitEntity().canSee(entityhuman.getBukkitEntity())) {
|
|
+ continue;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
if (d0 * d0 + d1 * d1 + d2 * d2 < 1024.0D) {
|
|
entityplayer.connection.send(new ClientboundBlockDestructionPacket(entityId, pos, progress));
|
|
}
|
|
@@ -1030,7 +1334,7 @@
|
|
|
|
@Override
|
|
public void levelEvent(@Nullable Player player, int eventId, BlockPos pos, int data) {
|
|
- this.server.getPlayerList().broadcast(player, (double) pos.getX(), (double) pos.getY(), (double) pos.getZ(), 64.0D, this.dimension(), new ClientboundLevelEventPacket(eventId, pos, data, false));
|
|
+ this.server.getPlayerList().broadcast(player, (double) pos.getX(), (double) pos.getY(), (double) pos.getZ(), 64.0D, this.dimension(), new ClientboundLevelEventPacket(eventId, pos, data, false)); // Paper - diff on change (the 64.0 distance is used as defaults for sound ranges in spigot config for ender dragon, end portal and wither)
|
|
}
|
|
|
|
public int getLogicalHeight() {
|
|
@@ -1060,7 +1364,18 @@
|
|
Iterator iterator = this.navigatingMobs.iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
- Mob entityinsentient = (Mob) iterator.next();
|
|
+ // CraftBukkit start - fix SPIGOT-6362
|
|
+ Mob entityinsentient;
|
|
+ try {
|
|
+ entityinsentient = (Mob) iterator.next();
|
|
+ } catch (java.util.ConcurrentModificationException ex) {
|
|
+ // This can happen because the pathfinder update below may trigger a chunk load, which in turn may cause more navigators to register
|
|
+ // In this case we just run the update again across all the iterators as the chunk will then be loaded
|
|
+ // As this is a relative edge case it is much faster than copying navigators (on either read or write)
|
|
+ this.sendBlockUpdated(pos, oldState, newState, flags);
|
|
+ return;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
PathNavigation navigationabstract = entityinsentient.getNavigation();
|
|
|
|
if (navigationabstract.shouldRecomputePath(pos)) {
|
|
@@ -1086,11 +1401,13 @@
|
|
|
|
@Override
|
|
public void updateNeighborsAt(BlockPos pos, Block block) {
|
|
+ if (captureBlockStates) { return; } // Paper - Cancel all physics during placement
|
|
this.updateNeighborsAt(pos, block, ExperimentalRedstoneUtils.initialOrientation(this, (Direction) null, (Direction) null));
|
|
}
|
|
|
|
@Override
|
|
public void updateNeighborsAt(BlockPos pos, Block sourceBlock, @Nullable Orientation orientation) {
|
|
+ if (captureBlockStates) { return; } // Paper - Cancel all physics during placement
|
|
this.neighborUpdater.updateNeighborsAtExceptFromFacing(pos, sourceBlock, (Direction) null, orientation);
|
|
}
|
|
|
|
@@ -1126,9 +1443,20 @@
|
|
|
|
@Override
|
|
public void explode(@Nullable Entity entity, @Nullable DamageSource damageSource, @Nullable ExplosionDamageCalculator behavior, double x, double y, double z, float power, boolean createFire, Level.ExplosionInteraction explosionSourceType, ParticleOptions smallParticle, ParticleOptions largeParticle, Holder<SoundEvent> soundEvent) {
|
|
+ // CraftBukkit start
|
|
+ this.explode0(entity, damageSource, behavior, x, y, z, power, createFire, explosionSourceType, smallParticle, largeParticle, soundEvent);
|
|
+ }
|
|
+
|
|
+ public ServerExplosion explode0(@Nullable Entity entity, @Nullable DamageSource damagesource, @Nullable ExplosionDamageCalculator explosiondamagecalculator, double d0, double d1, double d2, float f, boolean flag, Level.ExplosionInteraction world_a, ParticleOptions particleparam, ParticleOptions particleparam1, Holder<SoundEvent> holder) {
|
|
+ // Paper start - Allow explosions to damage source
|
|
+ return this.explode0(entity, damagesource, explosiondamagecalculator, d0, d1, d2, f, flag, world_a, particleparam, particleparam1, holder, null);
|
|
+ }
|
|
+ public ServerExplosion explode0(@Nullable Entity entity, @Nullable DamageSource damagesource, @Nullable ExplosionDamageCalculator explosiondamagecalculator, double d0, double d1, double d2, float f, boolean flag, Level.ExplosionInteraction world_a, ParticleOptions particleparam, ParticleOptions particleparam1, Holder<SoundEvent> holder, java.util.function.Consumer<ServerExplosion> configurator) {
|
|
+ // Paper end - Allow explosions to damage source
|
|
+ // CraftBukkit end
|
|
Explosion.BlockInteraction explosion_effect;
|
|
|
|
- switch (explosionSourceType) {
|
|
+ switch (world_a) {
|
|
case NONE:
|
|
explosion_effect = Explosion.BlockInteraction.KEEP;
|
|
break;
|
|
@@ -1144,16 +1472,27 @@
|
|
case TRIGGER:
|
|
explosion_effect = Explosion.BlockInteraction.TRIGGER_BLOCK;
|
|
break;
|
|
+ // CraftBukkit start - handle custom explosion type
|
|
+ case STANDARD:
|
|
+ explosion_effect = Explosion.BlockInteraction.DESTROY;
|
|
+ break;
|
|
+ // CraftBukkit end
|
|
default:
|
|
throw new MatchException((String) null, (Throwable) null);
|
|
}
|
|
|
|
Explosion.BlockInteraction explosion_effect1 = explosion_effect;
|
|
- Vec3 vec3d = new Vec3(x, y, z);
|
|
- ServerExplosion serverexplosion = new ServerExplosion(this, entity, damageSource, behavior, vec3d, power, createFire, explosion_effect1);
|
|
+ Vec3 vec3d = new Vec3(d0, d1, d2);
|
|
+ ServerExplosion serverexplosion = new ServerExplosion(this, entity, damagesource, explosiondamagecalculator, vec3d, f, flag, explosion_effect1);
|
|
+ if (configurator != null) configurator.accept(serverexplosion);// Paper - Allow explosions to damage source
|
|
|
|
serverexplosion.explode();
|
|
- ParticleOptions particleparam2 = serverexplosion.isSmall() ? smallParticle : largeParticle;
|
|
+ // CraftBukkit start
|
|
+ if (serverexplosion.wasCanceled) {
|
|
+ return serverexplosion;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+ ParticleOptions particleparam2 = serverexplosion.isSmall() ? particleparam : particleparam1;
|
|
Iterator iterator = this.players.iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
@@ -1162,10 +1501,11 @@
|
|
if (entityplayer.distanceToSqr(vec3d) < 4096.0D) {
|
|
Optional<Vec3> optional = Optional.ofNullable((Vec3) serverexplosion.getHitPlayers().get(entityplayer));
|
|
|
|
- entityplayer.connection.send(new ClientboundExplodePacket(vec3d, optional, particleparam2, soundEvent));
|
|
+ entityplayer.connection.send(new ClientboundExplodePacket(vec3d, optional, particleparam2, holder));
|
|
}
|
|
}
|
|
|
|
+ return serverexplosion; // CraftBukkit
|
|
}
|
|
|
|
private Explosion.BlockInteraction getDestroyType(GameRules.Key<GameRules.BooleanValue> decayRule) {
|
|
@@ -1226,17 +1566,29 @@
|
|
}
|
|
|
|
public <T extends ParticleOptions> int sendParticles(T parameters, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double speed) {
|
|
- return this.sendParticles(parameters, false, false, x, y, z, count, offsetX, offsetY, offsetZ, speed);
|
|
+ return this.sendParticlesSource(null, parameters, false, false, x, y, z, count, offsetX, offsetY, offsetZ, speed); // CraftBukkit - visibility api support
|
|
}
|
|
|
|
public <T extends ParticleOptions> int sendParticles(T parameters, boolean force, boolean important, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double speed) {
|
|
- ClientboundLevelParticlesPacket packetplayoutworldparticles = new ClientboundLevelParticlesPacket(parameters, force, important, x, y, z, (float) offsetX, (float) offsetY, (float) offsetZ, (float) speed, count);
|
|
- int j = 0;
|
|
+ return this.sendParticlesSource(null, parameters, force, important, x, y, z, count, offsetX, offsetY, offsetZ, speed); // CraftBukkit - visibility api support
|
|
+ }
|
|
|
|
- for (int k = 0; k < this.players.size(); ++k) {
|
|
- ServerPlayer entityplayer = (ServerPlayer) this.players.get(k);
|
|
+ // CraftBukkit start - visibility api support
|
|
+ public <T extends ParticleOptions> int sendParticlesSource(ServerPlayer sender, T t0, boolean flag, boolean flag1, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6) {
|
|
+ // Paper start - Particle API
|
|
+ return this.sendParticlesSource(this.players, sender, t0, flag, flag1, d0, d1, d2, i, d3, d4, d5, d6);
|
|
+ }
|
|
+ public <T extends ParticleOptions> int sendParticlesSource(List<ServerPlayer> receivers, @Nullable ServerPlayer sender, T t0, boolean flag, boolean flag1, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6) {
|
|
+ // Paper end - Particle API
|
|
+ // CraftBukkit end
|
|
+ ClientboundLevelParticlesPacket packetplayoutworldparticles = new ClientboundLevelParticlesPacket(t0, flag, flag1, d0, d1, d2, (float) d3, (float) d4, (float) d5, (float) d6, i);
|
|
+ int j = 0;
|
|
|
|
- if (this.sendParticles(entityplayer, force, x, y, z, packetplayoutworldparticles)) {
|
|
+ for (Player entityhuman : receivers) { // Paper - Particle API
|
|
+ ServerPlayer entityplayer = (ServerPlayer) entityhuman; // Paper - Particle API
|
|
+ if (sender != null && !entityplayer.getBukkitEntity().canSee(sender.getBukkitEntity())) continue; // CraftBukkit
|
|
+
|
|
+ if (this.sendParticles(entityplayer, flag, d0, d1, d2, packetplayoutworldparticles)) {
|
|
++j;
|
|
}
|
|
}
|
|
@@ -1292,7 +1644,7 @@
|
|
|
|
@Nullable
|
|
public BlockPos findNearestMapStructure(TagKey<Structure> structureTag, BlockPos pos, int radius, boolean skipReferencedStructures) {
|
|
- if (!this.server.getWorldData().worldGenOptions().generateStructures()) {
|
|
+ if (!this.serverLevelData.worldGenOptions().generateStructures()) { // CraftBukkit
|
|
return null;
|
|
} else {
|
|
Optional<HolderSet.Named<Structure>> optional = this.registryAccess().lookupOrThrow(Registries.STRUCTURE).get(structureTag);
|
|
@@ -1334,11 +1686,22 @@
|
|
@Nullable
|
|
@Override
|
|
public MapItemSavedData getMapData(MapId id) {
|
|
- return (MapItemSavedData) this.getServer().overworld().getDataStorage().get(MapItemSavedData.factory(), id.key());
|
|
+ // CraftBukkit start
|
|
+ MapItemSavedData worldmap = (MapItemSavedData) this.getServer().overworld().getDataStorage().get(MapItemSavedData.factory(), id.key());
|
|
+ if (worldmap != null) {
|
|
+ worldmap.id = id;
|
|
+ }
|
|
+ return worldmap;
|
|
+ // CraftBukkit end
|
|
}
|
|
|
|
@Override
|
|
public void setMapData(MapId id, MapItemSavedData state) {
|
|
+ // CraftBukkit start
|
|
+ state.id = id;
|
|
+ MapInitializeEvent event = new MapInitializeEvent(state.mapView);
|
|
+ Bukkit.getServer().getPluginManager().callEvent(event);
|
|
+ // CraftBukkit end
|
|
this.getServer().overworld().getDataStorage().set(id.key(), state);
|
|
}
|
|
|
|
@@ -1649,6 +2012,11 @@
|
|
@Override
|
|
public void blockUpdated(BlockPos pos, Block block) {
|
|
if (!this.isDebug()) {
|
|
+ // CraftBukkit start
|
|
+ if (this.populating) {
|
|
+ return;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
this.updateNeighborsAt(pos, block);
|
|
}
|
|
|
|
@@ -1668,12 +2036,12 @@
|
|
}
|
|
|
|
public boolean isFlat() {
|
|
- return this.server.getWorldData().isFlatWorld();
|
|
+ return this.serverLevelData.isFlatWorld(); // CraftBukkit
|
|
}
|
|
|
|
@Override
|
|
public long getSeed() {
|
|
- return this.server.getWorldData().worldGenOptions().seed();
|
|
+ return this.serverLevelData.worldGenOptions().seed(); // CraftBukkit
|
|
}
|
|
|
|
@Nullable
|
|
@@ -1696,7 +2064,7 @@
|
|
private static <T> String getTypeCount(Iterable<T> items, Function<T, String> classifier) {
|
|
try {
|
|
Object2IntOpenHashMap<String> object2intopenhashmap = new Object2IntOpenHashMap();
|
|
- Iterator iterator = items.iterator();
|
|
+ Iterator<T> iterator = items.iterator(); // CraftBukkit - decompile error
|
|
|
|
while (iterator.hasNext()) {
|
|
T t0 = iterator.next();
|
|
@@ -1705,7 +2073,7 @@
|
|
object2intopenhashmap.addTo(s, 1);
|
|
}
|
|
|
|
- return (String) object2intopenhashmap.object2IntEntrySet().stream().sorted(Comparator.comparing(Entry::getIntValue).reversed()).limit(5L).map((entry) -> {
|
|
+ return (String) object2intopenhashmap.object2IntEntrySet().stream().sorted(Comparator.comparing(Entry<String>::getIntValue).reversed()).limit(5L).map((entry) -> { // CraftBukkit - decompile error
|
|
String s1 = (String) entry.getKey();
|
|
|
|
return s1 + ":" + entry.getIntValue();
|
|
@@ -1717,6 +2085,7 @@
|
|
|
|
@Override
|
|
public LevelEntityGetter<Entity> getEntities() {
|
|
+ org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot
|
|
return this.entityManager.getEntityGetter();
|
|
}
|
|
|
|
@@ -1802,6 +2171,17 @@
|
|
return this.serverLevelData.getGameRules();
|
|
}
|
|
|
|
+ // Paper start - respect global sound events gamerule
|
|
+ public List<net.minecraft.server.level.ServerPlayer> getPlayersForGlobalSoundGamerule() {
|
|
+ return this.getGameRules().getBoolean(GameRules.RULE_GLOBAL_SOUND_EVENTS) ? ((ServerLevel) this).getServer().getPlayerList().players : ((ServerLevel) this).players();
|
|
+ }
|
|
+
|
|
+ public double getGlobalSoundRangeSquared(java.util.function.Function<org.spigotmc.SpigotWorldConfig, Integer> rangeFunction) {
|
|
+ final double range = rangeFunction.apply(this.spigotConfig);
|
|
+ return range <= 0 ? 64.0 * 64.0 : range * range; // 64 is taken from default in ServerLevel#levelEvent
|
|
+ }
|
|
+ // Paper end - respect global sound events gamerule
|
|
+
|
|
@Override
|
|
public CrashReportCategory fillReportDetails(CrashReport report) {
|
|
CrashReportCategory crashreportsystemdetails = super.fillReportDetails(report);
|
|
@@ -1836,7 +2216,8 @@
|
|
}
|
|
|
|
public void onTrackingStart(Entity entity) {
|
|
- ServerLevel.this.getChunkSource().addEntity(entity);
|
|
+ org.spigotmc.AsyncCatcher.catchOp("entity register"); // Spigot
|
|
+ // ServerLevel.this.getChunkSource().addEntity(entity); // Paper - ignore and warn about illegal addEntity calls instead of crashing server; moved down below valid=true
|
|
if (entity instanceof ServerPlayer entityplayer) {
|
|
ServerLevel.this.players.add(entityplayer);
|
|
ServerLevel.this.updateSleepingPlayerList();
|
|
@@ -1864,9 +2245,53 @@
|
|
}
|
|
|
|
entity.updateDynamicGameEventListener(DynamicGameEventListener::add);
|
|
+ entity.inWorld = true; // CraftBukkit - Mark entity as in world
|
|
+ entity.valid = true; // CraftBukkit
|
|
+ ServerLevel.this.getChunkSource().addEntity(entity); // Paper - ignore and warn about illegal addEntity calls instead of crashing server
|
|
+ // Paper start - Entity origin API
|
|
+ if (entity.getOriginVector() == null) {
|
|
+ entity.setOrigin(entity.getBukkitEntity().getLocation());
|
|
+ }
|
|
+ // Default to current world if unknown, gross assumption but entities rarely change world
|
|
+ if (entity.getOriginWorld() == null) {
|
|
+ entity.setOrigin(entity.getOriginVector().toLocation(getWorld()));
|
|
+ }
|
|
+ // Paper end - Entity origin API
|
|
+ new com.destroystokyo.paper.event.entity.EntityAddToWorldEvent(entity.getBukkitEntity(), ServerLevel.this.getWorld()).callEvent(); // Paper - fire while valid
|
|
}
|
|
|
|
public void onTrackingEnd(Entity entity) {
|
|
+ org.spigotmc.AsyncCatcher.catchOp("entity unregister"); // Spigot
|
|
+ // Spigot start
|
|
+ if ( entity instanceof Player )
|
|
+ {
|
|
+ com.google.common.collect.Streams.stream( ServerLevel.this.getServer().getAllLevels() ).map( ServerLevel::getDataStorage ).forEach( (worldData) ->
|
|
+ {
|
|
+ for (Object o : worldData.cache.values() )
|
|
+ {
|
|
+ if ( o instanceof MapItemSavedData )
|
|
+ {
|
|
+ MapItemSavedData map = (MapItemSavedData) o;
|
|
+ map.carriedByPlayers.remove( (Player) entity );
|
|
+ for ( Iterator<MapItemSavedData.HoldingPlayer> iter = (Iterator<MapItemSavedData.HoldingPlayer>) map.carriedBy.iterator(); iter.hasNext(); )
|
|
+ {
|
|
+ if ( iter.next().player == entity )
|
|
+ {
|
|
+ iter.remove();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ } );
|
|
+ }
|
|
+ // Spigot end
|
|
+ // Spigot Start
|
|
+ if (entity.getBukkitEntity() instanceof org.bukkit.inventory.InventoryHolder && (!(entity instanceof ServerPlayer) || entity.getRemovalReason() != Entity.RemovalReason.KILLED)) { // SPIGOT-6876: closeInventory clears death message
|
|
+ for (org.bukkit.entity.HumanEntity h : Lists.newArrayList(((org.bukkit.inventory.InventoryHolder) entity.getBukkitEntity()).getInventory().getViewers())) {
|
|
+ h.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED); // Paper - Inventory close reason
|
|
+ }
|
|
+ }
|
|
+ // Spigot End
|
|
ServerLevel.this.getChunkSource().removeEntity(entity);
|
|
if (entity instanceof ServerPlayer entityplayer) {
|
|
ServerLevel.this.players.remove(entityplayer);
|
|
@@ -1895,6 +2320,15 @@
|
|
}
|
|
|
|
entity.updateDynamicGameEventListener(DynamicGameEventListener::remove);
|
|
+ // CraftBukkit start
|
|
+ entity.valid = false;
|
|
+ if (!(entity instanceof ServerPlayer)) {
|
|
+ for (ServerPlayer player : ServerLevel.this.players) {
|
|
+ player.getBukkitEntity().onEntityRemove(entity);
|
|
+ }
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+ new com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent(entity.getBukkitEntity(), ServerLevel.this.getWorld()).callEvent(); // Paper - fire while valid
|
|
}
|
|
|
|
public void onSectionChange(Entity entity) {
|