diff options
author | MiniDigger | Martin <[email protected]> | 2024-01-14 11:04:49 +0100 |
---|---|---|
committer | MiniDigger | Martin <[email protected]> | 2024-01-14 11:04:49 +0100 |
commit | bee74680e607c2e29b038329f62181238911cd83 (patch) | |
tree | 708fd1a4a0227d9071243adf2a42d5e9e96cde4a /patch-remap/mache-spigotflower/net/minecraft/server/level/ServerLevel.java.patch | |
parent | 0a44692ef6ff6e255d48eb3ba1bb114166eafda9 (diff) | |
download | Paper-bee74680e607c2e29b038329f62181238911cd83.tar.gz Paper-bee74680e607c2e29b038329f62181238911cd83.zip |
add remapped patches as a testsoftspoon
Diffstat (limited to 'patch-remap/mache-spigotflower/net/minecraft/server/level/ServerLevel.java.patch')
-rw-r--r-- | patch-remap/mache-spigotflower/net/minecraft/server/level/ServerLevel.java.patch | 2159 |
1 files changed, 2159 insertions, 0 deletions
diff --git a/patch-remap/mache-spigotflower/net/minecraft/server/level/ServerLevel.java.patch b/patch-remap/mache-spigotflower/net/minecraft/server/level/ServerLevel.java.patch new file mode 100644 index 0000000000..dc29b1cb79 --- /dev/null +++ b/patch-remap/mache-spigotflower/net/minecraft/server/level/ServerLevel.java.patch @@ -0,0 +1,2159 @@ +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -44,7 +44,6 @@ + import net.minecraft.core.Direction; + import net.minecraft.core.Holder; + import net.minecraft.core.HolderSet; +-import net.minecraft.core.RegistryAccess; + import net.minecraft.core.SectionPos; + import net.minecraft.core.particles.ParticleOptions; + import net.minecraft.core.registries.BuiltInRegistries; +@@ -57,7 +56,6 @@ + import net.minecraft.network.protocol.game.ClientboundDamageEventPacket; + import net.minecraft.network.protocol.game.ClientboundEntityEventPacket; + import net.minecraft.network.protocol.game.ClientboundExplodePacket; +-import net.minecraft.network.protocol.game.ClientboundGameEventPacket; + import net.minecraft.network.protocol.game.ClientboundLevelEventPacket; + import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket; + import net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket; +@@ -103,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; +@@ -121,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; +@@ -143,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; +@@ -155,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; +@@ -163,6 +164,19 @@ + 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 { + +@@ -177,9 +191,9 @@ + final List<ServerPlayer> players; + private final ServerChunkCache chunkSource; + private final MinecraftServer server; +- private final ServerLevelData serverLevelData; ++ public final PrimaryLevelData serverLevelData; // CraftBukkit - type + final EntityTickList entityTickList; +- private final PersistentEntitySectionManager<Entity> entityManager; ++ public final PersistentEntitySectionManager<Entity> entityManager; + private final GameEventDispatcher gameEventDispatcher; + public boolean noSave; + private final SleepStatus sleepStatus; +@@ -202,12 +216,30 @@ + private final boolean tickTime; + private final RandomSequences randomSequences; + +- public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess levelstoragesource_levelstorageaccess, ServerLevelData serverleveldata, ResourceKey<Level> resourcekey, LevelStem levelstem, ChunkProgressListener chunkprogresslistener, boolean flag, long i, List<CustomSpawner> list, boolean flag1, @Nullable RandomSequences randomsequences) { +- RegistryAccess.Frozen registryaccess_frozen = minecraftserver.registryAccess(); +- Holder holder = levelstem.type(); ++ // CraftBukkit start ++ public final LevelStorageSource.LevelStorageAccess convertable; ++ public final UUID uuid; + +- Objects.requireNonNull(minecraftserver); +- super(serverleveldata, resourcekey, registryaccess_frozen, holder, minecraftserver::getProfiler, false, flag, i, minecraftserver.getMaxChainedNeighborUpdates()); ++ 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()); +@@ -219,20 +251,36 @@ + this.tickTime = flag1; + this.server = minecraftserver; + this.customSpawners = list; +- this.serverLevelData = serverleveldata; +- ChunkGenerator chunkgenerator = levelstem.generator(); ++ 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, levelstoragesource_levelstorageaccess.getDimensionPath(resourcekey).resolve("entities"), datafixer, flag2, minecraftserver); ++ EntityPersistentStorage<Entity> entitypersistentstorage = new EntityStorage(this, convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), datafixer, flag2, minecraftserver); + +- this.entityManager = new PersistentEntitySectionManager<>(Entity.class, new ServerLevel.EntityCallbacks(), entitypersistentstorage); ++ 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, levelstoragesource_levelstorageaccess, datafixer, structuretemplatemanager, executor, chunkgenerator, j, k, flag2, chunkprogresslistener, persistententitysectionmanager::updateChunkStatus, () -> { ++ 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(); +@@ -242,15 +290,15 @@ + this.getWorldBorder().setAbsoluteMaxSize(minecraftserver.getAbsoluteMaxWorldSize()); + this.raids = (Raids) this.getDataStorage().computeIfAbsent(Raids.factory(this), Raids.getFileId(this.dimensionTypeRegistration())); + if (!minecraftserver.isSingleplayer()) { +- serverleveldata.setGameType(minecraftserver.getDefaultGameType()); ++ iworlddataserver.setGameType(minecraftserver.getDefaultGameType()); + } + + 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, minecraftserver.getWorldData().worldGenOptions(), this.structureCheck); +- if (this.dimension() == Level.END && this.dimensionTypeRegistration().is(BuiltinDimensionTypes.END)) { +- this.dragonFight = new EndDragonFight(this, l, minecraftserver.getWorldData().endDragonFightData()); ++ 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; + } +@@ -260,44 +308,44 @@ + 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 enddragonfight) { +- this.dragonFight = enddragonfight; ++ public void setDragonFight(@Nullable EndDragonFight dragonFight) { ++ this.dragonFight = dragonFight; + } + +- public void setWeatherParameters(int i, int j, boolean flag, boolean flag1) { +- this.serverLevelData.setClearWeatherTime(i); +- this.serverLevelData.setRainTime(j); +- this.serverLevelData.setThunderTime(j); +- this.serverLevelData.setRaining(flag); +- this.serverLevelData.setThundering(flag1); ++ public void setWeatherParameters(int clearTime, int weatherTime, boolean isRaining, boolean isThundering) { ++ this.serverLevelData.setClearWeatherTime(clearTime); ++ this.serverLevelData.setRainTime(weatherTime); ++ this.serverLevelData.setThunderTime(weatherTime); ++ this.serverLevelData.setRaining(isRaining); ++ this.serverLevelData.setThundering(isThundering); + } + + @Override +- @Override +- public Holder<Biome> getUncachedNoiseBiome(int i, int j, int k) { +- return this.getChunkSource().getGenerator().getBiomeSource().getNoiseBiome(i, j, k, this.getChunkSource().randomState().sampler()); ++ public Holder<Biome> getUncachedNoiseBiome(int x, int y, int z) { ++ return this.getChunkSource().getGenerator().getBiomeSource().getNoiseBiome(x, y, z, this.getChunkSource().randomState().sampler()); + } + + public StructureManager structureManager() { + return this.structureManager; + } + +- public void tick(BooleanSupplier booleansupplier) { +- ProfilerFiller profilerfiller = this.getProfiler(); ++ public void tick(BooleanSupplier hasTimeLeft) { ++ ProfilerFiller gameprofilerfiller = this.getProfiler(); + + this.handlingTick = true; + TickRateManager tickratemanager = this.tickRateManager(); + boolean flag = tickratemanager.runsNormally(); + + if (flag) { +- profilerfiller.push("world border"); ++ gameprofilerfiller.push("world border"); + this.getWorldBorder().tick(); +- profilerfiller.popPush("weather"); ++ gameprofilerfiller.popPush("weather"); + this.advanceWeatherCycle(); + } + +@@ -305,12 +353,20 @@ + long j; + + if (this.sleepStatus.areEnoughSleeping(i) && this.sleepStatus.areEnoughDeepSleeping(i, this.players)) { ++ // CraftBukkit start ++ j = this.levelData.getDayTime() + 24000L; ++ TimeSkipEvent event = new TimeSkipEvent(this.getWorld(), TimeSkipEvent.SkipReason.NIGHT_SKIP, (j - j % 24000L) - this.getDayTime()); + if (this.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)) { +- j = this.levelData.getDayTime() + 24000L; +- this.setDayTime(j - j % 24000L); ++ 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(); + } +@@ -321,52 +377,52 @@ + this.tickTime(); + } + +- profilerfiller.popPush("tickPending"); ++ gameprofilerfiller.popPush("tickPending"); + if (!this.isDebug() && flag) { + j = this.getGameTime(); +- profilerfiller.push("blockTicks"); ++ gameprofilerfiller.push("blockTicks"); + this.blockTicks.tick(j, 65536, this::tickBlock); +- profilerfiller.popPush("fluidTicks"); ++ gameprofilerfiller.popPush("fluidTicks"); + this.fluidTicks.tick(j, 65536, this::tickFluid); +- profilerfiller.pop(); ++ gameprofilerfiller.pop(); + } + +- profilerfiller.popPush("raid"); ++ gameprofilerfiller.popPush("raid"); + if (flag) { + this.raids.tick(); + } + +- profilerfiller.popPush("chunkSource"); +- this.getChunkSource().tick(booleansupplier, true); +- profilerfiller.popPush("blockEvents"); ++ gameprofilerfiller.popPush("chunkSource"); ++ this.getChunkSource().tick(hasTimeLeft, true); ++ gameprofilerfiller.popPush("blockEvents"); + if (flag) { + this.runBlockEvents(); + } + + this.handlingTick = false; +- profilerfiller.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) { +- profilerfiller.push("entities"); ++ gameprofilerfiller.push("entities"); + if (this.dragonFight != null && flag) { +- profilerfiller.push("dragonFight"); ++ gameprofilerfiller.push("dragonFight"); + this.dragonFight.tick(); +- profilerfiller.pop(); ++ gameprofilerfiller.pop(); + } + + 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)) { +- profilerfiller.push("checkDespawn"); ++ gameprofilerfiller.push("checkDespawn"); + entity.checkDespawn(); +- profilerfiller.pop(); ++ gameprofilerfiller.pop(); + if (this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(entity.chunkPosition().toLong())) { + Entity entity1 = entity.getVehicle(); + +@@ -378,26 +434,25 @@ + entity.stopRiding(); + } + +- profilerfiller.push("tick"); ++ gameprofilerfiller.push("tick"); + this.guardEntityTick(this::tickNonPassenger, entity); +- profilerfiller.pop(); ++ gameprofilerfiller.pop(); + } + } + } + }); +- profilerfiller.pop(); ++ gameprofilerfiller.pop(); + this.tickBlockEntities(); + } + +- profilerfiller.push("entityManagement"); ++ gameprofilerfiller.push("entityManagement"); + this.entityManager.tick(); +- profilerfiller.pop(); ++ gameprofilerfiller.pop(); + } + + @Override +- @Override +- public boolean shouldTickBlocksAt(long i) { +- return this.chunkSource.chunkMap.getDistanceManager().inBlockTickingRange(i); ++ public boolean shouldTickBlocksAt(long chunkPos) { ++ return this.chunkSource.chunkMap.getDistanceManager().inBlockTickingRange(chunkPos); + } + + protected void tickTime() { +@@ -413,185 +468,185 @@ + } + } + +- public void setDayTime(long i) { +- this.serverLevelData.setDayTime(i); ++ public void setDayTime(long time) { ++ this.serverLevelData.setDayTime(time); + } + +- public void tickCustomSpawners(boolean flag, boolean flag1) { ++ public void tickCustomSpawners(boolean spawnEnemies, boolean spawnFriendlies) { + Iterator iterator = this.customSpawners.iterator(); + + while (iterator.hasNext()) { +- CustomSpawner customspawner = (CustomSpawner) iterator.next(); ++ CustomSpawner mobspawner = (CustomSpawner) iterator.next(); + +- customspawner.tick(this, flag, flag1); ++ mobspawner.tick(this, spawnEnemies, spawnFriendlies); + } + + } + + private boolean shouldDiscardEntity(Entity entity) { +- return !this.server.isSpawningAnimals() && (entity instanceof Animal || entity instanceof WaterAnimal) ? true : !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(); +- ((List) 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 levelchunk, int i) { +- ChunkPos chunkpos = levelchunk.getPos(); ++ public void tickChunk(LevelChunk chunk, int randomTickSpeed) { ++ ChunkPos chunkcoordintpair = chunk.getPos(); + boolean flag = this.isRaining(); +- int j = chunkpos.getMinBlockX(); +- int k = chunkpos.getMinBlockZ(); +- ProfilerFiller profilerfiller = this.getProfiler(); ++ int j = chunkcoordintpair.getMinBlockX(); ++ int k = chunkcoordintpair.getMinBlockZ(); ++ ProfilerFiller gameprofilerfiller = this.getProfiler(); + +- profilerfiller.push("thunder"); ++ gameprofilerfiller.push("thunder"); + if (flag && this.isThundering() && this.random.nextInt(100000) == 0) { +- BlockPos blockpos = this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15)); ++ BlockPos blockposition = this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15)); + +- if (this.isRainingAt(blockpos)) { +- DifficultyInstance difficultyinstance = this.getCurrentDifficultyAt(blockpos); +- boolean flag1 = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && this.random.nextDouble() < (double) difficultyinstance.getEffectiveDifficulty() * 0.01D && !this.getBlockState(blockpos.below()).is(Blocks.LIGHTNING_ROD); ++ 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 skeletonhorse = (SkeletonHorse) EntityType.SKELETON_HORSE.create(this); ++ SkeletonHorse entityhorseskeleton = (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); ++ 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 = (LightningBolt) EntityType.LIGHTNING_BOLT.create(this); ++ LightningBolt entitylightning = (LightningBolt) EntityType.LIGHTNING_BOLT.create(this); + +- if (lightningbolt != null) { +- lightningbolt.moveTo(Vec3.atBottomCenterOf(blockpos)); +- lightningbolt.setVisualOnly(flag1); +- this.addFreshEntity(lightningbolt); ++ if (entitylightning != null) { ++ entitylightning.moveTo(Vec3.atBottomCenterOf(blockposition)); ++ entitylightning.setVisualOnly(flag1); ++ this.strikeLightning(entitylightning, org.bukkit.event.weather.LightningStrikeEvent.Cause.WEATHER); // CraftBukkit + } + } + } + +- profilerfiller.popPush("iceandsnow"); ++ gameprofilerfiller.popPush("iceandsnow"); + +- for (int l = 0; l < i; ++l) { ++ for (int l = 0; l < randomTickSpeed; ++l) { + if (this.random.nextInt(48) == 0) { + this.tickPrecipitation(this.getBlockRandomPos(j, 0, k, 15)); + } + } + +- profilerfiller.popPush("tickBlocks"); +- if (i > 0) { +- LevelChunkSection[] alevelchunksection = levelchunk.getSections(); ++ gameprofilerfiller.popPush("tickBlocks"); ++ if (randomTickSpeed > 0) { ++ LevelChunkSection[] achunksection = chunk.getSections(); + +- for (int i1 = 0; i1 < alevelchunksection.length; ++i1) { +- LevelChunkSection levelchunksection = alevelchunksection[i1]; ++ for (int i1 = 0; i1 < achunksection.length; ++i1) { ++ LevelChunkSection chunksection = achunksection[i1]; + +- if (levelchunksection.isRandomlyTicking()) { +- int j1 = levelchunk.getSectionYFromSectionIndex(i1); ++ if (chunksection.isRandomlyTicking()) { ++ int j1 = chunk.getSectionYFromSectionIndex(i1); + int k1 = SectionPos.sectionToBlockCoord(j1); + +- for (int l1 = 0; l1 < i; ++l1) { +- BlockPos blockpos1 = this.getBlockRandomPos(j, k1, k, 15); ++ for (int l1 = 0; l1 < randomTickSpeed; ++l1) { ++ BlockPos blockposition1 = this.getBlockRandomPos(j, k1, k, 15); + +- profilerfiller.push("randomTick"); +- BlockState blockstate = levelchunksection.getBlockState(blockpos1.getX() - j, blockpos1.getY() - k1, blockpos1.getZ() - k); ++ gameprofilerfiller.push("randomTick"); ++ IBlockData iblockdata = chunksection.getBlockState(blockposition1.getX() - j, blockposition1.getY() - k1, blockposition1.getZ() - k); + +- if (blockstate.isRandomlyTicking()) { +- blockstate.randomTick(this, blockpos1, this.random); ++ if (iblockdata.isRandomlyTicking()) { ++ iblockdata.randomTick(this, blockposition1, this.random); + } + +- FluidState fluidstate = blockstate.getFluidState(); ++ FluidState fluid = iblockdata.getFluidState(); + +- if (fluidstate.isRandomlyTicking()) { +- fluidstate.randomTick(this, blockpos1, this.random); ++ if (fluid.isRandomlyTicking()) { ++ fluid.randomTick(this, blockposition1, this.random); + } + +- profilerfiller.pop(); ++ gameprofilerfiller.pop(); + } + } + } + } + +- profilerfiller.pop(); ++ gameprofilerfiller.pop(); + } + + @VisibleForTesting +- public void tickPrecipitation(BlockPos blockpos) { +- BlockPos blockpos1 = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, blockpos); +- BlockPos blockpos2 = blockpos1.below(); +- Biome biome = (Biome) this.getBiome(blockpos1).value(); ++ 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 (biome.shouldFreeze(this, blockpos2)) { +- this.setBlockAndUpdate(blockpos2, Blocks.ICE.defaultBlockState()); ++ if (biomebase.shouldFreeze(this, blockposition2)) { ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition2, Blocks.ICE.defaultBlockState(), null); // CraftBukkit + } + + if (this.isRaining()) { + int i = this.getGameRules().getInt(GameRules.RULE_SNOW_ACCUMULATION_HEIGHT); + +- if (i > 0 && biome.shouldSnow(this, blockpos1)) { +- BlockState blockstate = this.getBlockState(blockpos1); ++ if (i > 0 && biomebase.shouldSnow(this, blockposition1)) { ++ IBlockData iblockdata = this.getBlockState(blockposition1); + +- if (blockstate.is(Blocks.SNOW)) { +- int j = (Integer) blockstate.getValue(SnowLayerBlock.LAYERS); ++ if (iblockdata.is(Blocks.SNOW)) { ++ int j = (Integer) iblockdata.getValue(SnowLayerBlock.LAYERS); + + if (j < Math.min(i, 8)) { +- BlockState blockstate1 = (BlockState) blockstate.setValue(SnowLayerBlock.LAYERS, j + 1); ++ IBlockData iblockdata1 = (IBlockData) iblockdata.setValue(SnowLayerBlock.LAYERS, j + 1); + +- Block.pushEntitiesUp(blockstate, blockstate1, this, blockpos1); +- this.setBlockAndUpdate(blockpos1, blockstate1); ++ Block.pushEntitiesUp(iblockdata, iblockdata1, this, blockposition1); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition1, iblockdata1, null); // CraftBukkit + } + } else { +- this.setBlockAndUpdate(blockpos1, Blocks.SNOW.defaultBlockState()); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition1, Blocks.SNOW.defaultBlockState(), null); // CraftBukkit + } + } + +- Biome.Precipitation biome_precipitation = biome.getPrecipitationAt(blockpos2); ++ Biome.Precipitation biomebase_precipitation = biomebase.getPrecipitationAt(blockposition2); + +- if (biome_precipitation != Biome.Precipitation.NONE) { +- BlockState blockstate2 = this.getBlockState(blockpos2); ++ if (biomebase_precipitation != Biome.Precipitation.NONE) { ++ IBlockData iblockdata2 = this.getBlockState(blockposition2); + +- blockstate2.getBlock().handlePrecipitation(blockstate2, this, blockpos2, biome_precipitation); ++ iblockdata2.getBlock().handlePrecipitation(iblockdata2, this, blockposition2, biomebase_precipitation); + } + } + + } + +- private Optional<BlockPos> findLightningRod(BlockPos blockpos) { ++ private Optional<BlockPos> findLightningRod(BlockPos pos) { + Optional<BlockPos> optional = this.getPoiManager().findClosest((holder) -> { + return holder.is(PoiTypes.LIGHTNING_ROD); +- }, (blockpos1) -> { +- return blockpos1.getY() == this.getHeight(Heightmap.Types.WORLD_SURFACE, blockpos1.getX(), blockpos1.getZ()) - 1; +- }, blockpos, 128, PoiManager.Occupancy.ANY); ++ }, (blockposition1) -> { ++ return blockposition1.getY() == this.getHeight(Heightmap.Types.WORLD_SURFACE, blockposition1.getX(), blockposition1.getZ()) - 1; ++ }, pos, 128, PoiManager.Occupancy.ANY); + +- return optional.map((blockpos1) -> { +- return blockpos1.above(1); ++ return optional.map((blockposition1) -> { ++ return blockposition1.above(1); + }); + } + +- protected BlockPos findLightningTargetAround(BlockPos blockpos) { +- BlockPos blockpos1 = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, blockpos); +- Optional<BlockPos> optional = this.findLightningRod(blockpos1); ++ protected BlockPos findLightningTargetAround(BlockPos pos) { ++ BlockPos blockposition1 = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pos); ++ Optional<BlockPos> optional = this.findLightningRod(blockposition1); + + if (optional.isPresent()) { + return (BlockPos) optional.get(); + } else { +- AABB aabb = AABB.encapsulatingFullBlocks(blockpos1, new BlockPos(blockpos1.atY(this.getMaxBuildHeight()))).inflate(3.0D); +- List<LivingEntity> list = this.getEntitiesOfClass(LivingEntity.class, aabb, (livingentity) -> { +- return livingentity != null && livingentity.isAlive() && this.canSeeSky(livingentity.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 (blockpos1.getY() == this.getMinBuildHeight() - 1) { +- blockpos1 = blockpos1.above(2); ++ if (blockposition1.getY() == this.getMinBuildHeight() - 1) { ++ blockposition1 = blockposition1.above(2); + } + +- return blockpos1; ++ return blockposition1; + } + } + } +@@ -608,20 +663,20 @@ + if (this.canSleepThroughNights()) { + if (!this.getServer().isSingleplayer() || this.getServer().isPublished()) { + int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE); +- MutableComponent mutablecomponent; ++ MutableComponent ichatmutablecomponent; + + if (this.sleepStatus.areEnoughSleeping(i)) { +- mutablecomponent = Component.translatable("sleep.skipping_night"); ++ ichatmutablecomponent = Component.translatable("sleep.skipping_night"); + } else { +- mutablecomponent = Component.translatable("sleep.players_sleeping", this.sleepStatus.amountSleeping(), this.sleepStatus.sleepersNeeded(i)); ++ ichatmutablecomponent = Component.translatable("sleep.players_sleeping", this.sleepStatus.amountSleeping(), this.sleepStatus.sleepersNeeded(i)); + } + + Iterator iterator = this.players.iterator(); + + while (iterator.hasNext()) { +- ServerPlayer serverplayer = (ServerPlayer) iterator.next(); ++ ServerPlayer entityplayer = (ServerPlayer) iterator.next(); + +- serverplayer.displayClientMessage(mutablecomponent, true); ++ entityplayer.displayClientMessage(ichatmutablecomponent, true); + } + + } +@@ -636,7 +691,6 @@ + } + + @Override +- @Override + public ServerScoreboard getScoreboard() { + return this.server.getScoreboard(); + } +@@ -707,67 +761,102 @@ + this.rainLevel = Mth.clamp(this.rainLevel, 0.0F, 1.0F); + } + ++ /* CraftBukkit start + if (this.oRainLevel != this.rainLevel) { +- this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.RAIN_LEVEL_CHANGE, this.rainLevel), this.dimension()); ++ this.server.getPlayerList().broadcastAll(new PacketPlayOutGameStateChange(PacketPlayOutGameStateChange.RAIN_LEVEL_CHANGE, this.rainLevel), this.dimension()); + } + + if (this.oThunderLevel != this.thunderLevel) { +- this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE, this.thunderLevel), this.dimension()); ++ this.server.getPlayerList().broadcastAll(new PacketPlayOutGameStateChange(PacketPlayOutGameStateChange.THUNDER_LEVEL_CHANGE, this.thunderLevel), this.dimension()); + } + + if (flag != this.isRaining()) { + if (flag) { +- this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.STOP_RAINING, 0.0F)); ++ this.server.getPlayerList().broadcastAll(new PacketPlayOutGameStateChange(PacketPlayOutGameStateChange.STOP_RAINING, 0.0F)); + } else { +- this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.START_RAINING, 0.0F)); ++ this.server.getPlayerList().broadcastAll(new PacketPlayOutGameStateChange(PacketPlayOutGameStateChange.START_RAINING, 0.0F)); + } + +- this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.RAIN_LEVEL_CHANGE, this.rainLevel)); +- this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE, this.thunderLevel)); ++ this.server.getPlayerList().broadcastAll(new PacketPlayOutGameStateChange(PacketPlayOutGameStateChange.RAIN_LEVEL_CHANGE, this.rainLevel)); ++ this.server.getPlayerList().broadcastAll(new PacketPlayOutGameStateChange(PacketPlayOutGameStateChange.THUNDER_LEVEL_CHANGE, this.thunderLevel)); + } ++ // */ ++ for (int idx = 0; idx < this.players.size(); ++idx) { ++ if (((ServerPlayer) this.players.get(idx)).level() == this) { ++ ((ServerPlayer) this.players.get(idx)).tickWeather(); ++ } ++ } + ++ if (flag != this.isRaining()) { ++ // Only send weather packets to those affected ++ for (int idx = 0; idx < this.players.size(); ++idx) { ++ if (((ServerPlayer) this.players.get(idx)).level() == this) { ++ ((ServerPlayer) this.players.get(idx)).setPlayerWeather((!flag ? WeatherType.DOWNFALL : WeatherType.CLEAR), false); ++ } ++ } ++ } ++ for (int idx = 0; idx < this.players.size(); ++idx) { ++ if (((ServerPlayer) this.players.get(idx)).level() == this) { ++ ((ServerPlayer) this.players.get(idx)).updateWeather(this.oRainLevel, this.rainLevel, this.oThunderLevel, this.thunderLevel); ++ } ++ } ++ // CraftBukkit end ++ + } + + @VisibleForTesting + public void resetWeatherCycle() { +- this.serverLevelData.setRainTime(0); ++ // CraftBukkit start + this.serverLevelData.setRaining(false); +- this.serverLevelData.setThunderTime(0); ++ // If we stop due to everyone sleeping we should reset the weather duration to some other random value. ++ // Not that everyone ever manages to get the whole server to sleep at the same time.... ++ if (!this.serverLevelData.isRaining()) { ++ this.serverLevelData.setRainTime(0); ++ } ++ // CraftBukkit end + this.serverLevelData.setThundering(false); ++ // CraftBukkit start ++ // If we stop due to everyone sleeping we should reset the weather duration to some other random value. ++ // Not that everyone ever manages to get the whole server to sleep at the same time.... ++ if (!this.serverLevelData.isThundering()) { ++ this.serverLevelData.setThunderTime(0); ++ } ++ // CraftBukkit end + } + + public void resetEmptyTime() { + this.emptyTime = 0; + } + +- private void tickFluid(BlockPos blockpos, Fluid fluid) { +- FluidState fluidstate = this.getFluidState(blockpos); ++ private void tickFluid(BlockPos pos, Fluid fluid) { ++ FluidState fluid = this.getFluidState(pos); + +- if (fluidstate.is(fluid)) { +- fluidstate.tick(this, blockpos); ++ if (fluid.is(fluid)) { ++ fluid.tick(this, pos); + } + + } + +- private void tickBlock(BlockPos blockpos, Block block) { +- BlockState blockstate = this.getBlockState(blockpos); ++ private void tickBlock(BlockPos pos, Block block) { ++ IBlockData iblockdata = this.getBlockState(pos); + +- if (blockstate.is(block)) { +- blockstate.tick(this, blockpos, this.random); ++ if (iblockdata.is(block)) { ++ iblockdata.tick(this, pos, this.random); + } + + } + + public void tickNonPassenger(Entity entity) { + entity.setOldPosAndRot(); +- ProfilerFiller profilerfiller = this.getProfiler(); ++ ProfilerFiller gameprofilerfiller = this.getProfiler(); + + ++entity.tickCount; + this.getProfiler().push(() -> { + return BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString(); + }); +- profilerfiller.incrementCounter("tickNonPassenger"); ++ gameprofilerfiller.incrementCounter("tickNonPassenger"); + entity.tick(); ++ entity.postTick(); // CraftBukkit + this.getProfiler().pop(); + Iterator iterator = entity.getPassengers().iterator(); + +@@ -779,91 +868,100 @@ + + } + +- private void tickPassenger(Entity entity, Entity entity1) { +- if (!entity1.isRemoved() && entity1.getVehicle() == entity) { +- if (entity1 instanceof Player || this.entityTickList.contains(entity1)) { +- entity1.setOldPosAndRot(); +- ++entity1.tickCount; +- ProfilerFiller profilerfiller = this.getProfiler(); ++ private void tickPassenger(Entity ridingEntity, Entity passengerEntity) { ++ if (!passengerEntity.isRemoved() && passengerEntity.getVehicle() == ridingEntity) { ++ if (passengerEntity instanceof Player || this.entityTickList.contains(passengerEntity)) { ++ passengerEntity.setOldPosAndRot(); ++ ++passengerEntity.tickCount; ++ ProfilerFiller gameprofilerfiller = this.getProfiler(); + +- profilerfiller.push(() -> { +- return BuiltInRegistries.ENTITY_TYPE.getKey(entity1.getType()).toString(); ++ gameprofilerfiller.push(() -> { ++ return BuiltInRegistries.ENTITY_TYPE.getKey(passengerEntity.getType()).toString(); + }); +- profilerfiller.incrementCounter("tickPassenger"); +- entity1.rideTick(); +- profilerfiller.pop(); +- Iterator iterator = entity1.getPassengers().iterator(); ++ 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(entity1, entity2); ++ this.tickPassenger(passengerEntity, entity2); + } + + } + } else { +- entity1.stopRiding(); ++ passengerEntity.stopRiding(); + } + } + + @Override +- @Override +- public boolean mayInteract(Player player, BlockPos blockpos) { +- return !this.server.isUnderSpawnProtection(this, blockpos, player) && this.getWorldBorder().isWithinBounds(blockpos); ++ public boolean mayInteract(Player player, BlockPos pos) { ++ return !this.server.isUnderSpawnProtection(this, pos, player) && this.getWorldBorder().isWithinBounds(pos); + } + +- public void save(@Nullable ProgressListener progresslistener, boolean flag, boolean flag1) { +- ServerChunkCache serverchunkcache = this.getChunkSource(); ++ public void save(@Nullable ProgressListener progress, boolean flush, boolean skipSave) { ++ ServerChunkCache chunkproviderserver = this.getChunkSource(); + +- if (!flag1) { +- if (progresslistener != null) { +- progresslistener.progressStartNoAbort(Component.translatable("menu.savingLevel")); ++ if (!skipSave) { ++ org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld())); // CraftBukkit ++ if (progress != null) { ++ progress.progressStartNoAbort(Component.translatable("menu.savingLevel")); + } + + this.saveLevelData(); +- if (progresslistener != null) { +- progresslistener.progressStage(Component.translatable("menu.savingChunks")); ++ if (progress != null) { ++ progress.progressStage(Component.translatable("menu.savingChunks")); + } + +- serverchunkcache.save(flag); +- if (flag) { ++ 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(); + } + +- public <T extends Entity> List<? extends T> getEntities(EntityTypeTest<Entity, T> entitytypetest, Predicate<? super T> predicate) { ++ public <T extends Entity> List<? extends T> getEntities(EntityTypeTest<Entity, T> typeTest, Predicate<? super T> predicate) { + List<T> list = Lists.newArrayList(); + +- this.getEntities(entitytypetest, predicate, (List) list); ++ this.getEntities(typeTest, predicate, (List) list); + return list; + } + +- public <T extends Entity> void getEntities(EntityTypeTest<Entity, T> entitytypetest, Predicate<? super T> predicate, List<? super T> list) { +- this.getEntities(entitytypetest, predicate, list, Integer.MAX_VALUE); ++ public <T extends Entity> void getEntities(EntityTypeTest<Entity, T> typeTest, Predicate<? super T> predicate, List<? super T> output) { ++ this.getEntities(typeTest, predicate, output, Integer.MAX_VALUE); + } + +- public <T extends Entity> void getEntities(EntityTypeTest<Entity, T> entitytypetest, Predicate<? super T> predicate, List<? super T> list, int i) { +- this.getEntities().get(entitytypetest, (entity) -> { ++ public <T extends Entity> void getEntities(EntityTypeTest<Entity, T> typeTest, Predicate<? super T> predicate, List<? super T> output, int maxResults) { ++ this.getEntities().get(typeTest, (entity) -> { + if (predicate.test(entity)) { +- list.add(entity); +- if (list.size() >= i) { +- return AbortableIterationConsumer.Continuation.ABORT; ++ output.add(entity); ++ if (output.size() >= maxResults) { ++ return AbortableIterationConsumer.a.ABORT; + } + } + +- return AbortableIterationConsumer.Continuation.CONTINUE; ++ return AbortableIterationConsumer.a.CONTINUE; + }); + } + +@@ -875,16 +973,16 @@ + return this.getPlayers(predicate, Integer.MAX_VALUE); + } + +- public List<ServerPlayer> getPlayers(Predicate<? super ServerPlayer> predicate, int i) { ++ public List<ServerPlayer> getPlayers(Predicate<? super ServerPlayer> predicate, int maxResults) { + List<ServerPlayer> list = Lists.newArrayList(); + Iterator iterator = this.players.iterator(); + + while (iterator.hasNext()) { +- ServerPlayer serverplayer = (ServerPlayer) iterator.next(); ++ ServerPlayer entityplayer = (ServerPlayer) iterator.next(); + +- if (predicate.test(serverplayer)) { +- list.add(serverplayer); +- if (list.size() >= i) { ++ if (predicate.test(entityplayer)) { ++ list.add(entityplayer); ++ if (list.size() >= maxResults) { + return list; + } + } +@@ -901,93 +999,154 @@ + } + + @Override +- @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 addDuringCommandTeleport(ServerPlayer serverplayer) { +- this.addPlayer(serverplayer); ++ public void addDuringTeleport(Entity entity, CreatureSpawnEvent.SpawnReason reason) { ++ this.addEntity(entity, reason); ++ // CraftBukkit end + } + +- public void addDuringPortalTeleport(ServerPlayer serverplayer) { +- this.addPlayer(serverplayer); ++ public void addDuringCommandTeleport(ServerPlayer player) { ++ this.addPlayer(player); + } + +- public void addNewPlayer(ServerPlayer serverplayer) { +- this.addPlayer(serverplayer); ++ public void addDuringPortalTeleport(ServerPlayer player) { ++ this.addPlayer(player); + } + +- public void addRespawnedPlayer(ServerPlayer serverplayer) { +- this.addPlayer(serverplayer); ++ public void addNewPlayer(ServerPlayer player) { ++ this.addPlayer(player); + } + +- private void addPlayer(ServerPlayer serverplayer) { +- Entity entity = (Entity) this.getEntities().get(serverplayer.getUUID()); ++ public void addRespawnedPlayer(ServerPlayer player) { ++ this.addPlayer(player); ++ } + ++ private void addPlayer(ServerPlayer player) { ++ Entity entity = (Entity) this.getEntities().get(player.getUUID()); ++ + if (entity != null) { +- ServerLevel.LOGGER.warn("Force-added player with duplicate UUID {}", serverplayer.getUUID()); ++ ServerLevel.LOGGER.warn("Force-added player with duplicate UUID {}", player.getUUID()); + entity.unRide(); + this.removePlayerImmediately((ServerPlayer) entity, Entity.RemovalReason.DISCARDED); + } + +- this.entityManager.addNewEntity(serverplayer); ++ this.entityManager.addNewEntity(player); + } + +- private boolean addEntity(Entity entity) { ++ // CraftBukkit start ++ private boolean addEntity(Entity entity, CreatureSpawnEvent.SpawnReason spawnReason) { + if (entity.isRemoved()) { +- ServerLevel.LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityType.getKey(entity.getType())); ++ // WorldServer.LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityTypes.getKey(entity.getType())); // CraftBukkit + return false; + } else { ++ // SPIGOT-6415: Don't call spawn event when reason is null. For example when an entity teleports to a new world. ++ if (spawnReason != null && !CraftEventFactory.doEntityAddEventCalling(this, entity, spawnReason)) { ++ return false; ++ } ++ // CraftBukkit end ++ + return this.entityManager.addNewEntity(entity); + } + } + + public boolean tryAddFreshEntityWithPassengers(Entity entity) { +- Stream stream = entity.getSelfAndPassengers().map(Entity::getUUID); ++ // CraftBukkit start ++ return this.tryAddFreshEntityWithPassengers(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT); ++ } ++ ++ public boolean tryAddFreshEntityWithPassengers(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) { ++ // CraftBukkit end ++ Stream<UUID> stream = entity.getSelfAndPassengers().map(Entity::getUUID); // CraftBukkit - decompile error + PersistentEntitySectionManager persistententitysectionmanager = this.entityManager; + + Objects.requireNonNull(this.entityManager); + if (stream.anyMatch(persistententitysectionmanager::isLoaded)) { + return false; + } else { +- this.addFreshEntityWithPassengers(entity); ++ this.addFreshEntityWithPassengers(entity, reason); // CraftBukkit + return true; + } + } + +- public void unload(LevelChunk levelchunk) { +- levelchunk.clearAllBlockEntities(); +- levelchunk.unregisterTickContainerFromLevel(this); ++ public void unload(LevelChunk chunk) { ++ chunk.clearAllBlockEntities(); ++ chunk.unregisterTickContainerFromLevel(this); + } + +- public void removePlayerImmediately(ServerPlayer serverplayer, Entity.RemovalReason entity_removalreason) { +- serverplayer.remove(entity_removalreason); ++ public void removePlayerImmediately(ServerPlayer player, Entity.RemovalReason reason) { ++ 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 +- @Override +- public void destroyBlockProgress(int i, BlockPos blockpos, int j) { ++ public void destroyBlockProgress(int breakerId, BlockPos pos, int 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 serverplayer = (ServerPlayer) iterator.next(); ++ ServerPlayer entityplayer = (ServerPlayer) iterator.next(); + +- if (serverplayer != null && serverplayer.level() == this && serverplayer.getId() != i) { +- double d0 = (double) blockpos.getX() - serverplayer.getX(); +- double d1 = (double) blockpos.getY() - serverplayer.getY(); +- double d2 = (double) blockpos.getZ() - serverplayer.getZ(); ++ 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) { +- serverplayer.connection.send(new ClientboundBlockDestructionPacket(i, blockpos, j)); ++ entityplayer.connection.send(new ClientboundBlockDestructionPacket(breakerId, pos, progress)); + } + } + } +@@ -995,32 +1154,28 @@ + } + + @Override +- @Override +- public void playSeededSound(@Nullable Player player, double d0, double d1, double d2, Holder<SoundEvent> holder, SoundSource soundsource, float f, float f1, long i) { +- this.server.getPlayerList().broadcast(player, d0, d1, d2, (double) ((SoundEvent) holder.value()).getRange(f), this.dimension(), new ClientboundSoundPacket(holder, soundsource, d0, d1, d2, f, f1, i)); ++ 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 +- @Override +- public void playSeededSound(@Nullable Player player, Entity entity, Holder<SoundEvent> holder, SoundSource soundsource, float f, float f1, long i) { +- this.server.getPlayerList().broadcast(player, entity.getX(), entity.getY(), entity.getZ(), (double) ((SoundEvent) holder.value()).getRange(f), this.dimension(), new ClientboundSoundEntityPacket(holder, soundsource, entity, f, f1, i)); ++ 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) ((SoundEvent) sound.value()).getRange(volume), this.dimension(), new ClientboundSoundEntityPacket(sound, category, entity, volume, pitch, seed)); + } + + @Override +- @Override +- public void globalLevelEvent(int i, BlockPos blockpos, int j) { ++ public void globalLevelEvent(int id, BlockPos pos, int data) { + if (this.getGameRules().getBoolean(GameRules.RULE_GLOBAL_SOUND_EVENTS)) { +- this.server.getPlayerList().broadcastAll(new ClientboundLevelEventPacket(i, blockpos, j, true)); ++ this.server.getPlayerList().broadcastAll(new ClientboundLevelEventPacket(id, pos, data, true)); + } else { +- this.levelEvent((Player) null, i, blockpos, j); ++ this.levelEvent((Player) null, id, pos, data); + } + + } + + @Override +- @Override +- public void levelEvent(@Nullable Player player, int i, BlockPos blockpos, int j) { +- this.server.getPlayerList().broadcast(player, (double) blockpos.getX(), (double) blockpos.getY(), (double) blockpos.getZ(), 64.0D, this.dimension(), new ClientboundLevelEventPacket(i, blockpos, j, false)); ++ 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.0D, this.dimension(), new ClientboundLevelEventPacket(type, pos, data, false)); + } + + public int getLogicalHeight() { +@@ -1028,34 +1183,43 @@ + } + + @Override +- @Override +- public void gameEvent(GameEvent gameevent, Vec3 vec3, GameEvent.Context gameevent_context) { +- this.gameEventDispatcher.post(gameevent, vec3, gameevent_context); ++ public void gameEvent(GameEvent event, Vec3 position, GameEvent.Context context) { ++ this.gameEventDispatcher.post(event, position, context); + } + + @Override +- @Override +- public void sendBlockUpdated(BlockPos blockpos, BlockState blockstate, BlockState blockstate1, int i) { ++ public void sendBlockUpdated(BlockPos pos, IBlockData oldState, IBlockData newState, int flags) { + if (this.isUpdatingNavigations) { + String s = "recursive call to sendBlockUpdated"; + + Util.logAndPauseIfInIde("recursive call to sendBlockUpdated", new IllegalStateException("recursive call to sendBlockUpdated")); + } + +- this.getChunkSource().blockChanged(blockpos); +- VoxelShape voxelshape = blockstate.getCollisionShape(this, blockpos); +- VoxelShape voxelshape1 = blockstate1.getCollisionShape(this, blockpos); ++ this.getChunkSource().blockChanged(pos); ++ VoxelShape voxelshape = oldState.getCollisionShape(this, pos); ++ VoxelShape voxelshape1 = newState.getCollisionShape(this, pos); + + if (Shapes.joinIsNotEmpty(voxelshape, voxelshape1, BooleanOp.NOT_SAME)) { + List<PathNavigation> list = new ObjectArrayList(); + Iterator iterator = this.navigatingMobs.iterator(); + + while (iterator.hasNext()) { +- Mob mob = (Mob) iterator.next(); +- PathNavigation pathnavigation = mob.getNavigation(); ++ // 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 (pathnavigation.shouldRecomputePath(blockpos)) { +- list.add(pathnavigation); ++ if (navigationabstract.shouldRecomputePath(pos)) { ++ list.add(navigationabstract); + } + } + +@@ -1064,9 +1228,9 @@ + iterator = list.iterator(); + + while (iterator.hasNext()) { +- PathNavigation pathnavigation1 = (PathNavigation) iterator.next(); ++ PathNavigation navigationabstract1 = (PathNavigation) iterator.next(); + +- pathnavigation1.recomputePath(); ++ navigationabstract1.recomputePath(); + } + } finally { + this.isUpdatingNavigations = false; +@@ -1076,51 +1240,48 @@ + } + + @Override +- @Override +- public void updateNeighborsAt(BlockPos blockpos, Block block) { +- this.neighborUpdater.updateNeighborsAtExceptFromFacing(blockpos, block, (Direction) null); ++ public void updateNeighborsAt(BlockPos pos, Block block) { ++ this.neighborUpdater.updateNeighborsAtExceptFromFacing(pos, block, (Direction) null); + } + + @Override +- @Override +- public void updateNeighborsAtExceptFromFacing(BlockPos blockpos, Block block, Direction direction) { +- this.neighborUpdater.updateNeighborsAtExceptFromFacing(blockpos, block, direction); ++ public void updateNeighborsAtExceptFromFacing(BlockPos pos, Block blockType, Direction skipSide) { ++ this.neighborUpdater.updateNeighborsAtExceptFromFacing(pos, blockType, skipSide); + } + + @Override +- @Override +- public void neighborChanged(BlockPos blockpos, Block block, BlockPos blockpos1) { +- this.neighborUpdater.neighborChanged(blockpos, block, blockpos1); ++ public void neighborChanged(BlockPos pos, Block block, BlockPos fromPos) { ++ this.neighborUpdater.neighborChanged(pos, block, fromPos); + } + + @Override +- @Override +- public void neighborChanged(BlockState blockstate, BlockPos blockpos, Block block, BlockPos blockpos1, boolean flag) { +- this.neighborUpdater.neighborChanged(blockstate, blockpos, block, blockpos1, flag); ++ public void neighborChanged(IBlockData state, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving) { ++ this.neighborUpdater.neighborChanged(state, pos, block, fromPos, isMoving); + } + + @Override +- @Override +- public void broadcastEntityEvent(Entity entity, byte b0) { +- this.getChunkSource().broadcastAndSend(entity, new ClientboundEntityEventPacket(entity, b0)); ++ public void broadcastEntityEvent(Entity entity, byte state) { ++ this.getChunkSource().broadcastAndSend(entity, new ClientboundEntityEventPacket(entity, state)); + } + + @Override +- @Override +- public void broadcastDamageEvent(Entity entity, DamageSource damagesource) { +- this.getChunkSource().broadcastAndSend(entity, new ClientboundDamageEventPacket(entity, damagesource)); ++ public void broadcastDamageEvent(Entity entity, DamageSource damageSource) { ++ this.getChunkSource().broadcastAndSend(entity, new ClientboundDamageEventPacket(entity, damageSource)); + } + + @Override +- @Override + public ServerChunkCache getChunkSource() { + return this.chunkSource; + } + + @Override +- @Override +- public Explosion explode(@Nullable Entity entity, @Nullable DamageSource damagesource, @Nullable ExplosionDamageCalculator explosiondamagecalculator, double d0, double d1, double d2, float f, boolean flag, Level.ExplosionInteraction level_explosioninteraction, ParticleOptions particleoptions, ParticleOptions particleoptions1, SoundEvent soundevent) { +- Explosion explosion = this.explode(entity, damagesource, explosiondamagecalculator, d0, d1, d2, f, flag, level_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(); +@@ -1129,10 +1290,10 @@ + Iterator iterator = this.players.iterator(); + + while (iterator.hasNext()) { +- ServerPlayer serverplayer = (ServerPlayer) iterator.next(); ++ ServerPlayer entityplayer = (ServerPlayer) iterator.next(); + +- if (serverplayer.distanceToSqr(d0, d1, d2) < 4096.0D) { +- serverplayer.connection.send(new ClientboundExplodePacket(d0, d1, d2, f, explosion.getToBlow(), (Vec3) explosion.getHitPlayers().get(serverplayer), explosion.getBlockInteraction(), explosion.getSmallExplosionParticles(), explosion.getLargeExplosionParticles(), explosion.getExplosionSound())); ++ 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())); + } + } + +@@ -1140,50 +1301,46 @@ + } + + @Override +- @Override +- public void blockEvent(BlockPos blockpos, Block block, int i, int j) { +- this.blockEvents.add(new BlockEventData(blockpos, block, i, j)); ++ public void blockEvent(BlockPos pos, Block block, int eventID, int eventParam) { ++ this.blockEvents.add(new BlockEventData(pos, block, eventID, eventParam)); + } + + private void runBlockEvents() { + this.blockEventsToReschedule.clear(); + + while (!this.blockEvents.isEmpty()) { +- BlockEventData blockeventdata = (BlockEventData) this.blockEvents.removeFirst(); ++ BlockEventData blockactiondata = (BlockEventData) this.blockEvents.removeFirst(); + +- if (this.shouldTickBlocksAt(blockeventdata.pos())) { +- if (this.doBlockEvent(blockeventdata)) { +- this.server.getPlayerList().broadcast((Player) null, (double) blockeventdata.pos().getX(), (double) blockeventdata.pos().getY(), (double) blockeventdata.pos().getZ(), 64.0D, this.dimension(), new ClientboundBlockEventPacket(blockeventdata.pos(), blockeventdata.block(), blockeventdata.paramA(), blockeventdata.paramB())); ++ 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); + } + } + + this.blockEvents.addAll(this.blockEventsToReschedule); + } + +- private boolean doBlockEvent(BlockEventData blockeventdata) { +- BlockState blockstate = this.getBlockState(blockeventdata.pos()); ++ private boolean doBlockEvent(BlockEventData event) { ++ IBlockData iblockdata = this.getBlockState(event.pos()); + +- return blockstate.is(blockeventdata.block()) ? blockstate.triggerEvent(this, blockeventdata.pos(), blockeventdata.paramA(), blockeventdata.paramB()) : false; ++ return iblockdata.is(event.block()) ? iblockdata.triggerEvent(this, event.pos(), event.paramA(), event.paramB()) : false; + } + + @Override +- @Override + public LevelTicks<Block> getBlockTicks() { + return this.blockTicks; + } + + @Override +- @Override + public LevelTicks<Fluid> getFluidTicks() { + return this.fluidTicks; + } + + @Nonnull + @Override +- @Override + public MinecraftServer getServer() { + return this.server; + } +@@ -1196,14 +1353,21 @@ + return this.server.getStructureManager(); + } + +- public <T extends ParticleOptions> int sendParticles(T t0, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6) { +- ClientboundLevelParticlesPacket clientboundlevelparticlespacket = new ClientboundLevelParticlesPacket(t0, false, d0, d1, d2, (float) d3, (float) d4, (float) d5, (float) d6, i); ++ 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); ++ } ++ ++ 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 serverplayer = (ServerPlayer) this.players.get(k); ++ ServerPlayer entityplayer = (ServerPlayer) this.players.get(k); ++ if (sender != null && !entityplayer.getBukkitEntity().canSee(sender.getBukkitEntity())) continue; // CraftBukkit + +- if (this.sendParticles(serverplayer, false, d0, d1, d2, clientboundlevelparticlespacket)) { ++ if (this.sendParticles(entityplayer, force, d0, d1, d2, packetplayoutworldparticles)) { // CraftBukkit + ++j; + } + } +@@ -1211,20 +1375,20 @@ + return j; + } + +- public <T extends ParticleOptions> boolean sendParticles(ServerPlayer serverplayer, T t0, boolean flag, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6) { +- Packet<?> packet = new ClientboundLevelParticlesPacket(t0, flag, d0, d1, d2, (float) d3, (float) d4, (float) d5, (float) d6, i); ++ 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(serverplayer, flag, d0, d1, d2, packet); ++ return this.sendParticles(player, longDistance, posX, d1, posY, packet); + } + +- private boolean sendParticles(ServerPlayer serverplayer, boolean flag, double d0, double d1, double d2, Packet<?> packet) { +- if (serverplayer.level() != this) { ++ private boolean sendParticles(ServerPlayer player, boolean longDistance, double posX, double d1, double posY, Packet<?> packet) { ++ if (player.level() != this) { + return false; + } else { +- BlockPos blockpos = serverplayer.blockPosition(); ++ BlockPos blockposition = player.blockPosition(); + +- if (blockpos.closerToCenterThan(new Vec3(d0, d1, d2), flag ? 512.0D : 32.0D)) { +- serverplayer.connection.send(packet); ++ if (blockposition.closerToCenterThan(new Vec3(posX, d1, posY), longDistance ? 512.0D : 32.0D)) { ++ player.connection.send(packet); + return true; + } else { + return false; +@@ -1234,36 +1398,35 @@ + + @Nullable + @Override +- @Override +- public Entity getEntity(int i) { +- return (Entity) this.getEntities().get(i); ++ public Entity getEntity(int id) { ++ return (Entity) this.getEntities().get(id); + } + + /** @deprecated */ + @Deprecated + @Nullable +- public Entity getEntityOrPart(int i) { +- Entity entity = (Entity) this.getEntities().get(i); ++ public Entity getEntityOrPart(int id) { ++ Entity entity = (Entity) this.getEntities().get(id); + +- return entity != null ? entity : (Entity) this.dragonParts.get(i); ++ return entity != null ? entity : (Entity) this.dragonParts.get(id); + } + + @Nullable +- public Entity getEntity(UUID uuid) { +- return (Entity) this.getEntities().get(uuid); ++ public Entity getEntity(UUID uniqueId) { ++ return (Entity) this.getEntities().get(uniqueId); + } + + @Nullable +- public BlockPos findNearestMapStructure(TagKey<Structure> tagkey, BlockPos blockpos, int i, boolean flag) { +- if (!this.server.getWorldData().worldGenOptions().generateStructures()) { ++ public BlockPos findNearestMapStructure(TagKey<Structure> structureTag, BlockPos pos, int radius, boolean skipExistingChunks) { ++ if (!this.serverLevelData.worldGenOptions().generateStructures()) { // CraftBukkit + return null; + } else { +- Optional<HolderSet.Named<Structure>> optional = this.registryAccess().registryOrThrow(Registries.STRUCTURE).getTag(tagkey); ++ 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, (HolderSet) optional.get(), blockpos, i, flag); ++ Pair<BlockPos, Holder<Structure>> pair = this.getChunkSource().getGenerator().findNearestMapStructure(this, (HolderSet) optional.get(), pos, radius, skipExistingChunks); + + return pair != null ? (BlockPos) pair.getFirst() : null; + } +@@ -1271,24 +1434,21 @@ + } + + @Nullable +- public Pair<BlockPos, Holder<Biome>> findClosestBiome3d(Predicate<Holder<Biome>> predicate, BlockPos blockpos, int i, int j, int k) { +- return this.getChunkSource().getGenerator().getBiomeSource().findClosestBiome3d(blockpos, i, j, k, predicate, 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 +- @Override + public RecipeManager getRecipeManager() { + return this.server.getRecipeManager(); + } + + @Override +- @Override + public TickRateManager tickRateManager() { + return this.server.tickRateManager(); + } + + @Override +- @Override + public boolean noSave() { + return this.noSave; + } +@@ -1299,86 +1459,92 @@ + + @Nullable + @Override +- @Override +- public MapItemSavedData getMapData(String s) { +- return (MapItemSavedData) this.getServer().overworld().getDataStorage().get(MapItemSavedData.factory(), s); ++ public MapItemSavedData getMapData(String 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 +- @Override +- public void setMapData(String s, MapItemSavedData mapitemsaveddata) { +- this.getServer().overworld().getDataStorage().set(s, mapitemsaveddata); ++ 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 +- @Override + public int getFreeMapId() { + return ((MapIndex) this.getServer().overworld().getDataStorage().computeIfAbsent(MapIndex.factory(), "idcounts")).getFreeAuxValueForMap(); + } + +- public void setDefaultSpawnPos(BlockPos blockpos, float f) { +- ChunkPos chunkpos = new ChunkPos(new BlockPos(this.levelData.getXSpawn(), 0, this.levelData.getZSpawn())); ++ public void setDefaultSpawnPos(BlockPos pos, float angle) { ++ ChunkPos chunkcoordintpair = new ChunkPos(new BlockPos(this.levelData.getXSpawn(), 0, this.levelData.getZSpawn())); + +- this.levelData.setSpawn(blockpos, f); +- this.getChunkSource().removeRegionTicket(TicketType.START, chunkpos, 11, Unit.INSTANCE); +- this.getChunkSource().addRegionTicket(TicketType.START, new ChunkPos(blockpos), 11, Unit.INSTANCE); +- this.getServer().getPlayerList().broadcastAll(new ClientboundSetDefaultSpawnPositionPacket(blockpos, f)); ++ this.levelData.setSpawn(pos, angle); ++ 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 = (ForcedChunksSavedData) this.getDataStorage().get(ForcedChunksSavedData.factory(), "chunks"); ++ ForcedChunksSavedData forcedchunk = (ForcedChunksSavedData) this.getDataStorage().get(ForcedChunksSavedData.factory(), "chunks"); + +- return (LongSet) (forcedchunkssaveddata != null ? LongSets.unmodifiable(forcedchunkssaveddata.getChunks()) : LongSets.EMPTY_SET); ++ return (LongSet) (forcedchunk != null ? LongSets.unmodifiable(forcedchunk.getChunks()) : LongSets.EMPTY_SET); + } + +- public boolean setChunkForced(int i, int j, boolean flag) { +- ForcedChunksSavedData forcedchunkssaveddata = (ForcedChunksSavedData) this.getDataStorage().computeIfAbsent(ForcedChunksSavedData.factory(), "chunks"); +- ChunkPos chunkpos = new ChunkPos(i, j); +- long k = chunkpos.toLong(); ++ public boolean setChunkForced(int chunkX, int chunkZ, boolean add) { ++ ForcedChunksSavedData forcedchunk = (ForcedChunksSavedData) this.getDataStorage().computeIfAbsent(ForcedChunksSavedData.factory(), "chunks"); ++ ChunkPos chunkcoordintpair = new ChunkPos(chunkX, chunkZ); ++ long k = chunkcoordintpair.toLong(); + boolean flag1; + +- if (flag) { +- flag1 = forcedchunkssaveddata.getChunks().add(k); ++ if (add) { ++ flag1 = forcedchunk.getChunks().add(k); + if (flag1) { +- this.getChunk(i, j); ++ this.getChunk(chunkX, chunkZ); + } + } else { +- flag1 = forcedchunkssaveddata.getChunks().remove(k); ++ flag1 = forcedchunk.getChunks().remove(k); + } + +- forcedchunkssaveddata.setDirty(flag1); ++ forcedchunk.setDirty(flag1); + if (flag1) { +- this.getChunkSource().updateChunkForced(chunkpos, flag); ++ this.getChunkSource().updateChunkForced(chunkcoordintpair, add); + } + + return flag1; + } + + @Override +- @Override + public List<ServerPlayer> players() { + return this.players; + } + + @Override +- @Override +- public void onBlockStateChange(BlockPos blockpos, BlockState blockstate, BlockState blockstate1) { +- Optional<Holder<PoiType>> optional = PoiTypes.forState(blockstate); +- Optional<Holder<PoiType>> optional1 = PoiTypes.forState(blockstate1); ++ 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 blockpos1 = blockpos.immutable(); ++ BlockPos blockposition1 = pos.immutable(); + + optional.ifPresent((holder) -> { + this.getServer().execute(() -> { +- this.getPoiManager().remove(blockpos1); +- DebugPackets.sendPoiRemovedPacket(this, blockpos1); ++ this.getPoiManager().remove(blockposition1); ++ DebugPackets.sendPoiRemovedPacket(this, blockposition1); + }); + }); + optional1.ifPresent((holder) -> { + this.getServer().execute(() -> { +- this.getPoiManager().add(blockpos1, holder); +- DebugPackets.sendPoiAddedPacket(this, blockpos1); ++ this.getPoiManager().add(blockposition1, holder); ++ DebugPackets.sendPoiAddedPacket(this, blockposition1); + }); + }); + } +@@ -1388,20 +1554,20 @@ + return this.getChunkSource().getPoiManager(); + } + +- public boolean isVillage(BlockPos blockpos) { +- return this.isCloseToVillage(blockpos, 1); ++ public boolean isVillage(BlockPos pos) { ++ return this.isCloseToVillage(pos, 1); + } + +- public boolean isVillage(SectionPos sectionpos) { +- return this.isVillage(sectionpos.center()); ++ public boolean isVillage(SectionPos pos) { ++ return this.isVillage(pos.center()); + } + +- public boolean isCloseToVillage(BlockPos blockpos, int i) { +- return i > 6 ? false : this.sectionsToVillage(SectionPos.of(blockpos)) <= i; ++ public boolean isCloseToVillage(BlockPos pos, int sections) { ++ return sections > 6 ? false : this.sectionsToVillage(SectionPos.of(pos)) <= sections; + } + +- public int sectionsToVillage(SectionPos sectionpos) { +- return this.getPoiManager().sectionsToVillage(sectionpos); ++ public int sectionsToVillage(SectionPos pos) { ++ return this.getPoiManager().sectionsToVillage(pos); + } + + public Raids getRaids() { +@@ -1409,28 +1575,28 @@ + } + + @Nullable +- public Raid getRaidAt(BlockPos blockpos) { +- return this.raids.getNearbyRaid(blockpos, 9216); ++ public Raid getRaidAt(BlockPos pos) { ++ return this.raids.getNearbyRaid(pos, 9216); + } + +- public boolean isRaided(BlockPos blockpos) { +- return this.getRaidAt(blockpos) != null; ++ public boolean isRaided(BlockPos pos) { ++ return this.getRaidAt(pos) != null; + } + +- public void onReputationEvent(ReputationEventType reputationeventtype, Entity entity, ReputationEventHandler reputationeventhandler) { +- reputationeventhandler.onReputationEventFrom(reputationeventtype, entity); ++ public void onReputationEvent(ReputationEventType type, Entity target, ReputationEventHandler host) { ++ host.onReputationEventFrom(type, target); + } + + 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 { +- bufferedwriter.write(String.format(Locale.ROOT, "spawning_chunks: %d\n", chunkmap.getDistanceManager().getNaturalSpawnChunkCount())); +- NaturalSpawner.SpawnState naturalspawner_spawnstate = this.getChunkSource().getLastSpawnState(); ++ bufferedwriter.write(String.format(Locale.ROOT, "spawning_chunks: %d\n", playerchunkmap.getDistanceManager().getNaturalSpawnChunkCount())); ++ NaturalSpawner.SpawnState spawnercreature_d = this.getChunkSource().getLastSpawnState(); + +- if (naturalspawner_spawnstate != null) { +- ObjectIterator objectiterator = naturalspawner_spawnstate.getMobCategoryCounts().object2IntEntrySet().iterator(); ++ if (spawnercreature_d != null) { ++ ObjectIterator objectiterator = spawnercreature_d.getMobCategoryCounts().object2IntEntrySet().iterator(); + + while (objectiterator.hasNext()) { + Entry<MobCategory> entry = (Entry) objectiterator.next(); +@@ -1443,7 +1609,7 @@ + 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("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) { +@@ -1488,7 +1654,7 @@ + BufferedWriter bufferedwriter2 = Files.newBufferedWriter(path1); + + try { +- chunkmap.dumpChunks(bufferedwriter2); ++ playerchunkmap.dumpChunks(bufferedwriter2); + } catch (Throwable throwable4) { + if (bufferedwriter2 != null) { + try { +@@ -1570,52 +1736,55 @@ + + } + +- private static void dumpEntities(Writer writer, Iterable<Entity> iterable) 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); +- Iterator iterator = iterable.iterator(); ++ private static void dumpEntities(Writer writer, Iterable<Entity> entities) throws IOException { ++ 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(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); +- Component component = entity.getCustomName(); +- Component component1 = entity.getDisplayName(); ++ Component ichatbasecomponent = entity.getCustomName(); ++ Component ichatbasecomponent1 = entity.getDisplayName(); + +- csvoutput.writeRow(entity.getX(), entity.getY(), entity.getZ(), entity.getUUID(), BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()), entity.isAlive(), component1.getString(), component != null ? component.getString() : null); ++ 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 writer) throws IOException { +- CsvOutput csvoutput = CsvOutput.builder().addColumn("x").addColumn("y").addColumn("z").addColumn("type").build(writer); ++ private void dumpBlockEntityTickers(Writer output) throws IOException { ++ CsvOutput csvwriter = CsvOutput.builder().addColumn("x").addColumn("y").addColumn("z").addColumn("type").build(output); + Iterator iterator = this.blockEntityTickers.iterator(); + + while (iterator.hasNext()) { + TickingBlockEntity tickingblockentity = (TickingBlockEntity) iterator.next(); +- BlockPos blockpos = tickingblockentity.getPos(); ++ BlockPos blockposition = tickingblockentity.getPos(); + +- csvoutput.writeRow(blockpos.getX(), blockpos.getY(), blockpos.getZ(), tickingblockentity.getType()); ++ csvwriter.writeRow(blockposition.getX(), blockposition.getY(), blockposition.getZ(), tickingblockentity.getType()); + } + + } + + @VisibleForTesting +- public void clearBlockEvents(BoundingBox boundingbox) { +- this.blockEvents.removeIf((blockeventdata) -> { +- return boundingbox.isInside(blockeventdata.pos()); ++ public void clearBlockEvents(BoundingBox boundingBox) { ++ this.blockEvents.removeIf((blockactiondata) -> { ++ return boundingBox.isInside(blockactiondata.pos()); + }); + } + + @Override +- @Override +- public void blockUpdated(BlockPos blockpos, Block block) { ++ public void blockUpdated(BlockPos pos, Block block) { + if (!this.isDebug()) { +- this.updateNeighborsAt(blockpos, block); ++ // CraftBukkit start ++ if (populating) { ++ return; ++ } ++ // CraftBukkit end ++ this.updateNeighborsAt(pos, block); + } + + } + + @Override +- @Override +- public float getShade(Direction direction, boolean flag) { ++ public float getShade(Direction direction, boolean shade) { + return 1.0F; + } + +@@ -1623,19 +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 +- @Override + public long getSeed() { +- return this.server.getWorldData().worldGenOptions().seed(); ++ return this.serverLevelData.worldGenOptions().seed(); // CraftBukkit + } + + @Nullable +@@ -1644,7 +1811,6 @@ + } + + @Override +- @Override + public ServerLevel getLevel() { + return this; + } +@@ -1656,19 +1822,19 @@ + }), this.blockEntityTickers.size(), getTypeCount(this.blockEntityTickers, TickingBlockEntity::getType), this.getBlockTicks().count(), this.getFluidTicks().count(), this.gatherChunkSourceStats()); + } + +- private static <T> String getTypeCount(Iterable<T> iterable, Function<T, String> function) { ++ private static <T> String getTypeCount(Iterable<T> objects, Function<T, String> typeGetter) { + try { + Object2IntOpenHashMap<String> object2intopenhashmap = new Object2IntOpenHashMap(); +- Iterator iterator = iterable.iterator(); ++ Iterator<T> iterator = objects.iterator(); // CraftBukkit - decompile error + + while (iterator.hasNext()) { + T t0 = iterator.next(); +- String s = (String) function.apply(t0); ++ String s = (String) typeGetter.apply(t0); + + object2intopenhashmap.addTo(s, 1); + } + +- return (String) object2intopenhashmap.object2IntEntrySet().stream().sorted(Comparator.comparing(Entry::getIntValue).reversed()).limit(5L).map((entry) -> { ++ return (String) object2intopenhashmap.object2IntEntrySet().stream().sorted(Comparator.comparing(Entry<String>::getIntValue).reversed()).limit(5L).map((entry) -> { // CraftBukkit - decompile error + String s1 = (String) entry.getKey(); + + return s1 + ":" + entry.getIntValue(); +@@ -1678,87 +1844,99 @@ + } + } + +- public static void makeObsidianPlatform(ServerLevel serverlevel) { +- BlockPos blockpos = ServerLevel.END_SPAWN_POINT; +- int i = blockpos.getX(); +- int j = blockpos.getY() - 2; +- int k = blockpos.getZ(); ++ public static void makeObsidianPlatform(ServerLevel serverLevel) { ++ // CraftBukkit start ++ ServerLevel.makeObsidianPlatform(serverLevel, null); ++ } + +- BlockPos.betweenClosed(i - 2, j + 1, k - 2, i + 2, j + 3, k + 2).forEach((blockpos1) -> { +- serverlevel.setBlockAndUpdate(blockpos1, Blocks.AIR.defaultBlockState()); ++ 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((blockpos1) -> { +- serverlevel.setBlockAndUpdate(blockpos1, Blocks.OBSIDIAN.defaultBlockState()); ++ 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 +- @Override +- protected LevelEntityGetter<Entity> getEntities() { ++ public LevelEntityGetter<Entity> getEntities() { + return this.entityManager.getEntityGetter(); + } + +- public void addLegacyChunkEntities(Stream<Entity> stream) { +- this.entityManager.addLegacyChunkEntities(stream); ++ public void addLegacyChunkEntities(Stream<Entity> entities) { ++ this.entityManager.addLegacyChunkEntities(entities); + } + +- public void addWorldGenChunkEntities(Stream<Entity> stream) { +- this.entityManager.addWorldGenChunkEntities(stream); ++ public void addWorldGenChunkEntities(Stream<Entity> entities) { ++ this.entityManager.addWorldGenChunkEntities(entities); + } + +- public void startTickingChunk(LevelChunk levelchunk) { +- levelchunk.unpackTicks(this.getLevelData().getGameTime()); ++ public void startTickingChunk(LevelChunk chunk) { ++ chunk.unpackTicks(this.getLevelData().getGameTime()); + } + +- public void onStructureStartsAvailable(ChunkAccess chunkaccess) { ++ public void onStructureStartsAvailable(ChunkAccess chunk) { + this.server.execute(() -> { +- this.structureCheck.onStructureLoad(chunkaccess.getPos(), chunkaccess.getAllStarts()); ++ this.structureCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts()); + }); + } + + @Override +- @Override + public void close() throws IOException { + super.close(); + this.entityManager.close(); + } + + @Override +- @Override + public String gatherChunkSourceStats() { + String s = this.chunkSource.gatherStats(); + + return "Chunks[S] W: " + s + " E: " + this.entityManager.gatherStats(); + } + +- public boolean areEntitiesLoaded(long i) { +- return this.entityManager.areEntitiesLoaded(i); ++ public boolean areEntitiesLoaded(long chunkPos) { ++ return this.entityManager.areEntitiesLoaded(chunkPos); + } + +- private boolean isPositionTickingWithEntitiesLoaded(long i) { +- return this.areEntitiesLoaded(i) && this.chunkSource.isPositionTicking(i); ++ private boolean isPositionTickingWithEntitiesLoaded(long chunkPos) { ++ return this.areEntitiesLoaded(chunkPos) && this.chunkSource.isPositionTicking(chunkPos); + } + +- public boolean isPositionEntityTicking(BlockPos blockpos) { +- return this.entityManager.canPositionTick(blockpos) && this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(ChunkPos.asLong(blockpos)); ++ public boolean isPositionEntityTicking(BlockPos pos) { ++ return this.entityManager.canPositionTick(pos) && this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(ChunkPos.asLong(pos)); + } + +- public boolean isNaturalSpawningAllowed(BlockPos blockpos) { +- return this.entityManager.canPositionTick(blockpos); ++ public boolean isNaturalSpawningAllowed(BlockPos pos) { ++ return this.entityManager.canPositionTick(pos); + } + +- public boolean isNaturalSpawningAllowed(ChunkPos chunkpos) { +- return this.entityManager.canPositionTick(chunkpos); ++ public boolean isNaturalSpawningAllowed(ChunkPos chunkPos) { ++ return this.entityManager.canPositionTick(chunkPos); + } + + @Override +- @Override + public FeatureFlagSet enabledFeatures() { + return this.server.getWorldData().enabledFeatures(); + } + +- public RandomSource getRandomSequence(ResourceLocation resourcelocation) { +- return this.randomSequences.get(resourcelocation); ++ public RandomSource getRandomSequence(ResourceLocation location) { ++ return this.randomSequences.get(location); + } + + public RandomSequences getRandomSequences() { +@@ -1766,50 +1944,44 @@ + } + + @Override +- @Override +- public CrashReportCategory fillReportDetails(CrashReport crashreport) { +- CrashReportCategory crashreportcategory = super.fillReportDetails(crashreport); ++ public CrashReportCategory fillReportDetails(CrashReport report) { ++ CrashReportCategory crashreportsystemdetails = super.fillReportDetails(report); + +- crashreportcategory.setDetail("Loaded entity count", () -> { ++ crashreportsystemdetails.setDetail("Loaded entity count", () -> { + return String.valueOf(this.entityManager.count()); + }); +- return crashreportcategory; ++ return crashreportsystemdetails; + } + +- private final class EntityCallbacks implements LevelCallback<Entity> { ++ private final class a implements LevelCallback<Entity> { + +- EntityCallbacks() {} ++ a() {} + +- @Override + public void onCreated(Entity entity) {} + +- @Override + 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 serverplayer = (ServerPlayer) entity; ++ ServerPlayer entityplayer = (ServerPlayer) entity; + +- ServerLevel.this.players.add(serverplayer); ++ ServerLevel.this.players.add(entityplayer); + ServerLevel.this.updateSleepingPlayerList(); + } + + if (entity instanceof Mob) { +- Mob mob = (Mob) entity; ++ Mob entityinsentient = (Mob) entity; + + if (ServerLevel.this.isUpdatingNavigations) { + String s = "onTrackingStart called during navigation iteration"; +@@ -1817,36 +1989,37 @@ + 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 enderdragon = (EnderDragon) entity; +- EnderDragonPart[] aenderdragonpart = enderdragon.getSubEntities(); +- int i = aenderdragonpart.length; ++ EnderDragon entityenderdragon = (EnderDragon) entity; ++ EnderDragonPart[] aentitycomplexpart = entityenderdragon.getSubEntities(); ++ int i = aentitycomplexpart.length; + + for (int j = 0; j < i; ++j) { +- EnderDragonPart enderdragonpart = aenderdragonpart[j]; ++ EnderDragonPart entitycomplexpart = aentitycomplexpart[j]; + +- ServerLevel.this.dragonParts.put(enderdragonpart.getId(), enderdragonpart); ++ 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 serverplayer = (ServerPlayer) entity; ++ ServerPlayer entityplayer = (ServerPlayer) entity; + +- ServerLevel.this.players.remove(serverplayer); ++ ServerLevel.this.players.remove(entityplayer); + ServerLevel.this.updateSleepingPlayerList(); + } + + if (entity instanceof Mob) { +- Mob mob = (Mob) entity; ++ Mob entityinsentient = (Mob) entity; + + if (ServerLevel.this.isUpdatingNavigations) { + String s = "onTrackingStart called during navigation iteration"; +@@ -1854,25 +2027,32 @@ + 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 enderdragon = (EnderDragon) entity; +- EnderDragonPart[] aenderdragonpart = enderdragon.getSubEntities(); +- int i = aenderdragonpart.length; ++ EnderDragon entityenderdragon = (EnderDragon) entity; ++ EnderDragonPart[] aentitycomplexpart = entityenderdragon.getSubEntities(); ++ int i = aentitycomplexpart.length; + + for (int j = 0; j < i; ++j) { +- EnderDragonPart enderdragonpart = aenderdragonpart[j]; ++ EnderDragonPart entitycomplexpart = aentitycomplexpart[j]; + +- ServerLevel.this.dragonParts.remove(enderdragonpart.getId()); ++ 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); + } |