aboutsummaryrefslogtreecommitdiffhomepage
path: root/patch-remap/mache-vineflower/net/minecraft/server/level/ServerLevel.java.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patch-remap/mache-vineflower/net/minecraft/server/level/ServerLevel.java.patch')
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/server/level/ServerLevel.java.patch2379
1 files changed, 2379 insertions, 0 deletions
diff --git a/patch-remap/mache-vineflower/net/minecraft/server/level/ServerLevel.java.patch b/patch-remap/mache-vineflower/net/minecraft/server/level/ServerLevel.java.patch
new file mode 100644
index 0000000000..7c5161a5ef
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/server/level/ServerLevel.java.patch
@@ -0,0 +1,2379 @@
+--- a/net/minecraft/server/level/ServerLevel.java
++++ b/net/minecraft/server/level/ServerLevel.java
+@@ -9,17 +9,20 @@
+ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+ import it.unimi.dsi.fastutil.longs.LongSet;
+ import it.unimi.dsi.fastutil.longs.LongSets;
++import it.unimi.dsi.fastutil.objects.Object2IntMap.Entry;
+ import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
+ import it.unimi.dsi.fastutil.objects.ObjectArrayList;
++import it.unimi.dsi.fastutil.objects.ObjectIterator;
+ import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
+ import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
+-import it.unimi.dsi.fastutil.objects.Object2IntMap.Entry;
++import java.io.BufferedWriter;
+ import java.io.IOException;
+ import java.io.Writer;
+ import java.nio.file.Files;
+ import java.nio.file.Path;
+ import java.util.ArrayList;
+ import java.util.Comparator;
++import java.util.Iterator;
+ import java.util.List;
+ import java.util.Locale;
+ import java.util.Objects;
+@@ -46,13 +49,13 @@
+ import net.minecraft.core.registries.BuiltInRegistries;
+ import net.minecraft.core.registries.Registries;
+ import net.minecraft.network.chat.Component;
++import net.minecraft.network.chat.MutableComponent;
+ import net.minecraft.network.protocol.Packet;
+ import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket;
+ import net.minecraft.network.protocol.game.ClientboundBlockEventPacket;
+ 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;
+@@ -98,7 +101,7 @@
+ import net.minecraft.world.entity.animal.horse.SkeletonHorse;
+ import net.minecraft.world.entity.boss.EnderDragonPart;
+ import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
+-import net.minecraft.world.entity.npc.Npc;
++import net.minecraft.world.entity.npc.NPC;
+ import net.minecraft.world.entity.player.Player;
+ import net.minecraft.world.entity.raid.Raid;
+ import net.minecraft.world.entity.raid.Raids;
+@@ -116,11 +119,12 @@
+ 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;
+ import net.minecraft.world.level.block.entity.TickingBlockEntity;
+-import net.minecraft.world.level.block.state.BlockState;
++import net.minecraft.world.level.block.state.IBlockData;
+ import net.minecraft.world.level.chunk.ChunkAccess;
+ import net.minecraft.world.level.chunk.ChunkGenerator;
+ import net.minecraft.world.level.chunk.LevelChunk;
+@@ -138,7 +142,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;
+@@ -150,7 +156,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;
+@@ -158,8 +164,22 @@
+ import net.minecraft.world.phys.shapes.VoxelShape;
+ import net.minecraft.world.ticks.LevelTicks;
+ import org.slf4j.Logger;
++import org.bukkit.Bukkit;
++import org.bukkit.Location;
++import org.bukkit.WeatherType;
++import org.bukkit.craftbukkit.event.CraftEventFactory;
++import org.bukkit.craftbukkit.generator.CustomWorldChunkManager;
++import org.bukkit.craftbukkit.util.CraftNamespacedKey;
++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.GenericGameEvent;
++import org.bukkit.event.world.TimeSkipEvent;
++// CraftBukkit end
+
+ public class ServerLevel extends Level implements WorldGenLevel {
++
+ public static final BlockPos END_SPAWN_POINT = new BlockPos(100, 50, 0);
+ public static final IntProvider RAIN_DELAY = UniformInt.of(12000, 180000);
+ public static final IntProvider RAIN_DURATION = UniformInt.of(12000, 24000);
+@@ -168,121 +188,130 @@
+ private static final Logger LOGGER = LogUtils.getLogger();
+ private static final int EMPTY_TIME_NO_TICK = 300;
+ private static final int MAX_SCHEDULED_TICKS_PER_TICK = 65536;
+- final List<ServerPlayer> players = Lists.newArrayList();
++ final List<ServerPlayer> players;
+ private final ServerChunkCache chunkSource;
+ private final MinecraftServer server;
+- private final ServerLevelData serverLevelData;
+- final EntityTickList entityTickList = new EntityTickList();
+- private final PersistentEntitySectionManager<Entity> entityManager;
++ public final PrimaryLevelData serverLevelData; // CraftBukkit - type
++ final EntityTickList entityTickList;
++ public final PersistentEntitySectionManager<Entity> entityManager;
+ private final GameEventDispatcher gameEventDispatcher;
+ public boolean noSave;
+ private final SleepStatus sleepStatus;
+ private int emptyTime;
+ private final PortalForcer portalForcer;
+- private final LevelTicks<Block> blockTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded, this.getProfilerSupplier());
+- private final LevelTicks<Fluid> fluidTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded, this.getProfilerSupplier());
+- final Set<Mob> navigatingMobs = new ObjectOpenHashSet<>();
++ private final LevelTicks<Block> blockTicks;
++ private final LevelTicks<Fluid> fluidTicks;
++ final Set<Mob> navigatingMobs;
+ volatile boolean isUpdatingNavigations;
+ protected final Raids raids;
+- private final ObjectLinkedOpenHashSet<BlockEventData> blockEvents = new ObjectLinkedOpenHashSet<>();
+- private final List<BlockEventData> blockEventsToReschedule = new ArrayList<>(64);
++ private final ObjectLinkedOpenHashSet<BlockEventData> blockEvents;
++ private final List<BlockEventData> blockEventsToReschedule;
+ private boolean handlingTick;
+ private final List<CustomSpawner> customSpawners;
+ @Nullable
+ private EndDragonFight dragonFight;
+- final Int2ObjectMap<EnderDragonPart> dragonParts = new Int2ObjectOpenHashMap<>();
++ final Int2ObjectMap<EnderDragonPart> dragonParts;
+ private final StructureManager structureManager;
+ private final StructureCheck structureCheck;
+ private final boolean tickTime;
+ private final RandomSequences randomSequences;
+
+- public ServerLevel(
+- MinecraftServer server,
+- Executor dispatcher,
+- LevelStorageSource.LevelStorageAccess levelStorageAccess,
+- ServerLevelData serverLevelData,
+- ResourceKey<Level> dimension,
+- LevelStem levelStem,
+- ChunkProgressListener progressListener,
+- boolean isDebug,
+- long biomeZoomSeed,
+- List<CustomSpawner> customSpawners,
+- boolean tickTime,
+- @Nullable RandomSequences randomSequences
+- ) {
+- super(
+- serverLevelData,
+- dimension,
+- server.registryAccess(),
+- levelStem.type(),
+- server::getProfiler,
+- false,
+- isDebug,
+- biomeZoomSeed,
+- server.getMaxChainedNeighborUpdates()
+- );
+- this.tickTime = tickTime;
+- this.server = server;
+- this.customSpawners = customSpawners;
+- this.serverLevelData = serverLevelData;
+- ChunkGenerator chunkGenerator = levelStem.generator();
+- boolean flag = server.forceSynchronousWrites();
+- DataFixer fixerUpper = server.getFixerUpper();
+- EntityPersistentStorage<Entity> entityPersistentStorage = new EntityStorage(
+- this, levelStorageAccess.getDimensionPath(dimension).resolve("entities"), fixerUpper, flag, server
+- );
+- this.entityManager = new PersistentEntitySectionManager<>(Entity.class, new ServerLevel.EntityCallbacks(), entityPersistentStorage);
+- this.chunkSource = new ServerChunkCache(
+- this,
+- levelStorageAccess,
+- fixerUpper,
+- server.getStructureManager(),
+- dispatcher,
+- chunkGenerator,
+- server.getPlayerList().getViewDistance(),
+- server.getPlayerList().getSimulationDistance(),
+- flag,
+- progressListener,
+- this.entityManager::updateChunkStatus,
+- () -> server.overworld().getDataStorage()
+- );
++ // CraftBukkit start
++ public final LevelStorageSource.LevelStorageAccess convertable;
++ public final UUID uuid;
++
++ public LevelChunk getChunkIfLoaded(int x, int z) {
++ return this.chunkSource.getChunk(x, z, false);
++ }
++
++ @Override
++ public ResourceKey<LevelStem> getTypeKey() {
++ return convertable.dimensionType;
++ }
++
++ // 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) {
++ // IRegistryCustom.Dimension iregistrycustom_dimension = minecraftserver.registryAccess(); // CraftBukkit - decompile error
++ // Holder holder = worlddimension.type(); // CraftBukkit - decompile error
++
++ // Objects.requireNonNull(minecraftserver); // CraftBukkit - decompile error
++ super(iworlddataserver, resourcekey, minecraftserver.registryAccess(), worlddimension.type(), minecraftserver::getProfiler, false, flag, i, minecraftserver.getMaxChainedNeighborUpdates(), gen, biomeProvider, env);
++ this.pvpMode = minecraftserver.isPvpAllowed();
++ convertable = convertable_conversionsession;
++ uuid = WorldUUID.getUUID(convertable_conversionsession.levelDirectory.path().toFile());
++ // CraftBukkit end
++ this.players = Lists.newArrayList();
++ this.entityTickList = new EntityTickList();
++ this.blockTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded, this.getProfilerSupplier());
++ this.fluidTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded, this.getProfilerSupplier());
++ this.navigatingMobs = new ObjectOpenHashSet();
++ this.blockEvents = new ObjectLinkedOpenHashSet();
++ this.blockEventsToReschedule = new ArrayList(64);
++ this.dragonParts = new Int2ObjectOpenHashMap();
++ this.tickTime = flag1;
++ this.server = minecraftserver;
++ this.customSpawners = list;
++ this.serverLevelData = iworlddataserver;
++ ChunkGenerator chunkgenerator = worlddimension.generator();
++ // CraftBukkit start
++ serverLevelData.setWorld(this);
++
++ if (biomeProvider != null) {
++ BiomeSource worldChunkManager = new CustomWorldChunkManager(getWorld(), biomeProvider, server.registryAccess().registryOrThrow(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(this, convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), datafixer, flag2, minecraftserver);
++
++ this.entityManager = new PersistentEntitySectionManager<>(Entity.class, new ServerLevel.a(), entitypersistentstorage);
++ StructureTemplateManager structuretemplatemanager = minecraftserver.getStructureManager();
++ int j = minecraftserver.getPlayerList().getViewDistance();
++ int k = minecraftserver.getPlayerList().getSimulationDistance();
++ PersistentEntitySectionManager persistententitysectionmanager = this.entityManager;
++
++ Objects.requireNonNull(this.entityManager);
++ 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.raids = this.getDataStorage().computeIfAbsent(Raids.factory(this), Raids.getFileId(this.dimensionTypeRegistration()));
+- if (!server.isSingleplayer()) {
+- serverLevelData.setGameType(server.getDefaultGameType());
++ this.getWorldBorder().setAbsoluteMaxSize(minecraftserver.getAbsoluteMaxWorldSize());
++ this.raids = (Raids) this.getDataStorage().computeIfAbsent(Raids.factory(this), Raids.getFileId(this.dimensionTypeRegistration()));
++ if (!minecraftserver.isSingleplayer()) {
++ iworlddataserver.setGameType(minecraftserver.getDefaultGameType());
+ }
+
+- long l = server.getWorldData().worldGenOptions().seed();
+- this.structureCheck = new StructureCheck(
+- this.chunkSource.chunkScanner(),
+- this.registryAccess(),
+- server.getStructureManager(),
+- dimension,
+- chunkGenerator,
+- this.chunkSource.randomState(),
+- this,
+- chunkGenerator.getBiomeSource(),
+- l,
+- fixerUpper
+- );
+- 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());
++ long l = minecraftserver.getWorldData().worldGenOptions().seed();
++
++ this.structureCheck = new StructureCheck(this.chunkSource.chunkScanner(), this.registryAccess(), minecraftserver.getStructureManager(), resourcekey, chunkgenerator, this.chunkSource.randomState(), this, chunkgenerator.getBiomeSource(), l, datafixer);
++ this.structureManager = new StructureManager(this, this.serverLevelData.worldGenOptions(), 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 = Objects.requireNonNullElseGet(
+- randomSequences, () -> this.getDataStorage().computeIfAbsent(RandomSequences.factory(l), "random_sequences")
+- );
++ this.randomSequences = (RandomSequences) Objects.requireNonNullElseGet(randomsequences, () -> {
++ return (RandomSequences) this.getDataStorage().computeIfAbsent(RandomSequences.factory(l), "random_sequences");
++ });
++ this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit
+ }
+
++ /** @deprecated */
+ @Deprecated
+ @VisibleForTesting
+ public void setDragonFight(@Nullable EndDragonFight dragonFight) {
+@@ -307,25 +336,37 @@
+ }
+
+ public void tick(BooleanSupplier hasTimeLeft) {
+- ProfilerFiller profiler = this.getProfiler();
++ ProfilerFiller gameprofilerfiller = this.getProfiler();
++
+ this.handlingTick = true;
+- TickRateManager tickRateManager = this.tickRateManager();
+- boolean flag = tickRateManager.runsNormally();
++ TickRateManager tickratemanager = this.tickRateManager();
++ boolean flag = tickratemanager.runsNormally();
++
+ if (flag) {
+- profiler.push("world border");
++ gameprofilerfiller.push("world border");
+ this.getWorldBorder().tick();
+- profiler.popPush("weather");
++ gameprofilerfiller.popPush("weather");
+ this.advanceWeatherCycle();
+ }
+
+- int _int = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE);
+- if (this.sleepStatus.areEnoughSleeping(_int) && this.sleepStatus.areEnoughDeepSleeping(_int, this.players)) {
++ int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE);
++ 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)) {
+- long l = this.levelData.getDayTime() + 24000L;
+- this.setDayTime(l - l % 24000L);
++ 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();
+ }
+@@ -336,75 +377,77 @@
+ this.tickTime();
+ }
+
+- profiler.popPush("tickPending");
++ gameprofilerfiller.popPush("tickPending");
+ if (!this.isDebug() && flag) {
+- long l = this.getGameTime();
+- profiler.push("blockTicks");
+- this.blockTicks.tick(l, 65536, this::tickBlock);
+- profiler.popPush("fluidTicks");
+- this.fluidTicks.tick(l, 65536, this::tickFluid);
+- profiler.pop();
++ j = this.getGameTime();
++ gameprofilerfiller.push("blockTicks");
++ this.blockTicks.tick(j, 65536, this::tickBlock);
++ gameprofilerfiller.popPush("fluidTicks");
++ this.fluidTicks.tick(j, 65536, this::tickFluid);
++ gameprofilerfiller.pop();
+ }
+
+- profiler.popPush("raid");
++ gameprofilerfiller.popPush("raid");
+ if (flag) {
+ this.raids.tick();
+ }
+
+- profiler.popPush("chunkSource");
++ gameprofilerfiller.popPush("chunkSource");
+ this.getChunkSource().tick(hasTimeLeft, true);
+- profiler.popPush("blockEvents");
++ gameprofilerfiller.popPush("blockEvents");
+ if (flag) {
+ this.runBlockEvents();
+ }
+
+ this.handlingTick = false;
+- profiler.pop();
+- boolean flag1 = !this.players.isEmpty() || !this.getForcedChunks().isEmpty();
++ gameprofilerfiller.pop();
++ 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();
+ }
+
+ if (flag1 || this.emptyTime++ < 300) {
+- profiler.push("entities");
++ gameprofilerfiller.push("entities");
+ if (this.dragonFight != null && flag) {
+- profiler.push("dragonFight");
++ gameprofilerfiller.push("dragonFight");
+ this.dragonFight.tick();
+- profiler.pop();
++ gameprofilerfiller.pop();
+ }
+
+- this.entityTickList.forEach(entity -> {
++ this.entityTickList.forEach((entity) -> {
+ if (!entity.isRemoved()) {
+- if (this.shouldDiscardEntity(entity)) {
++ if (false && this.shouldDiscardEntity(entity)) { // CraftBukkit - We prevent spawning in general, so this butchering is not needed
+ entity.discard();
+- } else if (!tickRateManager.isEntityFrozen(entity)) {
+- profiler.push("checkDespawn");
++ } else if (!tickratemanager.isEntityFrozen(entity)) {
++ gameprofilerfiller.push("checkDespawn");
+ entity.checkDespawn();
+- profiler.pop();
++ gameprofilerfiller.pop();
+ if (this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(entity.chunkPosition().toLong())) {
+- Entity vehicle = entity.getVehicle();
+- if (vehicle != null) {
+- if (!vehicle.isRemoved() && vehicle.hasPassenger(entity)) {
++ Entity entity1 = entity.getVehicle();
++
++ if (entity1 != null) {
++ if (!entity1.isRemoved() && entity1.hasPassenger(entity)) {
+ return;
+ }
+
+ entity.stopRiding();
+ }
+
+- profiler.push("tick");
++ gameprofilerfiller.push("tick");
+ this.guardEntityTick(this::tickNonPassenger, entity);
+- profiler.pop();
++ gameprofilerfiller.pop();
+ }
+ }
+ }
+ });
+- profiler.pop();
++ gameprofilerfiller.pop();
+ this.tickBlockEntities();
+ }
+
+- profiler.push("entityManagement");
++ gameprofilerfiller.push("entityManagement");
+ this.entityManager.tick();
+- profiler.pop();
++ gameprofilerfiller.pop();
+ }
+
+ @Override
+@@ -414,12 +457,14 @@
+
+ protected void tickTime() {
+ if (this.tickTime) {
+- long l = this.levelData.getGameTime() + 1L;
+- this.serverLevelData.setGameTime(l);
+- this.serverLevelData.getScheduledEvents().tick(this.server, l);
++ long i = this.levelData.getGameTime() + 1L;
++
++ this.serverLevelData.setGameTime(i);
++ this.serverLevelData.getScheduledEvents().tick(this.server, i);
+ if (this.levelData.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)) {
+ this.setDayTime(this.levelData.getDayTime() + 1L);
+ }
++
+ }
+ }
+
+@@ -428,159 +473,180 @@
+ }
+
+ public void tickCustomSpawners(boolean spawnEnemies, boolean spawnFriendlies) {
+- for (CustomSpawner customSpawner : this.customSpawners) {
+- customSpawner.tick(this, spawnEnemies, spawnFriendlies);
++ Iterator iterator = this.customSpawners.iterator();
++
++ while (iterator.hasNext()) {
++ CustomSpawner mobspawner = (CustomSpawner) iterator.next();
++
++ mobspawner.tick(this, spawnEnemies, spawnFriendlies);
+ }
++
+ }
+
+ private boolean shouldDiscardEntity(Entity entity) {
+- return !this.server.isSpawningAnimals() && (entity instanceof Animal || entity instanceof WaterAnimal)
+- || !this.server.areNpcsEnabled() && entity instanceof Npc;
++ return !this.server.isSpawningAnimals() && (entity instanceof Animal || entity instanceof WaterAnimal) ? true : !this.server.areNpcsEnabled() && entity instanceof NPC;
+ }
+
+ private void wakeUpAllPlayers() {
+ this.sleepStatus.removeAllSleepers();
+- this.players.stream().filter(LivingEntity::isSleeping).collect(Collectors.toList()).forEach(serverPlayer -> serverPlayer.stopSleepInBed(false, false));
++ (this.players.stream().filter(LivingEntity::isSleeping).collect(Collectors.toList())).forEach((entityplayer) -> { // CraftBukkit - decompile error
++ entityplayer.stopSleepInBed(false, false);
++ });
+ }
+
+ public void tickChunk(LevelChunk chunk, int randomTickSpeed) {
+- ChunkPos pos = chunk.getPos();
+- boolean isRaining = this.isRaining();
+- int minBlockX = pos.getMinBlockX();
+- int minBlockZ = pos.getMinBlockZ();
+- ProfilerFiller profiler = this.getProfiler();
+- profiler.push("thunder");
+- if (isRaining && this.isThundering() && this.random.nextInt(100000) == 0) {
+- BlockPos blockPos = this.findLightningTargetAround(this.getBlockRandomPos(minBlockX, 0, minBlockZ, 15));
+- if (this.isRainingAt(blockPos)) {
+- DifficultyInstance currentDifficultyAt = this.getCurrentDifficultyAt(blockPos);
+- boolean flag = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)
+- && this.random.nextDouble() < (double)currentDifficultyAt.getEffectiveDifficulty() * 0.01
+- && !this.getBlockState(blockPos.below()).is(Blocks.LIGHTNING_ROD);
+- if (flag) {
+- SkeletonHorse skeletonHorse = EntityType.SKELETON_HORSE.create(this);
+- if (skeletonHorse != null) {
+- skeletonHorse.setTrap(true);
+- skeletonHorse.setAge(0);
+- skeletonHorse.setPos((double)blockPos.getX(), (double)blockPos.getY(), (double)blockPos.getZ());
+- this.addFreshEntity(skeletonHorse);
++ ChunkPos chunkcoordintpair = chunk.getPos();
++ boolean flag = this.isRaining();
++ int j = chunkcoordintpair.getMinBlockX();
++ int k = chunkcoordintpair.getMinBlockZ();
++ ProfilerFiller gameprofilerfiller = this.getProfiler();
++
++ gameprofilerfiller.push("thunder");
++ if (flag && this.isThundering() && this.random.nextInt(100000) == 0) {
++ 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);
++
++ if (flag1) {
++ SkeletonHorse entityhorseskeleton = (SkeletonHorse) EntityType.SKELETON_HORSE.create(this);
++
++ if (entityhorseskeleton != null) {
++ entityhorseskeleton.setTrap(true);
++ entityhorseskeleton.setAge(0);
++ entityhorseskeleton.setPos((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ());
++ this.addFreshEntity(entityhorseskeleton, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); // CraftBukkit
+ }
+ }
+
+- LightningBolt lightningBolt = EntityType.LIGHTNING_BOLT.create(this);
+- if (lightningBolt != null) {
+- lightningBolt.moveTo(Vec3.atBottomCenterOf(blockPos));
+- lightningBolt.setVisualOnly(flag);
+- this.addFreshEntity(lightningBolt);
++ LightningBolt entitylightning = (LightningBolt) EntityType.LIGHTNING_BOLT.create(this);
++
++ if (entitylightning != null) {
++ entitylightning.moveTo(Vec3.atBottomCenterOf(blockposition));
++ entitylightning.setVisualOnly(flag1);
++ this.strikeLightning(entitylightning, org.bukkit.event.weather.LightningStrikeEvent.Cause.WEATHER); // CraftBukkit
+ }
+ }
+ }
+
+- profiler.popPush("iceandsnow");
++ gameprofilerfiller.popPush("iceandsnow");
+
+- for (int i = 0; i < randomTickSpeed; i++) {
++ for (int l = 0; l < randomTickSpeed; ++l) {
+ if (this.random.nextInt(48) == 0) {
+- this.tickPrecipitation(this.getBlockRandomPos(minBlockX, 0, minBlockZ, 15));
++ this.tickPrecipitation(this.getBlockRandomPos(j, 0, k, 15));
+ }
+ }
+
+- profiler.popPush("tickBlocks");
++ gameprofilerfiller.popPush("tickBlocks");
+ if (randomTickSpeed > 0) {
+- LevelChunkSection[] sections = chunk.getSections();
++ LevelChunkSection[] achunksection = chunk.getSections();
+
+- for (int i1 = 0; i1 < sections.length; i1++) {
+- LevelChunkSection levelChunkSection = sections[i1];
+- if (levelChunkSection.isRandomlyTicking()) {
+- int sectionYFromSectionIndex = chunk.getSectionYFromSectionIndex(i1);
+- int i2 = SectionPos.sectionToBlockCoord(sectionYFromSectionIndex);
++ for (int i1 = 0; i1 < achunksection.length; ++i1) {
++ LevelChunkSection chunksection = achunksection[i1];
+
+- for (int i3 = 0; i3 < randomTickSpeed; i3++) {
+- BlockPos blockRandomPos = this.getBlockRandomPos(minBlockX, i2, minBlockZ, 15);
+- profiler.push("randomTick");
+- BlockState blockState = levelChunkSection.getBlockState(
+- blockRandomPos.getX() - minBlockX, blockRandomPos.getY() - i2, blockRandomPos.getZ() - minBlockZ
+- );
+- if (blockState.isRandomlyTicking()) {
+- blockState.randomTick(this, blockRandomPos, this.random);
++ if (chunksection.isRandomlyTicking()) {
++ int j1 = chunk.getSectionYFromSectionIndex(i1);
++ int k1 = SectionPos.sectionToBlockCoord(j1);
++
++ for (int l1 = 0; l1 < randomTickSpeed; ++l1) {
++ BlockPos blockposition1 = this.getBlockRandomPos(j, k1, k, 15);
++
++ gameprofilerfiller.push("randomTick");
++ IBlockData iblockdata = chunksection.getBlockState(blockposition1.getX() - j, blockposition1.getY() - k1, blockposition1.getZ() - k);
++
++ if (iblockdata.isRandomlyTicking()) {
++ iblockdata.randomTick(this, blockposition1, this.random);
+ }
+
+- FluidState fluidState = blockState.getFluidState();
+- if (fluidState.isRandomlyTicking()) {
+- fluidState.randomTick(this, blockRandomPos, this.random);
++ FluidState fluid = iblockdata.getFluidState();
++
++ if (fluid.isRandomlyTicking()) {
++ fluid.randomTick(this, blockposition1, this.random);
+ }
+
+- profiler.pop();
++ gameprofilerfiller.pop();
+ }
+ }
+ }
+ }
+
+- profiler.pop();
++ gameprofilerfiller.pop();
+ }
+
+ @VisibleForTesting
+- public void tickPrecipitation(BlockPos blockPos) {
+- BlockPos heightmapPos = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, blockPos);
+- BlockPos blockPos1 = heightmapPos.below();
+- Biome biome = this.getBiome(heightmapPos).value();
+- if (biome.shouldFreeze(this, blockPos1)) {
+- this.setBlockAndUpdate(blockPos1, Blocks.ICE.defaultBlockState());
++ public void tickPrecipitation(BlockPos blockposition) {
++ BlockPos blockposition1 = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, blockposition);
++ BlockPos blockposition2 = blockposition1.below();
++ Biome biomebase = (Biome) this.getBiome(blockposition1).value();
++
++ if (biomebase.shouldFreeze(this, blockposition2)) {
++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition2, Blocks.ICE.defaultBlockState(), null); // CraftBukkit
+ }
+
+ if (this.isRaining()) {
+- int _int = this.getGameRules().getInt(GameRules.RULE_SNOW_ACCUMULATION_HEIGHT);
+- if (_int > 0 && biome.shouldSnow(this, heightmapPos)) {
+- BlockState blockState = this.getBlockState(heightmapPos);
+- if (blockState.is(Blocks.SNOW)) {
+- int i = blockState.getValue(SnowLayerBlock.LAYERS);
+- if (i < Math.min(_int, 8)) {
+- BlockState blockState1 = blockState.setValue(SnowLayerBlock.LAYERS, Integer.valueOf(i + 1));
+- Block.pushEntitiesUp(blockState, blockState1, this, heightmapPos);
+- this.setBlockAndUpdate(heightmapPos, blockState1);
++ int i = this.getGameRules().getInt(GameRules.RULE_SNOW_ACCUMULATION_HEIGHT);
++
++ if (i > 0 && biomebase.shouldSnow(this, blockposition1)) {
++ IBlockData iblockdata = this.getBlockState(blockposition1);
++
++ if (iblockdata.is(Blocks.SNOW)) {
++ int j = (Integer) iblockdata.getValue(SnowLayerBlock.LAYERS);
++
++ if (j < Math.min(i, 8)) {
++ IBlockData iblockdata1 = (IBlockData) iblockdata.setValue(SnowLayerBlock.LAYERS, j + 1);
++
++ Block.pushEntitiesUp(iblockdata, iblockdata1, this, blockposition1);
++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition1, iblockdata1, null); // CraftBukkit
+ }
+ } else {
+- this.setBlockAndUpdate(heightmapPos, Blocks.SNOW.defaultBlockState());
++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition1, Blocks.SNOW.defaultBlockState(), null); // CraftBukkit
+ }
+ }
+
+- Biome.Precipitation precipitationAt = biome.getPrecipitationAt(blockPos1);
+- if (precipitationAt != Biome.Precipitation.NONE) {
+- BlockState blockState2 = this.getBlockState(blockPos1);
+- blockState2.getBlock().handlePrecipitation(blockState2, this, blockPos1, precipitationAt);
++ Biome.Precipitation biomebase_precipitation = biomebase.getPrecipitationAt(blockposition2);
++
++ if (biomebase_precipitation != Biome.Precipitation.NONE) {
++ IBlockData iblockdata2 = this.getBlockState(blockposition2);
++
++ iblockdata2.getBlock().handlePrecipitation(iblockdata2, this, blockposition2, biomebase_precipitation);
+ }
+ }
++
+ }
+
+ private Optional<BlockPos> findLightningRod(BlockPos pos) {
+- Optional<BlockPos> optional = this.getPoiManager()
+- .findClosest(
+- holder -> holder.is(PoiTypes.LIGHTNING_ROD),
+- blockPos -> blockPos.getY() == this.getHeight(Heightmap.Types.WORLD_SURFACE, blockPos.getX(), blockPos.getZ()) - 1,
+- pos,
+- 128,
+- PoiManager.Occupancy.ANY
+- );
+- return optional.map(blockPos -> blockPos.above(1));
++ Optional<BlockPos> optional = this.getPoiManager().findClosest((holder) -> {
++ return holder.is(PoiTypes.LIGHTNING_ROD);
++ }, (blockposition1) -> {
++ return blockposition1.getY() == this.getHeight(Heightmap.Types.WORLD_SURFACE, blockposition1.getX(), blockposition1.getZ()) - 1;
++ }, pos, 128, PoiManager.Occupancy.ANY);
++
++ return optional.map((blockposition1) -> {
++ return blockposition1.above(1);
++ });
+ }
+
+ protected BlockPos findLightningTargetAround(BlockPos pos) {
+- BlockPos heightmapPos = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pos);
+- Optional<BlockPos> optional = this.findLightningRod(heightmapPos);
++ BlockPos blockposition1 = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pos);
++ Optional<BlockPos> optional = this.findLightningRod(blockposition1);
++
+ if (optional.isPresent()) {
+- return optional.get();
++ return (BlockPos) optional.get();
+ } else {
+- AABB aABB = AABB.encapsulatingFullBlocks(heightmapPos, new BlockPos(heightmapPos.atY(this.getMaxBuildHeight()))).inflate(3.0);
+- List<LivingEntity> entitiesOfClass = this.getEntitiesOfClass(
+- LivingEntity.class, aABB, livingEntity -> livingEntity != null && livingEntity.isAlive() && this.canSeeSky(livingEntity.blockPosition())
+- );
+- if (!entitiesOfClass.isEmpty()) {
+- return entitiesOfClass.get(this.random.nextInt(entitiesOfClass.size())).blockPosition();
++ AABB axisalignedbb = AABB.encapsulatingFullBlocks(blockposition1, new BlockPos(blockposition1.atY(this.getMaxBuildHeight()))).inflate(3.0D);
++ List<LivingEntity> list = this.getEntitiesOfClass(LivingEntity.class, axisalignedbb, (entityliving) -> {
++ return entityliving != null && entityliving.isAlive() && this.canSeeSky(entityliving.blockPosition());
++ });
++
++ if (!list.isEmpty()) {
++ return ((LivingEntity) list.get(this.random.nextInt(list.size()))).blockPosition();
+ } else {
+- if (heightmapPos.getY() == this.getMinBuildHeight() - 1) {
+- heightmapPos = heightmapPos.above(2);
++ if (blockposition1.getY() == this.getMinBuildHeight() - 1) {
++ blockposition1 = blockposition1.above(2);
+ }
+
+- return heightmapPos;
++ return blockposition1;
+ }
+ }
+ }
+@@ -596,17 +662,23 @@
+ private void announceSleepStatus() {
+ if (this.canSleepThroughNights()) {
+ if (!this.getServer().isSingleplayer() || this.getServer().isPublished()) {
+- int _int = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE);
+- Component component;
+- if (this.sleepStatus.areEnoughSleeping(_int)) {
+- component = Component.translatable("sleep.skipping_night");
++ int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE);
++ MutableComponent ichatmutablecomponent;
++
++ if (this.sleepStatus.areEnoughSleeping(i)) {
++ ichatmutablecomponent = Component.translatable("sleep.skipping_night");
+ } else {
+- component = Component.translatable("sleep.players_sleeping", this.sleepStatus.amountSleeping(), this.sleepStatus.sleepersNeeded(_int));
++ ichatmutablecomponent = Component.translatable("sleep.players_sleeping", this.sleepStatus.amountSleeping(), this.sleepStatus.sleepersNeeded(i));
+ }
+
+- for (ServerPlayer serverPlayer : this.players) {
+- serverPlayer.displayClientMessage(component, true);
++ Iterator iterator = this.players.iterator();
++
++ while (iterator.hasNext()) {
++ ServerPlayer entityplayer = (ServerPlayer) iterator.next();
++
++ entityplayer.displayClientMessage(ichatmutablecomponent, true);
+ }
++
+ }
+ }
+ }
+@@ -615,6 +687,7 @@
+ if (!this.players.isEmpty() && this.sleepStatus.update(this.players)) {
+ this.announceSleepStatus();
+ }
++
+ }
+
+ @Override
+@@ -623,47 +696,51 @@
+ }
+
+ private void advanceWeatherCycle() {
+- boolean isRaining = this.isRaining();
++ boolean flag = this.isRaining();
++
+ if (this.dimensionType().hasSkyLight()) {
+ if (this.getGameRules().getBoolean(GameRules.RULE_WEATHER_CYCLE)) {
+- int clearWeatherTime = this.serverLevelData.getClearWeatherTime();
+- int thunderTime = this.serverLevelData.getThunderTime();
+- int rainTime = this.serverLevelData.getRainTime();
+- boolean isThundering = this.levelData.isThundering();
+- boolean isRaining1 = this.levelData.isRaining();
+- if (clearWeatherTime > 0) {
+- clearWeatherTime--;
+- thunderTime = isThundering ? 0 : 1;
+- rainTime = isRaining1 ? 0 : 1;
+- isThundering = false;
+- isRaining1 = false;
++ int i = this.serverLevelData.getClearWeatherTime();
++ int j = this.serverLevelData.getThunderTime();
++ int k = this.serverLevelData.getRainTime();
++ boolean flag1 = this.levelData.isThundering();
++ boolean flag2 = this.levelData.isRaining();
++
++ if (i > 0) {
++ --i;
++ j = flag1 ? 0 : 1;
++ k = flag2 ? 0 : 1;
++ flag1 = false;
++ flag2 = false;
+ } else {
+- if (thunderTime > 0) {
+- if (--thunderTime == 0) {
+- isThundering = !isThundering;
++ if (j > 0) {
++ --j;
++ if (j == 0) {
++ flag1 = !flag1;
+ }
+- } else if (isThundering) {
+- thunderTime = THUNDER_DURATION.sample(this.random);
++ } else if (flag1) {
++ j = ServerLevel.THUNDER_DURATION.sample(this.random);
+ } else {
+- thunderTime = THUNDER_DELAY.sample(this.random);
++ j = ServerLevel.THUNDER_DELAY.sample(this.random);
+ }
+
+- if (rainTime > 0) {
+- if (--rainTime == 0) {
+- isRaining1 = !isRaining1;
++ if (k > 0) {
++ --k;
++ if (k == 0) {
++ flag2 = !flag2;
+ }
+- } else if (isRaining1) {
+- rainTime = RAIN_DURATION.sample(this.random);
++ } else if (flag2) {
++ k = ServerLevel.RAIN_DURATION.sample(this.random);
+ } else {
+- rainTime = RAIN_DELAY.sample(this.random);
++ k = ServerLevel.RAIN_DELAY.sample(this.random);
+ }
+ }
+
+- this.serverLevelData.setThunderTime(thunderTime);
+- this.serverLevelData.setRainTime(rainTime);
+- this.serverLevelData.setClearWeatherTime(clearWeatherTime);
+- this.serverLevelData.setThundering(isThundering);
+- this.serverLevelData.setRaining(isRaining1);
++ this.serverLevelData.setThunderTime(j);
++ this.serverLevelData.setRainTime(k);
++ this.serverLevelData.setClearWeatherTime(i);
++ this.serverLevelData.setThundering(flag1);
++ this.serverLevelData.setRaining(flag2);
+ }
+
+ this.oThunderLevel = this.thunderLevel;
+@@ -684,36 +761,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 (isRaining != this.isRaining()) {
+- if (isRaining) {
+- this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.STOP_RAINING, 0.0F));
++ if (flag != this.isRaining()) {
++ if (flag) {
++ 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() {
+@@ -721,48 +829,70 @@
+ }
+
+ private void tickFluid(BlockPos pos, Fluid fluid) {
+- FluidState fluidState = this.getFluidState(pos);
+- if (fluidState.is(fluid)) {
+- fluidState.tick(this, pos);
++ FluidState fluid = this.getFluidState(pos);
++
++ if (fluid.is(fluid)) {
++ fluid.tick(this, pos);
+ }
++
+ }
+
+ private void tickBlock(BlockPos pos, Block block) {
+- BlockState blockState = this.getBlockState(pos);
+- if (blockState.is(block)) {
+- blockState.tick(this, pos, this.random);
++ IBlockData iblockdata = this.getBlockState(pos);
++
++ if (iblockdata.is(block)) {
++ iblockdata.tick(this, pos, this.random);
+ }
++
+ }
+
+ public void tickNonPassenger(Entity entity) {
+ entity.setOldPosAndRot();
+- ProfilerFiller profiler = this.getProfiler();
+- entity.tickCount++;
+- this.getProfiler().push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString());
+- profiler.incrementCounter("tickNonPassenger");
++ ProfilerFiller gameprofilerfiller = this.getProfiler();
++
++ ++entity.tickCount;
++ this.getProfiler().push(() -> {
++ return BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString();
++ });
++ gameprofilerfiller.incrementCounter("tickNonPassenger");
+ entity.tick();
++ entity.postTick(); // CraftBukkit
+ this.getProfiler().pop();
++ Iterator iterator = entity.getPassengers().iterator();
+
+- for (Entity entity1 : entity.getPassengers()) {
++ while (iterator.hasNext()) {
++ Entity entity1 = (Entity) iterator.next();
++
+ this.tickPassenger(entity, entity1);
+ }
++
+ }
+
+ private void tickPassenger(Entity ridingEntity, Entity passengerEntity) {
+- if (passengerEntity.isRemoved() || passengerEntity.getVehicle() != ridingEntity) {
+- passengerEntity.stopRiding();
+- } else if (passengerEntity instanceof Player || this.entityTickList.contains(passengerEntity)) {
+- passengerEntity.setOldPosAndRot();
+- passengerEntity.tickCount++;
+- ProfilerFiller profiler = this.getProfiler();
+- profiler.push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(passengerEntity.getType()).toString());
+- profiler.incrementCounter("tickPassenger");
+- passengerEntity.rideTick();
+- profiler.pop();
++ if (!passengerEntity.isRemoved() && passengerEntity.getVehicle() == ridingEntity) {
++ if (passengerEntity instanceof Player || this.entityTickList.contains(passengerEntity)) {
++ passengerEntity.setOldPosAndRot();
++ ++passengerEntity.tickCount;
++ ProfilerFiller gameprofilerfiller = this.getProfiler();
+
+- for (Entity entity : passengerEntity.getPassengers()) {
+- this.tickPassenger(passengerEntity, entity);
++ gameprofilerfiller.push(() -> {
++ return BuiltInRegistries.ENTITY_TYPE.getKey(passengerEntity.getType()).toString();
++ });
++ gameprofilerfiller.incrementCounter("tickPassenger");
++ passengerEntity.rideTick();
++ passengerEntity.postTick(); // CraftBukkit
++ gameprofilerfiller.pop();
++ Iterator iterator = passengerEntity.getPassengers().iterator();
++
++ while (iterator.hasNext()) {
++ Entity entity2 = (Entity) iterator.next();
++
++ this.tickPassenger(passengerEntity, entity2);
++ }
++
+ }
++ } else {
++ passengerEntity.stopRiding();
+ }
+ }
+
+@@ -772,8 +902,10 @@
+ }
+
+ public void save(@Nullable ProgressListener progress, boolean flush, boolean skipSave) {
+- ServerChunkCache chunkSource = this.getChunkSource();
++ ServerChunkCache chunkproviderserver = this.getChunkSource();
++
+ if (!skipSave) {
++ org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld())); // CraftBukkit
+ if (progress != null) {
+ progress.progressStartNoAbort(Component.translatable("menu.savingLevel"));
+ }
+@@ -783,18 +915,27 @@
+ progress.progressStage(Component.translatable("menu.savingChunks"));
+ }
+
+- chunkSource.save(flush);
++ chunkproviderserver.save(flush);
+ if (flush) {
+ this.entityManager.saveAll();
+ } else {
+ this.entityManager.autoSave();
+ }
++
+ }
++
++ // CraftBukkit start - moved from MinecraftServer.saveChunks
++ ServerLevel worldserver1 = this;
++
++ serverLevelData.setWorldBorder(worldserver1.getWorldBorder().createSettings());
++ serverLevelData.setCustomBossEvents(this.server.getCustomBossEvents().save());
++ convertable.saveDataTag(this.server.registryAccess(), this.serverLevelData, this.server.getPlayerList().getSingleplayerData());
++ // CraftBukkit end
+ }
+
+ private void saveLevelData() {
+ if (this.dragonFight != null) {
+- this.server.getWorldData().setEndDragonFightData(this.dragonFight.saveData());
++ this.serverLevelData.setEndDragonFightData(this.dragonFight.saveData()); // CraftBukkit
+ }
+
+ this.getChunkSource().getDataStorage().save();
+@@ -802,7 +943,8 @@
+
+ public <T extends Entity> List<? extends T> getEntities(EntityTypeTest<Entity, T> typeTest, Predicate<? super T> predicate) {
+ List<T> list = Lists.newArrayList();
+- this.getEntities(typeTest, predicate, list);
++
++ this.getEntities(typeTest, predicate, (List) list);
+ return list;
+ }
+
+@@ -811,20 +953,20 @@
+ }
+
+ public <T extends Entity> void getEntities(EntityTypeTest<Entity, T> typeTest, Predicate<? super T> predicate, List<? super T> output, int maxResults) {
+- this.getEntities().get(typeTest, value -> {
+- if (predicate.test(value)) {
+- output.add(value);
++ this.getEntities().get(typeTest, (entity) -> {
++ if (predicate.test(entity)) {
++ output.add(entity);
+ if (output.size() >= maxResults) {
+- return AbortableIterationConsumer.Continuation.ABORT;
++ return AbortableIterationConsumer.a.ABORT;
+ }
+ }
+
+- return AbortableIterationConsumer.Continuation.CONTINUE;
++ return AbortableIterationConsumer.a.CONTINUE;
+ });
+ }
+
+ public List<? extends EnderDragon> getDragons() {
+- return this.getEntities(EntityType.ENDER_DRAGON, LivingEntity::isAlive);
++ return this.getEntities((EntityTypeTest) EntityType.ENDER_DRAGON, LivingEntity::isAlive);
+ }
+
+ public List<ServerPlayer> getPlayers(Predicate<? super ServerPlayer> predicate) {
+@@ -833,10 +975,13 @@
+
+ public List<ServerPlayer> getPlayers(Predicate<? super ServerPlayer> predicate, int maxResults) {
+ List<ServerPlayer> list = Lists.newArrayList();
++ Iterator iterator = this.players.iterator();
+
+- for (ServerPlayer serverPlayer : this.players) {
+- if (predicate.test(serverPlayer)) {
+- list.add(serverPlayer);
++ while (iterator.hasNext()) {
++ ServerPlayer entityplayer = (ServerPlayer) iterator.next();
++
++ if (predicate.test(entityplayer)) {
++ list.add(entityplayer);
+ if (list.size() >= maxResults) {
+ return list;
+ }
+@@ -848,23 +993,46 @@
+
+ @Nullable
+ public ServerPlayer getRandomPlayer() {
+- List<ServerPlayer> players = this.getPlayers(LivingEntity::isAlive);
+- return players.isEmpty() ? null : players.get(this.random.nextInt(players.size()));
++ List<ServerPlayer> list = this.getPlayers(LivingEntity::isAlive);
++
++ return list.isEmpty() ? null : (ServerPlayer) list.get(this.random.nextInt(list.size()));
+ }
+
+ @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) {
+- this.addEntity(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) {
++ this.addEntity(entity, reason);
++ // CraftBukkit end
++ }
++
+ public void addDuringCommandTeleport(ServerPlayer player) {
+ this.addPlayer(player);
+ }
+@@ -882,30 +1050,48 @@
+ }
+
+ private void addPlayer(ServerPlayer player) {
+- Entity entity = this.getEntities().get(player.getUUID());
++ Entity entity = (Entity) this.getEntities().get(player.getUUID());
++
+ if (entity != null) {
+- LOGGER.warn("Force-added player with duplicate UUID {}", player.getUUID());
++ ServerLevel.LOGGER.warn("Force-added player with duplicate UUID {}", player.getUUID());
+ entity.unRide();
+- this.removePlayerImmediately((ServerPlayer)entity, Entity.RemovalReason.DISCARDED);
++ this.removePlayerImmediately((ServerPlayer) entity, Entity.RemovalReason.DISCARDED);
+ }
+
+ this.entityManager.addNewEntity(player);
+ }
+
+- private boolean addEntity(Entity entity) {
++ // CraftBukkit start
++ private boolean addEntity(Entity entity, CreatureSpawnEvent.SpawnReason spawnReason) {
+ if (entity.isRemoved()) {
+- 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) {
+- if (entity.getSelfAndPassengers().map(Entity::getUUID).anyMatch(this.entityManager::isLoaded)) {
++ // 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;
+ }
+ }
+@@ -919,50 +1105,62 @@
+ player.remove(reason);
+ }
+
++ // 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 breakerId, BlockPos pos, int progress) {
+- for (ServerPlayer serverPlayer : this.server.getPlayerList().getPlayers()) {
+- if (serverPlayer != null && serverPlayer.level() == this && serverPlayer.getId() != breakerId) {
+- double d = (double)pos.getX() - serverPlayer.getX();
+- double d1 = (double)pos.getY() - serverPlayer.getY();
+- double d2 = (double)pos.getZ() - serverPlayer.getZ();
+- if (d * d + d1 * d1 + d2 * d2 < 1024.0) {
+- serverPlayer.connection.send(new ClientboundBlockDestructionPacket(breakerId, pos, progress));
++ Iterator iterator = this.server.getPlayerList().getPlayers().iterator();
++
++ // CraftBukkit start
++ Player entityhuman = null;
++ Entity entity = this.getEntity(breakerId);
++ if (entity instanceof Player) entityhuman = (Player) entity;
++ // CraftBukkit end
++
++ while (iterator.hasNext()) {
++ ServerPlayer entityplayer = (ServerPlayer) iterator.next();
++
++ if (entityplayer != null && entityplayer.level() == this && entityplayer.getId() != breakerId) {
++ double d0 = (double) pos.getX() - entityplayer.getX();
++ 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(breakerId, pos, progress));
++ }
+ }
+ }
++
+ }
+
+ @Override
+- public void playSeededSound(
+- @Nullable Player player, double x, double y, double z, Holder<SoundEvent> sound, SoundSource source, float volume, float pitch, long seed
+- ) {
+- this.server
+- .getPlayerList()
+- .broadcast(
+- player,
+- x,
+- y,
+- z,
+- (double)sound.value().getRange(volume),
+- this.dimension(),
+- new ClientboundSoundPacket(sound, source, x, y, z, volume, pitch, seed)
+- );
++ public void playSeededSound(@Nullable Player player, double x, double d1, double y, Holder<SoundEvent> holder, SoundSource z, float f, float sound, long source) {
++ this.server.getPlayerList().broadcast(player, x, d1, y, (double) ((SoundEvent) holder.value()).getRange(f), this.dimension(), new ClientboundSoundPacket(holder, z, x, d1, y, f, sound, source));
+ }
+
+ @Override
+ public void playSeededSound(@Nullable Player player, Entity entity, Holder<SoundEvent> sound, SoundSource category, float volume, float pitch, long seed) {
+- this.server
+- .getPlayerList()
+- .broadcast(
+- player,
+- entity.getX(),
+- entity.getY(),
+- entity.getZ(),
+- (double)sound.value().getRange(volume),
+- this.dimension(),
+- new ClientboundSoundEntityPacket(sound, category, entity, volume, pitch, seed)
+- );
++ this.server.getPlayerList().broadcast(player, entity.getX(), entity.getY(), entity.getZ(), (double) ((SoundEvent) sound.value()).getRange(volume), this.dimension(), new ClientboundSoundEntityPacket(sound, category, entity, volume, pitch, seed));
+ }
+
+ @Override
+@@ -970,23 +1168,14 @@
+ if (this.getGameRules().getBoolean(GameRules.RULE_GLOBAL_SOUND_EVENTS)) {
+ this.server.getPlayerList().broadcastAll(new ClientboundLevelEventPacket(id, pos, data, true));
+ } else {
+- this.levelEvent(null, id, pos, data);
++ this.levelEvent((Player) null, id, pos, data);
+ }
++
+ }
+
+ @Override
+ public void levelEvent(@Nullable Player player, int type, BlockPos pos, int data) {
+- this.server
+- .getPlayerList()
+- .broadcast(
+- player,
+- (double)pos.getX(),
+- (double)pos.getY(),
+- (double)pos.getZ(),
+- 64.0,
+- this.dimension(),
+- new ClientboundLevelEventPacket(type, pos, data, false)
+- );
++ this.server.getPlayerList().broadcast(player, (double) pos.getX(), (double) pos.getY(), (double) pos.getZ(), 64.0D, this.dimension(), new ClientboundLevelEventPacket(type, pos, data, false));
+ }
+
+ public int getLogicalHeight() {
+@@ -999,40 +1188,60 @@
+ }
+
+ @Override
+- public void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags) {
++ public void sendBlockUpdated(BlockPos pos, IBlockData oldState, IBlockData newState, int flags) {
+ if (this.isUpdatingNavigations) {
+- String string = "recursive call to sendBlockUpdated";
++ String s = "recursive call to sendBlockUpdated";
++
+ Util.logAndPauseIfInIde("recursive call to sendBlockUpdated", new IllegalStateException("recursive call to sendBlockUpdated"));
+ }
+
+ this.getChunkSource().blockChanged(pos);
+- VoxelShape collisionShape = oldState.getCollisionShape(this, pos);
+- VoxelShape collisionShape1 = newState.getCollisionShape(this, pos);
+- if (Shapes.joinIsNotEmpty(collisionShape, collisionShape1, BooleanOp.NOT_SAME)) {
+- List<PathNavigation> list = new ObjectArrayList<>();
++ VoxelShape voxelshape = oldState.getCollisionShape(this, pos);
++ VoxelShape voxelshape1 = newState.getCollisionShape(this, pos);
+
+- for (Mob mob : this.navigatingMobs) {
+- PathNavigation navigation = mob.getNavigation();
+- if (navigation.shouldRecomputePath(pos)) {
+- list.add(navigation);
++ if (Shapes.joinIsNotEmpty(voxelshape, voxelshape1, BooleanOp.NOT_SAME)) {
++ List<PathNavigation> list = new ObjectArrayList();
++ Iterator iterator = this.navigatingMobs.iterator();
++
++ while (iterator.hasNext()) {
++ // 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)
++ sendBlockUpdated(pos, oldState, newState, flags);
++ return;
+ }
++ // CraftBukkit end
++ PathNavigation navigationabstract = entityinsentient.getNavigation();
++
++ if (navigationabstract.shouldRecomputePath(pos)) {
++ list.add(navigationabstract);
++ }
+ }
+
+ try {
+ this.isUpdatingNavigations = true;
++ iterator = list.iterator();
+
+- for (PathNavigation pathNavigation : list) {
+- pathNavigation.recomputePath();
++ while (iterator.hasNext()) {
++ PathNavigation navigationabstract1 = (PathNavigation) iterator.next();
++
++ navigationabstract1.recomputePath();
+ }
+ } finally {
+ this.isUpdatingNavigations = false;
+ }
++
+ }
+ }
+
+ @Override
+ public void updateNeighborsAt(BlockPos pos, Block block) {
+- this.neighborUpdater.updateNeighborsAtExceptFromFacing(pos, block, null);
++ this.neighborUpdater.updateNeighborsAtExceptFromFacing(pos, block, (Direction) null);
+ }
+
+ @Override
+@@ -1046,7 +1255,7 @@
+ }
+
+ @Override
+- public void neighborChanged(BlockState state, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving) {
++ public void neighborChanged(IBlockData state, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving) {
+ this.neighborUpdater.neighborChanged(state, pos, block, fromPos, isMoving);
+ }
+
+@@ -1066,44 +1275,25 @@
+ }
+
+ @Override
+- public Explosion explode(
+- @Nullable Entity entity,
+- @Nullable DamageSource damageSource,
+- @Nullable ExplosionDamageCalculator explosionDamageCalculator,
+- double d,
+- double d1,
+- double d2,
+- float f,
+- boolean flag,
+- Level.ExplosionInteraction explosionInteraction,
+- ParticleOptions particleOptions,
+- ParticleOptions particleOptions1,
+- SoundEvent soundEvent
+- ) {
+- Explosion explosion = this.explode(
+- entity, damageSource, explosionDamageCalculator, d, d1, d2, f, flag, explosionInteraction, false, particleOptions, particleOptions1, soundEvent
+- );
++ public Explosion explode(@Nullable Entity entity, @Nullable DamageSource damagesource, @Nullable ExplosionDamageCalculator explosiondamagecalculator, double d0, double d1, double d2, float f, boolean flag, Level.a world_a, ParticleOptions particleparam, ParticleOptions particleparam1, SoundEvent soundeffect) {
++ Explosion explosion = this.explode(entity, damagesource, explosiondamagecalculator, d0, d1, d2, f, flag, world_a, false, particleparam, particleparam1, soundeffect);
++ // CraftBukkit start
++ if (explosion.wasCanceled) {
++ return explosion;
++ }
++ // CraftBukkit end
++
+ if (!explosion.interactsWithBlocks()) {
+ explosion.clearToBlow();
+ }
+
+- for (ServerPlayer serverPlayer : this.players) {
+- if (serverPlayer.distanceToSqr(d, d1, d2) < 4096.0) {
+- serverPlayer.connection
+- .send(
+- new ClientboundExplodePacket(
+- d,
+- d1,
+- d2,
+- f,
+- explosion.getToBlow(),
+- explosion.getHitPlayers().get(serverPlayer),
+- explosion.getBlockInteraction(),
+- explosion.getSmallExplosionParticles(),
+- explosion.getLargeExplosionParticles(),
+- explosion.getExplosionSound()
+- )
+- );
++ Iterator iterator = this.players.iterator();
++
++ while (iterator.hasNext()) {
++ ServerPlayer entityplayer = (ServerPlayer) iterator.next();
++
++ if (entityplayer.distanceToSqr(d0, d1, d2) < 4096.0D) {
++ entityplayer.connection.send(new ClientboundExplodePacket(d0, d1, d2, f, explosion.getToBlow(), (Vec3) explosion.getHitPlayers().get(entityplayer), explosion.getBlockInteraction(), explosion.getSmallExplosionParticles(), explosion.getLargeExplosionParticles(), explosion.getExplosionSound()));
+ }
+ }
+
+@@ -1119,23 +1309,14 @@
+ this.blockEventsToReschedule.clear();
+
+ while (!this.blockEvents.isEmpty()) {
+- BlockEventData blockEventData = this.blockEvents.removeFirst();
+- if (this.shouldTickBlocksAt(blockEventData.pos())) {
+- if (this.doBlockEvent(blockEventData)) {
+- this.server
+- .getPlayerList()
+- .broadcast(
+- null,
+- (double)blockEventData.pos().getX(),
+- (double)blockEventData.pos().getY(),
+- (double)blockEventData.pos().getZ(),
+- 64.0,
+- this.dimension(),
+- new ClientboundBlockEventPacket(blockEventData.pos(), blockEventData.block(), blockEventData.paramA(), blockEventData.paramB())
+- );
++ BlockEventData blockactiondata = (BlockEventData) this.blockEvents.removeFirst();
++
++ if (this.shouldTickBlocksAt(blockactiondata.pos())) {
++ if (this.doBlockEvent(blockactiondata)) {
++ this.server.getPlayerList().broadcast((Player) null, (double) blockactiondata.pos().getX(), (double) blockactiondata.pos().getY(), (double) blockactiondata.pos().getZ(), 64.0D, this.dimension(), new ClientboundBlockEventPacket(blockactiondata.pos(), blockactiondata.block(), blockactiondata.paramA(), blockactiondata.paramB()));
+ }
+ } else {
+- this.blockEventsToReschedule.add(blockEventData);
++ this.blockEventsToReschedule.add(blockactiondata);
+ }
+ }
+
+@@ -1143,8 +1324,9 @@
+ }
+
+ private boolean doBlockEvent(BlockEventData event) {
+- BlockState blockState = this.getBlockState(event.pos());
+- return blockState.is(event.block()) && blockState.triggerEvent(this, event.pos(), event.paramA(), event.paramB());
++ IBlockData iblockdata = this.getBlockState(event.pos());
++
++ return iblockdata.is(event.block()) ? iblockdata.triggerEvent(this, event.pos(), event.paramA(), event.paramB()) : false;
+ }
+
+ @Override
+@@ -1171,49 +1353,41 @@
+ return this.server.getStructureManager();
+ }
+
+- public <T extends ParticleOptions> int sendParticles(
+- T type, double posX, double posY, double posZ, int particleCount, double xOffset, double yOffset, double zOffset, double speed
+- ) {
+- ClientboundLevelParticlesPacket clientboundLevelParticlesPacket = new ClientboundLevelParticlesPacket(
+- type, false, posX, posY, posZ, (float)xOffset, (float)yOffset, (float)zOffset, (float)speed, particleCount
+- );
+- int i = 0;
++ public <T extends ParticleOptions> int sendParticles(T type, double posX, double d1, double posY, int i, double posZ, double d4, double particleCount, double xOffset) {
++ // CraftBukkit - visibility api support
++ return sendParticles(null, type, posX, d1, posY, i, posZ, d4, particleCount, xOffset, false);
++ }
+
+- for (int i1 = 0; i1 < this.players.size(); i1++) {
+- ServerPlayer serverPlayer = this.players.get(i1);
+- if (this.sendParticles(serverPlayer, false, posX, posY, posZ, clientboundLevelParticlesPacket)) {
+- i++;
++ public <T extends ParticleOptions> int sendParticles(ServerPlayer sender, T t0, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6, boolean force) {
++ ClientboundLevelParticlesPacket packetplayoutworldparticles = new ClientboundLevelParticlesPacket(t0, force, d0, d1, d2, (float) d3, (float) d4, (float) d5, (float) d6, i);
++ // CraftBukkit end
++ int j = 0;
++
++ for (int k = 0; k < this.players.size(); ++k) {
++ ServerPlayer entityplayer = (ServerPlayer) this.players.get(k);
++ if (sender != null && !entityplayer.getBukkitEntity().canSee(sender.getBukkitEntity())) continue; // CraftBukkit
++
++ if (this.sendParticles(entityplayer, force, d0, d1, d2, packetplayoutworldparticles)) { // CraftBukkit
++ ++j;
+ }
+ }
+
+- return i;
++ return j;
+ }
+
+- public <T extends ParticleOptions> boolean sendParticles(
+- ServerPlayer player,
+- T type,
+- boolean longDistance,
+- double posX,
+- double posY,
+- double posZ,
+- int particleCount,
+- double xOffset,
+- double yOffset,
+- double zOffset,
+- double speed
+- ) {
+- Packet<?> packet = new ClientboundLevelParticlesPacket(
+- type, longDistance, posX, posY, posZ, (float)xOffset, (float)yOffset, (float)zOffset, (float)speed, particleCount
+- );
+- return this.sendParticles(player, longDistance, posX, posY, posZ, packet);
++ public <T extends ParticleOptions> boolean sendParticles(ServerPlayer player, T type, boolean longDistance, double posX, double d1, double posY, int i, double posZ, double d4, double particleCount, double xOffset) {
++ Packet<?> packet = new ClientboundLevelParticlesPacket(type, longDistance, posX, d1, posY, (float) posZ, (float) d4, (float) particleCount, (float) xOffset, i);
++
++ return this.sendParticles(player, longDistance, posX, d1, posY, packet);
+ }
+
+- private boolean sendParticles(ServerPlayer player, boolean longDistance, double posX, double posY, double posZ, Packet<?> packet) {
++ private boolean sendParticles(ServerPlayer player, boolean longDistance, double posX, double d1, double posY, Packet<?> packet) {
+ if (player.level() != this) {
+ return false;
+ } else {
+- BlockPos blockPos = player.blockPosition();
+- if (blockPos.closerToCenterThan(new Vec3(posX, posY, posZ), longDistance ? 512.0 : 32.0)) {
++ BlockPos blockposition = player.blockPosition();
++
++ if (blockposition.closerToCenterThan(new Vec3(posX, d1, posY), longDistance ? 512.0D : 32.0D)) {
+ player.connection.send(packet);
+ return true;
+ } else {
+@@ -1225,44 +1399,43 @@
+ @Nullable
+ @Override
+ public Entity getEntity(int id) {
+- return this.getEntities().get(id);
++ return (Entity) this.getEntities().get(id);
+ }
+
++ /** @deprecated */
+ @Deprecated
+ @Nullable
+ public Entity getEntityOrPart(int id) {
+- Entity entity = this.getEntities().get(id);
+- return entity != null ? entity : this.dragonParts.get(id);
++ Entity entity = (Entity) this.getEntities().get(id);
++
++ return entity != null ? entity : (Entity) this.dragonParts.get(id);
+ }
+
+ @Nullable
+ public Entity getEntity(UUID uniqueId) {
+- return this.getEntities().get(uniqueId);
++ return (Entity) this.getEntities().get(uniqueId);
+ }
+
+ @Nullable
+ public BlockPos findNearestMapStructure(TagKey<Structure> structureTag, BlockPos pos, int radius, boolean skipExistingChunks) {
+- if (!this.server.getWorldData().worldGenOptions().generateStructures()) {
++ if (!this.serverLevelData.worldGenOptions().generateStructures()) { // CraftBukkit
+ return null;
+ } else {
+- Optional<HolderSet.Named<Structure>> tag = this.registryAccess().registryOrThrow(Registries.STRUCTURE).getTag(structureTag);
+- if (tag.isEmpty()) {
++ Optional<HolderSet.Named<Structure>> optional = this.registryAccess().registryOrThrow(Registries.STRUCTURE).getTag(structureTag);
++
++ if (optional.isEmpty()) {
+ return null;
+ } else {
+- Pair<BlockPos, Holder<Structure>> pair = this.getChunkSource()
+- .getGenerator()
+- .findNearestMapStructure(this, tag.get(), pos, radius, skipExistingChunks);
+- return pair != null ? pair.getFirst() : null;
++ Pair<BlockPos, Holder<Structure>> pair = this.getChunkSource().getGenerator().findNearestMapStructure(this, (HolderSet) optional.get(), pos, radius, skipExistingChunks);
++
++ return pair != null ? (BlockPos) pair.getFirst() : null;
+ }
+ }
+ }
+
+ @Nullable
+- public Pair<BlockPos, Holder<Biome>> findClosestBiome3d(Predicate<Holder<Biome>> biomePredicate, BlockPos pos, int i, int i1, int i2) {
+- return this.getChunkSource()
+- .getGenerator()
+- .getBiomeSource()
+- .findClosestBiome3d(pos, i, i1, i2, biomePredicate, this.getChunkSource().randomState().sampler(), this);
++ public Pair<BlockPos, Holder<Biome>> findClosestBiome3d(Predicate<Holder<Biome>> biomePredicate, BlockPos pos, int i, int j, int k) {
++ return this.getChunkSource().getGenerator().getBiomeSource().findClosestBiome3d(pos, i, j, k, biomePredicate, this.getChunkSource().randomState().sampler(), this);
+ }
+
+ @Override
+@@ -1287,52 +1460,66 @@
+ @Nullable
+ @Override
+ public MapItemSavedData getMapData(String mapName) {
+- return this.getServer().overworld().getDataStorage().get(MapItemSavedData.factory(), mapName);
++ // CraftBukkit start
++ MapItemSavedData worldmap = (MapItemSavedData) this.getServer().overworld().getDataStorage().get(MapItemSavedData.factory(), mapName);
++ if (worldmap != null) {
++ worldmap.id = mapName;
++ }
++ return worldmap;
++ // CraftBukkit end
+ }
+
+ @Override
+ public void setMapData(String mapName, MapItemSavedData data) {
++ // CraftBukkit start
++ data.id = mapName;
++ MapInitializeEvent event = new MapInitializeEvent(data.mapView);
++ Bukkit.getServer().getPluginManager().callEvent(event);
++ // CraftBukkit end
+ this.getServer().overworld().getDataStorage().set(mapName, data);
+ }
+
+ @Override
+ public int getFreeMapId() {
+- return this.getServer().overworld().getDataStorage().computeIfAbsent(MapIndex.factory(), "idcounts").getFreeAuxValueForMap();
++ return ((MapIndex) this.getServer().overworld().getDataStorage().computeIfAbsent(MapIndex.factory(), "idcounts")).getFreeAuxValueForMap();
+ }
+
+ public void setDefaultSpawnPos(BlockPos pos, float angle) {
+- ChunkPos chunkPos = new ChunkPos(new BlockPos(this.levelData.getXSpawn(), 0, this.levelData.getZSpawn()));
++ ChunkPos chunkcoordintpair = new ChunkPos(new BlockPos(this.levelData.getXSpawn(), 0, this.levelData.getZSpawn()));
++
+ this.levelData.setSpawn(pos, angle);
+- this.getChunkSource().removeRegionTicket(TicketType.START, chunkPos, 11, Unit.INSTANCE);
++ this.getChunkSource().removeRegionTicket(TicketType.START, chunkcoordintpair, 11, Unit.INSTANCE);
+ this.getChunkSource().addRegionTicket(TicketType.START, new ChunkPos(pos), 11, Unit.INSTANCE);
+ this.getServer().getPlayerList().broadcastAll(new ClientboundSetDefaultSpawnPositionPacket(pos, angle));
+ }
+
+ public LongSet getForcedChunks() {
+- ForcedChunksSavedData forcedChunksSavedData = this.getDataStorage().get(ForcedChunksSavedData.factory(), "chunks");
+- return (LongSet)(forcedChunksSavedData != null ? LongSets.unmodifiable(forcedChunksSavedData.getChunks()) : LongSets.EMPTY_SET);
++ ForcedChunksSavedData forcedchunk = (ForcedChunksSavedData) this.getDataStorage().get(ForcedChunksSavedData.factory(), "chunks");
++
++ return (LongSet) (forcedchunk != null ? LongSets.unmodifiable(forcedchunk.getChunks()) : LongSets.EMPTY_SET);
+ }
+
+ public boolean setChunkForced(int chunkX, int chunkZ, boolean add) {
+- ForcedChunksSavedData forcedChunksSavedData = this.getDataStorage().computeIfAbsent(ForcedChunksSavedData.factory(), "chunks");
+- ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
+- long l = chunkPos.toLong();
+- boolean flag;
++ ForcedChunksSavedData forcedchunk = (ForcedChunksSavedData) this.getDataStorage().computeIfAbsent(ForcedChunksSavedData.factory(), "chunks");
++ ChunkPos chunkcoordintpair = new ChunkPos(chunkX, chunkZ);
++ long k = chunkcoordintpair.toLong();
++ boolean flag1;
++
+ if (add) {
+- flag = forcedChunksSavedData.getChunks().add(l);
+- if (flag) {
++ flag1 = forcedchunk.getChunks().add(k);
++ if (flag1) {
+ this.getChunk(chunkX, chunkZ);
+ }
+ } else {
+- flag = forcedChunksSavedData.getChunks().remove(l);
++ flag1 = forcedchunk.getChunks().remove(k);
+ }
+
+- forcedChunksSavedData.setDirty(flag);
+- if (flag) {
+- this.getChunkSource().updateChunkForced(chunkPos, add);
++ forcedchunk.setDirty(flag1);
++ if (flag1) {
++ this.getChunkSource().updateChunkForced(chunkcoordintpair, add);
+ }
+
+- return flag;
++ return flag1;
+ }
+
+ @Override
+@@ -1341,19 +1528,25 @@
+ }
+
+ @Override
+- public void onBlockStateChange(BlockPos pos, BlockState blockState, BlockState newState) {
++ public void onBlockStateChange(BlockPos pos, IBlockData blockState, IBlockData newState) {
+ Optional<Holder<PoiType>> optional = PoiTypes.forState(blockState);
+ Optional<Holder<PoiType>> optional1 = PoiTypes.forState(newState);
++
+ if (!Objects.equals(optional, optional1)) {
+- BlockPos blockPos = pos.immutable();
+- optional.ifPresent(holder -> this.getServer().execute(() -> {
+- this.getPoiManager().remove(blockPos);
+- DebugPackets.sendPoiRemovedPacket(this, blockPos);
+- }));
+- optional1.ifPresent(holder -> this.getServer().execute(() -> {
+- this.getPoiManager().add(blockPos, (Holder<PoiType>)holder);
+- DebugPackets.sendPoiAddedPacket(this, blockPos);
+- }));
++ BlockPos blockposition1 = pos.immutable();
++
++ optional.ifPresent((holder) -> {
++ this.getServer().execute(() -> {
++ this.getPoiManager().remove(blockposition1);
++ DebugPackets.sendPoiRemovedPacket(this, blockposition1);
++ });
++ });
++ optional1.ifPresent((holder) -> {
++ this.getServer().execute(() -> {
++ this.getPoiManager().add(blockposition1, holder);
++ DebugPackets.sendPoiAddedPacket(this, blockposition1);
++ });
++ });
+ }
+ }
+
+@@ -1370,7 +1563,7 @@
+ }
+
+ public boolean isCloseToVillage(BlockPos pos, int sections) {
+- return sections <= 6 && this.sectionsToVillage(SectionPos.of(pos)) <= sections;
++ return sections > 6 ? false : this.sectionsToVillage(SectionPos.of(pos)) <= sections;
+ }
+
+ public int sectionsToVillage(SectionPos pos) {
+@@ -1395,104 +1588,199 @@
+ }
+
+ public void saveDebugReport(Path path) throws IOException {
+- ChunkMap chunkMap = this.getChunkSource().chunkMap;
++ ChunkMap playerchunkmap = this.getChunkSource().chunkMap;
++ BufferedWriter bufferedwriter = Files.newBufferedWriter(path.resolve("stats.txt"));
+
+- try (Writer bufferedWriter = Files.newBufferedWriter(path.resolve("stats.txt"))) {
+- bufferedWriter.write(String.format(Locale.ROOT, "spawning_chunks: %d\n", chunkMap.getDistanceManager().getNaturalSpawnChunkCount()));
+- NaturalSpawner.SpawnState lastSpawnState = this.getChunkSource().getLastSpawnState();
+- if (lastSpawnState != null) {
+- for (Entry<MobCategory> entry : lastSpawnState.getMobCategoryCounts().object2IntEntrySet()) {
+- bufferedWriter.write(String.format(Locale.ROOT, "spawn_count.%s: %d\n", entry.getKey().getName(), entry.getIntValue()));
++ try {
++ bufferedwriter.write(String.format(Locale.ROOT, "spawning_chunks: %d\n", playerchunkmap.getDistanceManager().getNaturalSpawnChunkCount()));
++ NaturalSpawner.SpawnState spawnercreature_d = this.getChunkSource().getLastSpawnState();
++
++ if (spawnercreature_d != null) {
++ ObjectIterator objectiterator = spawnercreature_d.getMobCategoryCounts().object2IntEntrySet().iterator();
++
++ while (objectiterator.hasNext()) {
++ Entry<MobCategory> entry = (Entry) objectiterator.next();
++
++ bufferedwriter.write(String.format(Locale.ROOT, "spawn_count.%s: %d\n", ((MobCategory) entry.getKey()).getName(), entry.getIntValue()));
+ }
+ }
+
+- bufferedWriter.write(String.format(Locale.ROOT, "entities: %s\n", this.entityManager.gatherStats()));
+- bufferedWriter.write(String.format(Locale.ROOT, "block_entity_tickers: %d\n", this.blockEntityTickers.size()));
+- bufferedWriter.write(String.format(Locale.ROOT, "block_ticks: %d\n", this.getBlockTicks().count()));
+- bufferedWriter.write(String.format(Locale.ROOT, "fluid_ticks: %d\n", this.getFluidTicks().count()));
+- bufferedWriter.write("distance_manager: " + chunkMap.getDistanceManager().getDebugStatus() + "\n");
+- bufferedWriter.write(String.format(Locale.ROOT, "pending_tasks: %d\n", this.getChunkSource().getPendingTasksCount()));
++ bufferedwriter.write(String.format(Locale.ROOT, "entities: %s\n", this.entityManager.gatherStats()));
++ bufferedwriter.write(String.format(Locale.ROOT, "block_entity_tickers: %d\n", this.blockEntityTickers.size()));
++ bufferedwriter.write(String.format(Locale.ROOT, "block_ticks: %d\n", this.getBlockTicks().count()));
++ bufferedwriter.write(String.format(Locale.ROOT, "fluid_ticks: %d\n", this.getFluidTicks().count()));
++ bufferedwriter.write("distance_manager: " + playerchunkmap.getDistanceManager().getDebugStatus() + "\n");
++ bufferedwriter.write(String.format(Locale.ROOT, "pending_tasks: %d\n", this.getChunkSource().getPendingTasksCount()));
++ } catch (Throwable throwable) {
++ if (bufferedwriter != null) {
++ try {
++ bufferedwriter.close();
++ } catch (Throwable throwable1) {
++ throwable.addSuppressed(throwable1);
++ }
++ }
++
++ throw throwable;
+ }
+
+- CrashReport crashReport = new CrashReport("Level dump", new Exception("dummy"));
+- this.fillReportDetails(crashReport);
++ if (bufferedwriter != null) {
++ bufferedwriter.close();
++ }
+
+- try (Writer bufferedWriter1 = Files.newBufferedWriter(path.resolve("example_crash.txt"))) {
+- bufferedWriter1.write(crashReport.getFriendlyReport());
++ CrashReport crashreport = new CrashReport("Level dump", new Exception("dummy"));
++
++ this.fillReportDetails(crashreport);
++ BufferedWriter bufferedwriter1 = Files.newBufferedWriter(path.resolve("example_crash.txt"));
++
++ try {
++ bufferedwriter1.write(crashreport.getFriendlyReport());
++ } catch (Throwable throwable2) {
++ if (bufferedwriter1 != null) {
++ try {
++ bufferedwriter1.close();
++ } catch (Throwable throwable3) {
++ throwable2.addSuppressed(throwable3);
++ }
++ }
++
++ throw throwable2;
+ }
+
++ if (bufferedwriter1 != null) {
++ bufferedwriter1.close();
++ }
++
+ Path path1 = path.resolve("chunks.csv");
++ BufferedWriter bufferedwriter2 = Files.newBufferedWriter(path1);
+
+- try (Writer bufferedWriter2 = Files.newBufferedWriter(path1)) {
+- chunkMap.dumpChunks(bufferedWriter2);
++ try {
++ playerchunkmap.dumpChunks(bufferedwriter2);
++ } catch (Throwable throwable4) {
++ if (bufferedwriter2 != null) {
++ try {
++ bufferedwriter2.close();
++ } catch (Throwable throwable5) {
++ throwable4.addSuppressed(throwable5);
++ }
++ }
++
++ throw throwable4;
+ }
+
++ if (bufferedwriter2 != null) {
++ bufferedwriter2.close();
++ }
++
+ Path path2 = path.resolve("entity_chunks.csv");
++ BufferedWriter bufferedwriter3 = Files.newBufferedWriter(path2);
+
+- try (Writer bufferedWriter3 = Files.newBufferedWriter(path2)) {
+- this.entityManager.dumpSections(bufferedWriter3);
++ try {
++ this.entityManager.dumpSections(bufferedwriter3);
++ } catch (Throwable throwable6) {
++ if (bufferedwriter3 != null) {
++ try {
++ bufferedwriter3.close();
++ } catch (Throwable throwable7) {
++ throwable6.addSuppressed(throwable7);
++ }
++ }
++
++ throw throwable6;
+ }
+
++ if (bufferedwriter3 != null) {
++ bufferedwriter3.close();
++ }
++
+ Path path3 = path.resolve("entities.csv");
++ BufferedWriter bufferedwriter4 = Files.newBufferedWriter(path3);
+
+- try (Writer bufferedWriter4 = Files.newBufferedWriter(path3)) {
+- dumpEntities(bufferedWriter4, this.getEntities().getAll());
++ try {
++ dumpEntities(bufferedwriter4, this.getEntities().getAll());
++ } catch (Throwable throwable8) {
++ if (bufferedwriter4 != null) {
++ try {
++ bufferedwriter4.close();
++ } catch (Throwable throwable9) {
++ throwable8.addSuppressed(throwable9);
++ }
++ }
++
++ throw throwable8;
+ }
+
++ if (bufferedwriter4 != null) {
++ bufferedwriter4.close();
++ }
++
+ Path path4 = path.resolve("block_entities.csv");
++ BufferedWriter bufferedwriter5 = Files.newBufferedWriter(path4);
+
+- try (Writer bufferedWriter5 = Files.newBufferedWriter(path4)) {
+- this.dumpBlockEntityTickers(bufferedWriter5);
++ try {
++ this.dumpBlockEntityTickers(bufferedwriter5);
++ } catch (Throwable throwable10) {
++ if (bufferedwriter5 != null) {
++ try {
++ bufferedwriter5.close();
++ } catch (Throwable throwable11) {
++ throwable10.addSuppressed(throwable11);
++ }
++ }
++
++ throw throwable10;
+ }
++
++ if (bufferedwriter5 != null) {
++ bufferedwriter5.close();
++ }
++
+ }
+
+ private static void dumpEntities(Writer writer, Iterable<Entity> entities) throws IOException {
+- CsvOutput csvOutput = CsvOutput.builder()
+- .addColumn("x")
+- .addColumn("y")
+- .addColumn("z")
+- .addColumn("uuid")
+- .addColumn("type")
+- .addColumn("alive")
+- .addColumn("display_name")
+- .addColumn("custom_name")
+- .build(writer);
++ CsvOutput csvwriter = CsvOutput.builder().addColumn("x").addColumn("y").addColumn("z").addColumn("uuid").addColumn("type").addColumn("alive").addColumn("display_name").addColumn("custom_name").build(writer);
++ Iterator iterator = entities.iterator();
+
+- for (Entity entity : entities) {
+- Component customName = entity.getCustomName();
+- Component displayName = entity.getDisplayName();
+- csvOutput.writeRow(
+- entity.getX(),
+- entity.getY(),
+- entity.getZ(),
+- entity.getUUID(),
+- BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()),
+- entity.isAlive(),
+- displayName.getString(),
+- customName != null ? customName.getString() : null
+- );
++ while (iterator.hasNext()) {
++ Entity entity = (Entity) iterator.next();
++ Component ichatbasecomponent = entity.getCustomName();
++ Component ichatbasecomponent1 = entity.getDisplayName();
++
++ csvwriter.writeRow(entity.getX(), entity.getY(), entity.getZ(), entity.getUUID(), BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()), entity.isAlive(), ichatbasecomponent1.getString(), ichatbasecomponent != null ? ichatbasecomponent.getString() : null);
+ }
++
+ }
+
+ private void dumpBlockEntityTickers(Writer output) throws IOException {
+- CsvOutput csvOutput = CsvOutput.builder().addColumn("x").addColumn("y").addColumn("z").addColumn("type").build(output);
++ CsvOutput csvwriter = CsvOutput.builder().addColumn("x").addColumn("y").addColumn("z").addColumn("type").build(output);
++ Iterator iterator = this.blockEntityTickers.iterator();
+
+- for (TickingBlockEntity tickingBlockEntity : this.blockEntityTickers) {
+- BlockPos pos = tickingBlockEntity.getPos();
+- csvOutput.writeRow(pos.getX(), pos.getY(), pos.getZ(), tickingBlockEntity.getType());
++ while (iterator.hasNext()) {
++ TickingBlockEntity tickingblockentity = (TickingBlockEntity) iterator.next();
++ BlockPos blockposition = tickingblockentity.getPos();
++
++ csvwriter.writeRow(blockposition.getX(), blockposition.getY(), blockposition.getZ(), tickingblockentity.getType());
+ }
++
+ }
+
+ @VisibleForTesting
+ public void clearBlockEvents(BoundingBox boundingBox) {
+- this.blockEvents.removeIf(blockEventData -> boundingBox.isInside(blockEventData.pos()));
++ this.blockEvents.removeIf((blockactiondata) -> {
++ return boundingBox.isInside(blockactiondata.pos());
++ });
+ }
+
+ @Override
+ public void blockUpdated(BlockPos pos, Block block) {
+ if (!this.isDebug()) {
++ // CraftBukkit start
++ if (populating) {
++ return;
++ }
++ // CraftBukkit end
+ this.updateNeighborsAt(pos, block);
+ }
++
+ }
+
+ @Override
+@@ -1504,18 +1792,17 @@
+ return this.getEntities().getAll();
+ }
+
+- @Override
+ public String toString() {
+ return "ServerLevel[" + this.serverLevelData.getLevelName() + "]";
+ }
+
+ 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
+@@ -1530,53 +1817,65 @@
+
+ @VisibleForTesting
+ public String getWatchdogStats() {
+- return String.format(
+- Locale.ROOT,
+- "players: %s, entities: %s [%s], block_entities: %d [%s], block_ticks: %d, fluid_ticks: %d, chunk_source: %s",
+- this.players.size(),
+- this.entityManager.gatherStats(),
+- getTypeCount(this.entityManager.getEntityGetter().getAll(), entity -> BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString()),
+- this.blockEntityTickers.size(),
+- getTypeCount(this.blockEntityTickers, TickingBlockEntity::getType),
+- this.getBlockTicks().count(),
+- this.getFluidTicks().count(),
+- this.gatherChunkSourceStats()
+- );
++ return String.format(Locale.ROOT, "players: %s, entities: %s [%s], block_entities: %d [%s], block_ticks: %d, fluid_ticks: %d, chunk_source: %s", this.players.size(), this.entityManager.gatherStats(), getTypeCount(this.entityManager.getEntityGetter().getAll(), (entity) -> {
++ return BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString();
++ }), this.blockEntityTickers.size(), getTypeCount(this.blockEntityTickers, TickingBlockEntity::getType), this.getBlockTicks().count(), this.getFluidTicks().count(), this.gatherChunkSourceStats());
+ }
+
+ private static <T> String getTypeCount(Iterable<T> objects, Function<T, String> typeGetter) {
+ try {
+- Object2IntOpenHashMap<String> map = new Object2IntOpenHashMap<>();
++ Object2IntOpenHashMap<String> object2intopenhashmap = new Object2IntOpenHashMap();
++ Iterator<T> iterator = objects.iterator(); // CraftBukkit - decompile error
+
+- for (T object : objects) {
+- String string = typeGetter.apply(object);
+- map.addTo(string, 1);
++ while (iterator.hasNext()) {
++ T t0 = iterator.next();
++ String s = (String) typeGetter.apply(t0);
++
++ object2intopenhashmap.addTo(s, 1);
+ }
+
+- return map.object2IntEntrySet()
+- .stream()
+- .sorted(Comparator.<Entry<String>, Integer>comparing(Entry::getIntValue).reversed())
+- .limit(5L)
+- .map(entry -> entry.getKey() + ":" + entry.getIntValue())
+- .collect(Collectors.joining(","));
+- } catch (Exception var6) {
++ 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();
++ }).collect(Collectors.joining(","));
++ } catch (Exception exception) {
+ return "";
+ }
+ }
+
+ public static void makeObsidianPlatform(ServerLevel serverLevel) {
+- BlockPos blockPos = END_SPAWN_POINT;
+- int x = blockPos.getX();
+- int i = blockPos.getY() - 2;
+- int z = blockPos.getZ();
+- BlockPos.betweenClosed(x - 2, i + 1, z - 2, x + 2, i + 3, z + 2)
+- .forEach(blockPos1 -> serverLevel.setBlockAndUpdate(blockPos1, Blocks.AIR.defaultBlockState()));
+- BlockPos.betweenClosed(x - 2, i, z - 2, x + 2, i, z + 2)
+- .forEach(blockPos1 -> serverLevel.setBlockAndUpdate(blockPos1, Blocks.OBSIDIAN.defaultBlockState()));
++ // CraftBukkit start
++ ServerLevel.makeObsidianPlatform(serverLevel, null);
+ }
+
++ public static void makeObsidianPlatform(ServerLevel worldserver, Entity entity) {
++ // CraftBukkit end
++ BlockPos blockposition = ServerLevel.END_SPAWN_POINT;
++ int i = blockposition.getX();
++ int j = blockposition.getY() - 2;
++ int k = blockposition.getZ();
++
++ // CraftBukkit start
++ org.bukkit.craftbukkit.util.BlockStateListPopulator blockList = new org.bukkit.craftbukkit.util.BlockStateListPopulator(worldserver);
++ BlockPos.betweenClosed(i - 2, j + 1, k - 2, i + 2, j + 3, k + 2).forEach((blockposition1) -> {
++ blockList.setBlock(blockposition1, Blocks.AIR.defaultBlockState(), 3);
++ });
++ BlockPos.betweenClosed(i - 2, j, k - 2, i + 2, j, k + 2).forEach((blockposition1) -> {
++ blockList.setBlock(blockposition1, Blocks.OBSIDIAN.defaultBlockState(), 3);
++ });
++ org.bukkit.World bworld = worldserver.getWorld();
++ org.bukkit.event.world.PortalCreateEvent portalEvent = new org.bukkit.event.world.PortalCreateEvent((List<org.bukkit.block.BlockState>) (List) blockList.getList(), bworld, (entity == null) ? null : entity.getBukkitEntity(), org.bukkit.event.world.PortalCreateEvent.CreateReason.END_PLATFORM);
++
++ worldserver.getCraftServer().getPluginManager().callEvent(portalEvent);
++ if (!portalEvent.isCancelled()) {
++ blockList.updateList();
++ }
++ // CraftBukkit end
++ }
++
+ @Override
+- protected LevelEntityGetter<Entity> getEntities() {
++ public LevelEntityGetter<Entity> getEntities() {
+ return this.entityManager.getEntityGetter();
+ }
+
+@@ -1593,7 +1892,9 @@
+ }
+
+ public void onStructureStartsAvailable(ChunkAccess chunk) {
+- this.server.execute(() -> this.structureCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts()));
++ this.server.execute(() -> {
++ this.structureCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts());
++ });
+ }
+
+ @Override
+@@ -1604,7 +1905,9 @@
+
+ @Override
+ public String gatherChunkSourceStats() {
+- return "Chunks[S] W: " + this.chunkSource.gatherStats() + " E: " + this.entityManager.gatherStats();
++ String s = this.chunkSource.gatherStats();
++
++ return "Chunks[S] W: " + s + " E: " + this.entityManager.gatherStats();
+ }
+
+ public boolean areEntitiesLoaded(long chunkPos) {
+@@ -1642,88 +1945,114 @@
+
+ @Override
+ public CrashReportCategory fillReportDetails(CrashReport report) {
+- CrashReportCategory crashReportCategory = super.fillReportDetails(report);
+- crashReportCategory.setDetail("Loaded entity count", () -> String.valueOf(this.entityManager.count()));
+- return crashReportCategory;
++ CrashReportCategory crashreportsystemdetails = super.fillReportDetails(report);
++
++ crashreportsystemdetails.setDetail("Loaded entity count", () -> {
++ return String.valueOf(this.entityManager.count());
++ });
++ return crashreportsystemdetails;
+ }
+
+- final class EntityCallbacks implements LevelCallback<Entity> {
+- @Override
+- public void onCreated(Entity entity) {
+- }
++ private final class a implements LevelCallback<Entity> {
+
+- @Override
++ a() {}
++
++ public void onCreated(Entity entity) {}
++
+ public void onDestroyed(Entity entity) {
+ ServerLevel.this.getScoreboard().entityRemoved(entity);
+ }
+
+- @Override
+ public void onTickingStart(Entity entity) {
+ ServerLevel.this.entityTickList.add(entity);
+ }
+
+- @Override
+ public void onTickingEnd(Entity entity) {
+ ServerLevel.this.entityTickList.remove(entity);
+ }
+
+- @Override
+ public void onTrackingStart(Entity entity) {
+ ServerLevel.this.getChunkSource().addEntity(entity);
+- if (entity instanceof ServerPlayer serverPlayer) {
+- ServerLevel.this.players.add(serverPlayer);
++ if (entity instanceof ServerPlayer) {
++ ServerPlayer entityplayer = (ServerPlayer) entity;
++
++ ServerLevel.this.players.add(entityplayer);
+ ServerLevel.this.updateSleepingPlayerList();
+ }
+
+- if (entity instanceof Mob mob) {
++ if (entity instanceof Mob) {
++ Mob entityinsentient = (Mob) entity;
++
+ if (ServerLevel.this.isUpdatingNavigations) {
+- String string = "onTrackingStart called during navigation iteration";
+- Util.logAndPauseIfInIde(
+- "onTrackingStart called during navigation iteration", new IllegalStateException("onTrackingStart called during navigation iteration")
+- );
++ String s = "onTrackingStart called during navigation iteration";
++
++ Util.logAndPauseIfInIde("onTrackingStart called during navigation iteration", new IllegalStateException("onTrackingStart called during navigation iteration"));
+ }
+
+- ServerLevel.this.navigatingMobs.add(mob);
++ ServerLevel.this.navigatingMobs.add(entityinsentient);
+ }
+
+- if (entity instanceof EnderDragon enderDragon) {
+- for (EnderDragonPart enderDragonPart : enderDragon.getSubEntities()) {
+- ServerLevel.this.dragonParts.put(enderDragonPart.getId(), enderDragonPart);
++ if (entity instanceof EnderDragon) {
++ EnderDragon entityenderdragon = (EnderDragon) entity;
++ EnderDragonPart[] aentitycomplexpart = entityenderdragon.getSubEntities();
++ int i = aentitycomplexpart.length;
++
++ for (int j = 0; j < i; ++j) {
++ EnderDragonPart entitycomplexpart = aentitycomplexpart[j];
++
++ ServerLevel.this.dragonParts.put(entitycomplexpart.getId(), entitycomplexpart);
+ }
+ }
+
+ entity.updateDynamicGameEventListener(DynamicGameEventListener::add);
++ entity.inWorld = true; // CraftBukkit - Mark entity as in world
++ entity.valid = true; // CraftBukkit
+ }
+
+- @Override
+ public void onTrackingEnd(Entity entity) {
+ ServerLevel.this.getChunkSource().removeEntity(entity);
+- if (entity instanceof ServerPlayer serverPlayer) {
+- ServerLevel.this.players.remove(serverPlayer);
++ if (entity instanceof ServerPlayer) {
++ ServerPlayer entityplayer = (ServerPlayer) entity;
++
++ ServerLevel.this.players.remove(entityplayer);
+ ServerLevel.this.updateSleepingPlayerList();
+ }
+
+- if (entity instanceof Mob mob) {
++ if (entity instanceof Mob) {
++ Mob entityinsentient = (Mob) entity;
++
+ if (ServerLevel.this.isUpdatingNavigations) {
+- String string = "onTrackingStart called during navigation iteration";
+- Util.logAndPauseIfInIde(
+- "onTrackingStart called during navigation iteration", new IllegalStateException("onTrackingStart called during navigation iteration")
+- );
++ String s = "onTrackingStart called during navigation iteration";
++
++ Util.logAndPauseIfInIde("onTrackingStart called during navigation iteration", new IllegalStateException("onTrackingStart called during navigation iteration"));
+ }
+
+- ServerLevel.this.navigatingMobs.remove(mob);
++ ServerLevel.this.navigatingMobs.remove(entityinsentient);
+ }
+
+- if (entity instanceof EnderDragon enderDragon) {
+- for (EnderDragonPart enderDragonPart : enderDragon.getSubEntities()) {
+- ServerLevel.this.dragonParts.remove(enderDragonPart.getId());
++ if (entity instanceof EnderDragon) {
++ EnderDragon entityenderdragon = (EnderDragon) entity;
++ EnderDragonPart[] aentitycomplexpart = entityenderdragon.getSubEntities();
++ int i = aentitycomplexpart.length;
++
++ for (int j = 0; j < i; ++j) {
++ EnderDragonPart entitycomplexpart = aentitycomplexpart[j];
++
++ ServerLevel.this.dragonParts.remove(entitycomplexpart.getId());
+ }
+ }
+
+ entity.updateDynamicGameEventListener(DynamicGameEventListener::remove);
++ // CraftBukkit start
++ entity.valid = false;
++ if (!(entity instanceof ServerPlayer)) {
++ for (ServerPlayer player : players) {
++ player.getBukkitEntity().onEntityRemove(entity);
++ }
++ }
++ // CraftBukkit end
+ }
+
+- @Override
+ public void onSectionChange(Entity entity) {
+ entity.updateDynamicGameEventListener(DynamicGameEventListener::move);
+ }