aboutsummaryrefslogtreecommitdiffhomepage
path: root/patch-remap/mache-vineflower-stripped/net/minecraft/server/level/ServerLevel.java.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patch-remap/mache-vineflower-stripped/net/minecraft/server/level/ServerLevel.java.patch')
-rw-r--r--patch-remap/mache-vineflower-stripped/net/minecraft/server/level/ServerLevel.java.patch849
1 files changed, 849 insertions, 0 deletions
diff --git a/patch-remap/mache-vineflower-stripped/net/minecraft/server/level/ServerLevel.java.patch b/patch-remap/mache-vineflower-stripped/net/minecraft/server/level/ServerLevel.java.patch
new file mode 100644
index 0000000000..03bd29e1b2
--- /dev/null
+++ b/patch-remap/mache-vineflower-stripped/net/minecraft/server/level/ServerLevel.java.patch
@@ -0,0 +1,849 @@
+--- a/net/minecraft/server/level/ServerLevel.java
++++ b/net/minecraft/server/level/ServerLevel.java
+@@ -158,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 {
+ public static final BlockPos END_SPAWN_POINT = new BlockPos(100, 50, 0);
+@@ -171,9 +191,9 @@
+ final List<ServerPlayer> players = Lists.newArrayList();
+ private final ServerChunkCache chunkSource;
+ private final MinecraftServer server;
+- private final ServerLevelData serverLevelData;
+- final EntityTickList entityTickList = new EntityTickList();
+- private final PersistentEntitySectionManager<Entity> entityManager;
++ public final PrimaryLevelData serverLevelData; // CraftBukkit - type
++ final EntityTickList entityTickList;
++ public final PersistentEntitySectionManager<Entity> entityManager;
+ private final GameEventDispatcher gameEventDispatcher;
+ public boolean noSave;
+ private final SleepStatus sleepStatus;
+@@ -196,56 +216,73 @@
+ private final boolean tickTime;
+ private final RandomSequences randomSequences;
+
+- public ServerLevel(
+- MinecraftServer server,
+- Executor dispatcher,
+- LevelStorageSource.LevelStorageAccess levelStorageAccess,
+- ServerLevelData serverLevelData,
+- ResourceKey<Level> dimension,
+- LevelStem levelStem,
+- ChunkProgressListener progressListener,
+- boolean isDebug,
+- long biomeZoomSeed,
+- List<CustomSpawner> customSpawners,
+- boolean tickTime,
+- @Nullable RandomSequences randomSequences
+- ) {
+- super(
+- serverLevelData,
+- dimension,
+- server.registryAccess(),
+- levelStem.type(),
+- server::getProfiler,
+- false,
+- isDebug,
+- biomeZoomSeed,
+- server.getMaxChainedNeighborUpdates()
+- );
+- this.tickTime = tickTime;
+- this.server = server;
+- this.customSpawners = customSpawners;
+- this.serverLevelData = serverLevelData;
+- ChunkGenerator chunkGenerator = levelStem.generator();
+- boolean flag = server.forceSynchronousWrites();
+- DataFixer fixerUpper = server.getFixerUpper();
+- EntityPersistentStorage<Entity> entityPersistentStorage = new EntityStorage(
+- this, levelStorageAccess.getDimensionPath(dimension).resolve("entities"), fixerUpper, flag, server
+- );
+- this.entityManager = new PersistentEntitySectionManager<>(Entity.class, new ServerLevel.EntityCallbacks(), entityPersistentStorage);
+- this.chunkSource = new ServerChunkCache(
+- this,
+- levelStorageAccess,
+- fixerUpper,
+- server.getStructureManager(),
+- dispatcher,
+- chunkGenerator,
+- server.getPlayerList().getViewDistance(),
+- server.getPlayerList().getSimulationDistance(),
+- flag,
+- progressListener,
+- this.entityManager::updateChunkStatus,
+- () -> server.overworld().getDataStorage()
+- );
++ // CraftBukkit start
++ public final LevelStorageSource.LevelStorageAccess convertable;
++ public final UUID uuid;
++
++ public LevelChunk getChunkIfLoaded(int x, int z) {
++ return this.chunkSource.getChunk(x, z, false);
++ }
++
++ @Override
++ public ResourceKey<LevelStem> getTypeKey() {
++ return convertable.dimensionType;
++ }
++
++ // Add env and gen to constructor, IWorldDataServer -> WorldDataServer
++ public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PrimaryLevelData iworlddataserver, ResourceKey<Level> resourcekey, LevelStem worlddimension, ChunkProgressListener worldloadlistener, boolean flag, long i, List<CustomSpawner> list, boolean flag1, @Nullable RandomSequences randomsequences, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) {
++ // IRegistryCustom.Dimension iregistrycustom_dimension = minecraftserver.registryAccess(); // CraftBukkit - decompile error
++ // Holder holder = worlddimension.type(); // CraftBukkit - decompile error
++
++ // Objects.requireNonNull(minecraftserver); // CraftBukkit - decompile error
++ super(iworlddataserver, resourcekey, minecraftserver.registryAccess(), worlddimension.type(), minecraftserver::getProfiler, false, flag, i, minecraftserver.getMaxChainedNeighborUpdates(), gen, biomeProvider, env);
++ this.pvpMode = minecraftserver.isPvpAllowed();
++ convertable = convertable_conversionsession;
++ uuid = WorldUUID.getUUID(convertable_conversionsession.levelDirectory.path().toFile());
++ // CraftBukkit end
++ this.players = Lists.newArrayList();
++ this.entityTickList = new EntityTickList();
++ this.blockTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded, this.getProfilerSupplier());
++ this.fluidTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded, this.getProfilerSupplier());
++ this.navigatingMobs = new ObjectOpenHashSet();
++ this.blockEvents = new ObjectLinkedOpenHashSet();
++ this.blockEventsToReschedule = new ArrayList(64);
++ this.dragonParts = new Int2ObjectOpenHashMap();
++ this.tickTime = flag1;
++ this.server = minecraftserver;
++ this.customSpawners = list;
++ this.serverLevelData = iworlddataserver;
++ ChunkGenerator chunkgenerator = worlddimension.generator();
++ // CraftBukkit start
++ serverLevelData.setWorld(this);
++
++ if (biomeProvider != null) {
++ BiomeSource worldChunkManager = new CustomWorldChunkManager(getWorld(), biomeProvider, server.registryAccess().registryOrThrow(Registries.BIOME));
++ if (chunkgenerator instanceof NoiseBasedChunkGenerator cga) {
++ chunkgenerator = new NoiseBasedChunkGenerator(worldChunkManager, cga.settings);
++ } else if (chunkgenerator instanceof FlatLevelSource cpf) {
++ chunkgenerator = new FlatLevelSource(cpf.settings(), worldChunkManager);
++ }
++ }
++
++ if (gen != null) {
++ chunkgenerator = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, chunkgenerator, gen);
++ }
++ // CraftBukkit end
++ boolean flag2 = minecraftserver.forceSynchronousWrites();
++ DataFixer datafixer = minecraftserver.getFixerUpper();
++ EntityPersistentStorage<Entity> entitypersistentstorage = new EntityStorage(this, convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), datafixer, flag2, minecraftserver);
++
++ this.entityManager = new PersistentEntitySectionManager<>(Entity.class, new ServerLevel.a(), entitypersistentstorage);
++ StructureTemplateManager structuretemplatemanager = minecraftserver.getStructureManager();
++ int j = minecraftserver.getPlayerList().getViewDistance();
++ int k = minecraftserver.getPlayerList().getSimulationDistance();
++ PersistentEntitySectionManager persistententitysectionmanager = this.entityManager;
++
++ Objects.requireNonNull(this.entityManager);
++ this.chunkSource = new ServerChunkCache(this, convertable_conversionsession, datafixer, structuretemplatemanager, executor, chunkgenerator, j, k, flag2, worldloadlistener, persistententitysectionmanager::updateChunkStatus, () -> {
++ return minecraftserver.overworld().getDataStorage();
++ });
+ this.chunkSource.getGeneratorState().ensureStructuresGenerated();
+ this.portalForcer = new PortalForcer(this);
+ this.updateSkyBrightness();
+@@ -256,31 +293,22 @@
+ serverLevelData.setGameType(server.getDefaultGameType());
+ }
+
+- long l = server.getWorldData().worldGenOptions().seed();
+- this.structureCheck = new StructureCheck(
+- this.chunkSource.chunkScanner(),
+- this.registryAccess(),
+- server.getStructureManager(),
+- dimension,
+- chunkGenerator,
+- this.chunkSource.randomState(),
+- this,
+- chunkGenerator.getBiomeSource(),
+- l,
+- fixerUpper
+- );
+- this.structureManager = new StructureManager(this, server.getWorldData().worldGenOptions(), this.structureCheck);
+- if (this.dimension() == Level.END && this.dimensionTypeRegistration().is(BuiltinDimensionTypes.END)) {
+- this.dragonFight = new EndDragonFight(this, l, server.getWorldData().endDragonFightData());
++ long l = minecraftserver.getWorldData().worldGenOptions().seed();
++
++ this.structureCheck = new StructureCheck(this.chunkSource.chunkScanner(), this.registryAccess(), minecraftserver.getStructureManager(), resourcekey, chunkgenerator, this.chunkSource.randomState(), this, chunkgenerator.getBiomeSource(), l, datafixer);
++ this.structureManager = new StructureManager(this, this.serverLevelData.worldGenOptions(), structureCheck); // CraftBukkit
++ if ((this.dimension() == Level.END && this.dimensionTypeRegistration().is(BuiltinDimensionTypes.END)) || env == org.bukkit.World.Environment.THE_END) { // CraftBukkit - Allow to create EnderDragonBattle in default and custom END
++ this.dragonFight = new EndDragonFight(this, this.serverLevelData.worldGenOptions().seed(), this.serverLevelData.endDragonFightData()); // CraftBukkit
+ } else {
+ this.dragonFight = null;
+ }
+
+ this.sleepStatus = new SleepStatus();
+ this.gameEventDispatcher = new GameEventDispatcher(this);
+- this.randomSequences = Objects.requireNonNullElseGet(
+- randomSequences, () -> this.getDataStorage().computeIfAbsent(RandomSequences.factory(l), "random_sequences")
+- );
++ this.randomSequences = (RandomSequences) Objects.requireNonNullElseGet(randomsequences, () -> {
++ return (RandomSequences) this.getDataStorage().computeIfAbsent(RandomSequences.factory(l), "random_sequences");
++ });
++ this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit
+ }
+
+ @Deprecated
+@@ -318,14 +349,22 @@
+ this.advanceWeatherCycle();
+ }
+
+- int _int = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE);
+- if (this.sleepStatus.areEnoughSleeping(_int) && this.sleepStatus.areEnoughDeepSleeping(_int, this.players)) {
++ int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE);
++ long j;
++
++ if (this.sleepStatus.areEnoughSleeping(i) && this.sleepStatus.areEnoughDeepSleeping(i, this.players)) {
++ // CraftBukkit start
++ j = this.levelData.getDayTime() + 24000L;
++ TimeSkipEvent event = new TimeSkipEvent(this.getWorld(), TimeSkipEvent.SkipReason.NIGHT_SKIP, (j - j % 24000L) - this.getDayTime());
+ if (this.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)) {
+ long l = this.levelData.getDayTime() + 24000L;
+ this.setDayTime(l - l % 24000L);
+ }
+
+- this.wakeUpAllPlayers();
++ if (!event.isCancelled()) {
++ this.wakeUpAllPlayers();
++ }
++ // CraftBukkit end
+ if (this.getGameRules().getBoolean(GameRules.RULE_WEATHER_CYCLE) && this.isRaining()) {
+ this.resetWeatherCycle();
+ }
+@@ -359,8 +400,9 @@
+ }
+
+ this.handlingTick = false;
+- profiler.pop();
+- boolean flag1 = !this.players.isEmpty() || !this.getForcedChunks().isEmpty();
++ gameprofilerfiller.pop();
++ boolean flag1 = true || !this.players.isEmpty() || !this.getForcedChunks().isEmpty(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players
++
+ if (flag1) {
+ this.resetEmptyTime();
+ }
+@@ -375,7 +417,7 @@
+
+ this.entityTickList.forEach(entity -> {
+ if (!entity.isRemoved()) {
+- if (this.shouldDiscardEntity(entity)) {
++ if (false && this.shouldDiscardEntity(entity)) { // CraftBukkit - We prevent spawning in general, so this butchering is not needed
+ entity.discard();
+ } else if (!tickRateManager.isEntityFrozen(entity)) {
+ profiler.push("checkDespawn");
+@@ -444,34 +495,37 @@
+ }
+
+ public void tickChunk(LevelChunk chunk, int randomTickSpeed) {
+- ChunkPos pos = chunk.getPos();
+- boolean isRaining = this.isRaining();
+- int minBlockX = pos.getMinBlockX();
+- int minBlockZ = pos.getMinBlockZ();
+- ProfilerFiller profiler = this.getProfiler();
+- profiler.push("thunder");
+- if (isRaining && this.isThundering() && this.random.nextInt(100000) == 0) {
+- BlockPos blockPos = this.findLightningTargetAround(this.getBlockRandomPos(minBlockX, 0, minBlockZ, 15));
+- if (this.isRainingAt(blockPos)) {
+- DifficultyInstance currentDifficultyAt = this.getCurrentDifficultyAt(blockPos);
+- boolean flag = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)
+- && this.random.nextDouble() < (double)currentDifficultyAt.getEffectiveDifficulty() * 0.01
+- && !this.getBlockState(blockPos.below()).is(Blocks.LIGHTNING_ROD);
+- if (flag) {
+- SkeletonHorse skeletonHorse = EntityType.SKELETON_HORSE.create(this);
+- if (skeletonHorse != null) {
+- skeletonHorse.setTrap(true);
+- skeletonHorse.setAge(0);
+- skeletonHorse.setPos((double)blockPos.getX(), (double)blockPos.getY(), (double)blockPos.getZ());
+- this.addFreshEntity(skeletonHorse);
++ ChunkPos chunkcoordintpair = chunk.getPos();
++ boolean flag = this.isRaining();
++ int j = chunkcoordintpair.getMinBlockX();
++ int k = chunkcoordintpair.getMinBlockZ();
++ ProfilerFiller gameprofilerfiller = this.getProfiler();
++
++ gameprofilerfiller.push("thunder");
++ if (flag && this.isThundering() && this.random.nextInt(100000) == 0) {
++ BlockPos blockposition = this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15));
++
++ if (this.isRainingAt(blockposition)) {
++ DifficultyInstance difficultydamagescaler = this.getCurrentDifficultyAt(blockposition);
++ boolean flag1 = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && this.random.nextDouble() < (double) difficultydamagescaler.getEffectiveDifficulty() * 0.01D && !this.getBlockState(blockposition.below()).is(Blocks.LIGHTNING_ROD);
++
++ if (flag1) {
++ SkeletonHorse entityhorseskeleton = (SkeletonHorse) EntityType.SKELETON_HORSE.create(this);
++
++ if (entityhorseskeleton != null) {
++ entityhorseskeleton.setTrap(true);
++ entityhorseskeleton.setAge(0);
++ entityhorseskeleton.setPos((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ());
++ this.addFreshEntity(entityhorseskeleton, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); // CraftBukkit
+ }
+ }
+
+- LightningBolt lightningBolt = EntityType.LIGHTNING_BOLT.create(this);
+- if (lightningBolt != null) {
+- lightningBolt.moveTo(Vec3.atBottomCenterOf(blockPos));
+- lightningBolt.setVisualOnly(flag);
+- this.addFreshEntity(lightningBolt);
++ LightningBolt entitylightning = (LightningBolt) EntityType.LIGHTNING_BOLT.create(this);
++
++ if (entitylightning != null) {
++ entitylightning.moveTo(Vec3.atBottomCenterOf(blockposition));
++ entitylightning.setVisualOnly(flag1);
++ this.strikeLightning(entitylightning, org.bukkit.event.weather.LightningStrikeEvent.Cause.WEATHER); // CraftBukkit
+ }
+ }
+ }
+@@ -519,27 +575,32 @@
+ }
+
+ @VisibleForTesting
+- public void tickPrecipitation(BlockPos blockPos) {
+- BlockPos heightmapPos = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, blockPos);
+- BlockPos blockPos1 = heightmapPos.below();
+- Biome biome = this.getBiome(heightmapPos).value();
+- if (biome.shouldFreeze(this, blockPos1)) {
+- this.setBlockAndUpdate(blockPos1, Blocks.ICE.defaultBlockState());
++ public void tickPrecipitation(BlockPos blockposition) {
++ BlockPos blockposition1 = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, blockposition);
++ BlockPos blockposition2 = blockposition1.below();
++ Biome biomebase = (Biome) this.getBiome(blockposition1).value();
++
++ if (biomebase.shouldFreeze(this, blockposition2)) {
++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition2, Blocks.ICE.defaultBlockState(), null); // CraftBukkit
+ }
+
+ if (this.isRaining()) {
+- int _int = this.getGameRules().getInt(GameRules.RULE_SNOW_ACCUMULATION_HEIGHT);
+- if (_int > 0 && biome.shouldSnow(this, heightmapPos)) {
+- BlockState blockState = this.getBlockState(heightmapPos);
+- if (blockState.is(Blocks.SNOW)) {
+- int i = blockState.getValue(SnowLayerBlock.LAYERS);
+- if (i < Math.min(_int, 8)) {
+- BlockState blockState1 = blockState.setValue(SnowLayerBlock.LAYERS, Integer.valueOf(i + 1));
+- Block.pushEntitiesUp(blockState, blockState1, this, heightmapPos);
+- this.setBlockAndUpdate(heightmapPos, blockState1);
++ int i = this.getGameRules().getInt(GameRules.RULE_SNOW_ACCUMULATION_HEIGHT);
++
++ if (i > 0 && biomebase.shouldSnow(this, blockposition1)) {
++ IBlockData iblockdata = this.getBlockState(blockposition1);
++
++ if (iblockdata.is(Blocks.SNOW)) {
++ int j = (Integer) iblockdata.getValue(SnowLayerBlock.LAYERS);
++
++ if (j < Math.min(i, 8)) {
++ IBlockData iblockdata1 = (IBlockData) iblockdata.setValue(SnowLayerBlock.LAYERS, j + 1);
++
++ Block.pushEntitiesUp(iblockdata, iblockdata1, this, blockposition1);
++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition1, iblockdata1, null); // CraftBukkit
+ }
+ } else {
+- this.setBlockAndUpdate(heightmapPos, Blocks.SNOW.defaultBlockState());
++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition1, Blocks.SNOW.defaultBlockState(), null); // CraftBukkit
+ }
+ }
+
+@@ -684,6 +761,7 @@
+ this.rainLevel = Mth.clamp(this.rainLevel, 0.0F, 1.0F);
+ }
+
++ /* CraftBukkit start
+ if (this.oRainLevel != this.rainLevel) {
+ this.server
+ .getPlayerList()
+@@ -706,14 +780,48 @@
+ this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.RAIN_LEVEL_CHANGE, this.rainLevel));
+ this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.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() {
+@@ -741,6 +856,7 @@
+ this.getProfiler().push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString());
+ profiler.incrementCounter("tickNonPassenger");
+ entity.tick();
++ entity.postTick(); // CraftBukkit
+ this.getProfiler().pop();
+
+ for (Entity entity1 : entity.getPassengers()) {
+@@ -760,8 +875,21 @@
+ passengerEntity.rideTick();
+ profiler.pop();
+
+- for (Entity entity : passengerEntity.getPassengers()) {
+- this.tickPassenger(passengerEntity, entity);
++ gameprofilerfiller.push(() -> {
++ return BuiltInRegistries.ENTITY_TYPE.getKey(passengerEntity.getType()).toString();
++ });
++ gameprofilerfiller.incrementCounter("tickPassenger");
++ passengerEntity.rideTick();
++ passengerEntity.postTick(); // CraftBukkit
++ gameprofilerfiller.pop();
++ Iterator iterator = passengerEntity.getPassengers().iterator();
++
++ while (iterator.hasNext()) {
++ Entity entity2 = (Entity) iterator.next();
++
++ this.tickPassenger(passengerEntity, entity2);
++ }
++
+ }
+ }
+ }
+@@ -774,6 +905,7 @@
+ public void save(@Nullable ProgressListener progress, boolean flush, boolean skipSave) {
+ ServerChunkCache chunkSource = this.getChunkSource();
+ if (!skipSave) {
++ org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld())); // CraftBukkit
+ if (progress != null) {
+ progress.progressStartNoAbort(Component.translatable("menu.savingLevel"));
+ }
+@@ -790,11 +923,19 @@
+ 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();
+@@ -854,17 +1000,39 @@
+
+ @Override
+ public boolean addFreshEntity(Entity entity) {
+- return this.addEntity(entity);
++ // CraftBukkit start
++ return this.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.DEFAULT);
+ }
+
++ @Override
++ public boolean addFreshEntity(Entity entity, CreatureSpawnEvent.SpawnReason reason) {
++ return this.addEntity(entity, reason);
++ // CraftBukkit end
++ }
++
+ public boolean addWithUUID(Entity entity) {
+- return this.addEntity(entity);
++ // CraftBukkit start
++ return this.addWithUUID(entity, CreatureSpawnEvent.SpawnReason.DEFAULT);
+ }
+
++ public boolean addWithUUID(Entity entity, CreatureSpawnEvent.SpawnReason reason) {
++ return this.addEntity(entity, reason);
++ // CraftBukkit end
++ }
++
+ public void addDuringTeleport(Entity entity) {
+- this.addEntity(entity);
++ // CraftBukkit start
++ // SPIGOT-6415: Don't call spawn event for entities which travel trough worlds,
++ // since it is only an implementation detail, that a new entity is created when
++ // they are traveling between worlds.
++ this.addDuringTeleport(entity, null);
+ }
+
++ public void addDuringTeleport(Entity entity, CreatureSpawnEvent.SpawnReason reason) {
++ this.addEntity(entity, reason);
++ // CraftBukkit end
++ }
++
+ public void addDuringCommandTeleport(ServerPlayer player) {
+ this.addPlayer(player);
+ }
+@@ -892,20 +1061,37 @@
+ this.entityManager.addNewEntity(player);
+ }
+
+- private boolean addEntity(Entity entity) {
++ // CraftBukkit start
++ private boolean addEntity(Entity entity, CreatureSpawnEvent.SpawnReason spawnReason) {
+ if (entity.isRemoved()) {
+- LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityType.getKey(entity.getType()));
++ // WorldServer.LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityTypes.getKey(entity.getType())); // CraftBukkit
+ return false;
+ } else {
++ // SPIGOT-6415: Don't call spawn event when reason is null. For example when an entity teleports to a new world.
++ if (spawnReason != null && !CraftEventFactory.doEntityAddEventCalling(this, entity, spawnReason)) {
++ return false;
++ }
++ // CraftBukkit end
++
+ return this.entityManager.addNewEntity(entity);
+ }
+ }
+
+ public boolean tryAddFreshEntityWithPassengers(Entity entity) {
+- if (entity.getSelfAndPassengers().map(Entity::getUUID).anyMatch(this.entityManager::isLoaded)) {
++ // CraftBukkit start
++ return this.tryAddFreshEntityWithPassengers(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT);
++ }
++
++ public boolean tryAddFreshEntityWithPassengers(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) {
++ // CraftBukkit end
++ Stream<UUID> stream = entity.getSelfAndPassengers().map(Entity::getUUID); // CraftBukkit - decompile error
++ PersistentEntitySectionManager persistententitysectionmanager = this.entityManager;
++
++ Objects.requireNonNull(this.entityManager);
++ if (stream.anyMatch(persistententitysectionmanager::isLoaded)) {
+ return false;
+ } else {
+- this.addFreshEntityWithPassengers(entity);
++ this.addFreshEntityWithPassengers(entity, reason); // CraftBukkit
+ return true;
+ }
+ }
+@@ -919,16 +1105,49 @@
+ player.remove(reason);
+ }
+
++ // CraftBukkit start
++ public boolean strikeLightning(Entity entitylightning) {
++ return this.strikeLightning(entitylightning, LightningStrikeEvent.Cause.UNKNOWN);
++ }
++
++ public boolean strikeLightning(Entity entitylightning, LightningStrikeEvent.Cause cause) {
++ LightningStrikeEvent lightning = CraftEventFactory.callLightningStrikeEvent((org.bukkit.entity.LightningStrike) entitylightning.getBukkitEntity(), cause);
++
++ if (lightning.isCancelled()) {
++ return false;
++ }
++
++ return this.addFreshEntity(entitylightning);
++ }
++ // CraftBukkit end
++
+ @Override
+ public void destroyBlockProgress(int breakerId, BlockPos pos, int progress) {
+- for (ServerPlayer serverPlayer : this.server.getPlayerList().getPlayers()) {
+- if (serverPlayer != null && serverPlayer.level() == this && serverPlayer.getId() != breakerId) {
+- double d = (double)pos.getX() - serverPlayer.getX();
+- double d1 = (double)pos.getY() - serverPlayer.getY();
+- double d2 = (double)pos.getZ() - serverPlayer.getZ();
+- if (d * d + d1 * d1 + d2 * d2 < 1024.0) {
+- serverPlayer.connection.send(new ClientboundBlockDestructionPacket(breakerId, pos, progress));
++ Iterator iterator = this.server.getPlayerList().getPlayers().iterator();
++
++ // CraftBukkit start
++ Player entityhuman = null;
++ Entity entity = this.getEntity(breakerId);
++ if (entity instanceof Player) entityhuman = (Player) entity;
++ // CraftBukkit end
++
++ while (iterator.hasNext()) {
++ ServerPlayer entityplayer = (ServerPlayer) iterator.next();
++
++ if (entityplayer != null && entityplayer.level() == this && entityplayer.getId() != breakerId) {
++ double d0 = (double) pos.getX() - entityplayer.getX();
++ double d1 = (double) pos.getY() - entityplayer.getY();
++ double d2 = (double) pos.getZ() - entityplayer.getZ();
++
++ // CraftBukkit start
++ if (entityhuman != null && !entityplayer.getBukkitEntity().canSee(entityhuman.getBukkitEntity())) {
++ continue;
+ }
++ // CraftBukkit end
++
++ if (d0 * d0 + d1 * d1 + d2 * d2 < 1024.0D) {
++ entityplayer.connection.send(new ClientboundBlockDestructionPacket(breakerId, pos, progress));
++ }
+ }
+ }
+ }
+@@ -1011,11 +1199,28 @@
+ if (Shapes.joinIsNotEmpty(collisionShape, collisionShape1, BooleanOp.NOT_SAME)) {
+ List<PathNavigation> list = new ObjectArrayList<>();
+
+- for (Mob mob : this.navigatingMobs) {
+- PathNavigation navigation = mob.getNavigation();
+- if (navigation.shouldRecomputePath(pos)) {
+- list.add(navigation);
++ if (Shapes.joinIsNotEmpty(voxelshape, voxelshape1, BooleanOp.NOT_SAME)) {
++ List<PathNavigation> list = new ObjectArrayList();
++ Iterator iterator = this.navigatingMobs.iterator();
++
++ while (iterator.hasNext()) {
++ // CraftBukkit start - fix SPIGOT-6362
++ Mob entityinsentient;
++ try {
++ entityinsentient = (Mob) iterator.next();
++ } catch (java.util.ConcurrentModificationException ex) {
++ // This can happen because the pathfinder update below may trigger a chunk load, which in turn may cause more navigators to register
++ // In this case we just run the update again across all the iterators as the chunk will then be loaded
++ // As this is a relative edge case it is much faster than copying navigators (on either read or write)
++ sendBlockUpdated(pos, oldState, newState, flags);
++ return;
+ }
++ // CraftBukkit end
++ PathNavigation navigationabstract = entityinsentient.getNavigation();
++
++ if (navigationabstract.shouldRecomputePath(pos)) {
++ list.add(navigationabstract);
++ }
+ }
+
+ try {
+@@ -1066,23 +1275,14 @@
+ }
+
+ @Override
+- public Explosion explode(
+- @Nullable Entity entity,
+- @Nullable DamageSource damageSource,
+- @Nullable ExplosionDamageCalculator explosionDamageCalculator,
+- double d,
+- double d1,
+- double d2,
+- float f,
+- boolean flag,
+- Level.ExplosionInteraction explosionInteraction,
+- ParticleOptions particleOptions,
+- ParticleOptions particleOptions1,
+- SoundEvent soundEvent
+- ) {
+- Explosion explosion = this.explode(
+- entity, damageSource, explosionDamageCalculator, d, d1, d2, f, flag, explosionInteraction, false, particleOptions, particleOptions1, soundEvent
+- );
++ public Explosion explode(@Nullable Entity entity, @Nullable DamageSource damagesource, @Nullable ExplosionDamageCalculator explosiondamagecalculator, double d0, double d1, double d2, float f, boolean flag, Level.a world_a, ParticleOptions particleparam, ParticleOptions particleparam1, SoundEvent soundeffect) {
++ Explosion explosion = this.explode(entity, damagesource, explosiondamagecalculator, d0, d1, d2, f, flag, world_a, false, particleparam, particleparam1, soundeffect);
++ // CraftBukkit start
++ if (explosion.wasCanceled) {
++ return explosion;
++ }
++ // CraftBukkit end
++
+ if (!explosion.interactsWithBlocks()) {
+ explosion.clearToBlow();
+ }
+@@ -1171,18 +1353,22 @@
+ return this.server.getStructureManager();
+ }
+
+- public <T extends ParticleOptions> int sendParticles(
+- T type, double posX, double posY, double posZ, int particleCount, double xOffset, double yOffset, double zOffset, double speed
+- ) {
+- ClientboundLevelParticlesPacket clientboundLevelParticlesPacket = new ClientboundLevelParticlesPacket(
+- type, false, posX, posY, posZ, (float)xOffset, (float)yOffset, (float)zOffset, (float)speed, particleCount
+- );
+- int i = 0;
++ public <T extends ParticleOptions> int sendParticles(T type, double posX, double d1, double posY, int i, double posZ, double d4, double particleCount, double xOffset) {
++ // CraftBukkit - visibility api support
++ return sendParticles(null, type, posX, d1, posY, i, posZ, d4, particleCount, xOffset, false);
++ }
+
+- for (int i1 = 0; i1 < this.players.size(); i1++) {
+- ServerPlayer serverPlayer = this.players.get(i1);
+- if (this.sendParticles(serverPlayer, false, posX, posY, posZ, clientboundLevelParticlesPacket)) {
+- i++;
++ public <T extends ParticleOptions> int sendParticles(ServerPlayer sender, T t0, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6, boolean force) {
++ ClientboundLevelParticlesPacket packetplayoutworldparticles = new ClientboundLevelParticlesPacket(t0, force, d0, d1, d2, (float) d3, (float) d4, (float) d5, (float) d6, i);
++ // CraftBukkit end
++ int j = 0;
++
++ for (int k = 0; k < this.players.size(); ++k) {
++ ServerPlayer entityplayer = (ServerPlayer) this.players.get(k);
++ if (sender != null && !entityplayer.getBukkitEntity().canSee(sender.getBukkitEntity())) continue; // CraftBukkit
++
++ if (this.sendParticles(entityplayer, force, d0, d1, d2, packetplayoutworldparticles)) { // CraftBukkit
++ ++j;
+ }
+ }
+
+@@ -1242,7 +1418,7 @@
+
+ @Nullable
+ public BlockPos findNearestMapStructure(TagKey<Structure> structureTag, BlockPos pos, int radius, boolean skipExistingChunks) {
+- if (!this.server.getWorldData().worldGenOptions().generateStructures()) {
++ if (!this.serverLevelData.worldGenOptions().generateStructures()) { // CraftBukkit
+ return null;
+ } else {
+ Optional<HolderSet.Named<Structure>> tag = this.registryAccess().registryOrThrow(Registries.STRUCTURE).getTag(structureTag);
+@@ -1287,11 +1460,22 @@
+ @Nullable
+ @Override
+ public MapItemSavedData getMapData(String mapName) {
+- return this.getServer().overworld().getDataStorage().get(MapItemSavedData.factory(), mapName);
++ // CraftBukkit start
++ MapItemSavedData worldmap = (MapItemSavedData) this.getServer().overworld().getDataStorage().get(MapItemSavedData.factory(), mapName);
++ if (worldmap != null) {
++ worldmap.id = mapName;
++ }
++ return worldmap;
++ // CraftBukkit end
+ }
+
+ @Override
+ public void setMapData(String mapName, MapItemSavedData data) {
++ // CraftBukkit start
++ data.id = mapName;
++ MapInitializeEvent event = new MapInitializeEvent(data.mapView);
++ Bukkit.getServer().getPluginManager().callEvent(event);
++ // CraftBukkit end
+ this.getServer().overworld().getDataStorage().set(mapName, data);
+ }
+
+@@ -1491,6 +1773,11 @@
+ @Override
+ public void blockUpdated(BlockPos pos, Block block) {
+ if (!this.isDebug()) {
++ // CraftBukkit start
++ if (populating) {
++ return;
++ }
++ // CraftBukkit end
+ this.updateNeighborsAt(pos, block);
+ }
+ }
+@@ -1510,12 +1797,12 @@
+ }
+
+ public boolean isFlat() {
+- return this.server.getWorldData().isFlatWorld();
++ return this.serverLevelData.isFlatWorld(); // CraftBukkit
+ }
+
+ @Override
+ public long getSeed() {
+- return this.server.getWorldData().worldGenOptions().seed();
++ return this.serverLevelData.worldGenOptions().seed(); // CraftBukkit
+ }
+
+ @Nullable
+@@ -1565,16 +1845,35 @@
+ }
+
+ public static void makeObsidianPlatform(ServerLevel serverLevel) {
+- BlockPos blockPos = END_SPAWN_POINT;
+- int x = blockPos.getX();
+- int i = blockPos.getY() - 2;
+- int z = blockPos.getZ();
+- BlockPos.betweenClosed(x - 2, i + 1, z - 2, x + 2, i + 3, z + 2)
+- .forEach(blockPos1 -> serverLevel.setBlockAndUpdate(blockPos1, Blocks.AIR.defaultBlockState()));
+- BlockPos.betweenClosed(x - 2, i, z - 2, x + 2, i, z + 2)
+- .forEach(blockPos1 -> serverLevel.setBlockAndUpdate(blockPos1, Blocks.OBSIDIAN.defaultBlockState()));
++ // CraftBukkit start
++ ServerLevel.makeObsidianPlatform(serverLevel, null);
+ }
+
++ public static void makeObsidianPlatform(ServerLevel worldserver, Entity entity) {
++ // CraftBukkit end
++ BlockPos blockposition = ServerLevel.END_SPAWN_POINT;
++ int i = blockposition.getX();
++ int j = blockposition.getY() - 2;
++ int k = blockposition.getZ();
++
++ // CraftBukkit start
++ org.bukkit.craftbukkit.util.BlockStateListPopulator blockList = new org.bukkit.craftbukkit.util.BlockStateListPopulator(worldserver);
++ BlockPos.betweenClosed(i - 2, j + 1, k - 2, i + 2, j + 3, k + 2).forEach((blockposition1) -> {
++ blockList.setBlock(blockposition1, Blocks.AIR.defaultBlockState(), 3);
++ });
++ BlockPos.betweenClosed(i - 2, j, k - 2, i + 2, j, k + 2).forEach((blockposition1) -> {
++ blockList.setBlock(blockposition1, Blocks.OBSIDIAN.defaultBlockState(), 3);
++ });
++ org.bukkit.World bworld = worldserver.getWorld();
++ org.bukkit.event.world.PortalCreateEvent portalEvent = new org.bukkit.event.world.PortalCreateEvent((List<org.bukkit.block.BlockState>) (List) blockList.getList(), bworld, (entity == null) ? null : entity.getBukkitEntity(), org.bukkit.event.world.PortalCreateEvent.CreateReason.END_PLATFORM);
++
++ worldserver.getCraftServer().getPluginManager().callEvent(portalEvent);
++ if (!portalEvent.isCancelled()) {
++ blockList.updateList();
++ }
++ // CraftBukkit end
++ }
++
+ @Override
+ protected LevelEntityGetter<Entity> getEntities() {
+ return this.entityManager.getEntityGetter();
+@@ -1693,6 +2005,8 @@
+ }
+
+ entity.updateDynamicGameEventListener(DynamicGameEventListener::add);
++ entity.inWorld = true; // CraftBukkit - Mark entity as in world
++ entity.valid = true; // CraftBukkit
+ }
+
+ @Override
+@@ -1721,6 +2043,14 @@
+ }
+
+ entity.updateDynamicGameEventListener(DynamicGameEventListener::remove);
++ // CraftBukkit start
++ entity.valid = false;
++ if (!(entity instanceof ServerPlayer)) {
++ for (ServerPlayer player : players) {
++ player.getBukkitEntity().onEntityRemove(entity);
++ }
++ }
++ // CraftBukkit end
+ }
+
+ @Override