aboutsummaryrefslogtreecommitdiffhomepage
path: root/patch-remap/og/net/minecraft/server/level/WorldServer.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patch-remap/og/net/minecraft/server/level/WorldServer.patch')
-rw-r--r--patch-remap/og/net/minecraft/server/level/WorldServer.patch635
1 files changed, 635 insertions, 0 deletions
diff --git a/patch-remap/og/net/minecraft/server/level/WorldServer.patch b/patch-remap/og/net/minecraft/server/level/WorldServer.patch
new file mode 100644
index 0000000000..2def431f84
--- /dev/null
+++ b/patch-remap/og/net/minecraft/server/level/WorldServer.patch
@@ -0,0 +1,635 @@
+--- a/net/minecraft/server/level/WorldServer.java
++++ b/net/minecraft/server/level/WorldServer.java
+@@ -164,6 +164,26 @@
+ import net.minecraft.world.ticks.TickListServer;
+ import org.slf4j.Logger;
+
++// CraftBukkit start
++import net.minecraft.world.level.biome.WorldChunkManager;
++import net.minecraft.world.level.dimension.WorldDimension;
++import net.minecraft.world.level.levelgen.ChunkGeneratorAbstract;
++import net.minecraft.world.level.levelgen.ChunkProviderFlat;
++import net.minecraft.world.level.storage.WorldDataServer;
++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 WorldServer extends World implements GeneratorAccessSeed {
+
+ public static final BlockPosition END_SPAWN_POINT = new BlockPosition(100, 50, 0);
+@@ -177,7 +197,7 @@
+ final List<EntityPlayer> players;
+ private final ChunkProviderServer chunkSource;
+ private final MinecraftServer server;
+- public final IWorldDataServer serverLevelData;
++ public final WorldDataServer serverLevelData; // CraftBukkit - type
+ final EntityTickList entityTickList;
+ public final PersistentEntitySectionManager<Entity> entityManager;
+ private final GameEventDispatcher gameEventDispatcher;
+@@ -202,12 +222,30 @@
+ private final boolean tickTime;
+ private final RandomSequences randomSequences;
+
+- public WorldServer(MinecraftServer minecraftserver, Executor executor, Convertable.ConversionSession convertable_conversionsession, IWorldDataServer iworlddataserver, ResourceKey<World> resourcekey, WorldDimension worlddimension, WorldLoadListener worldloadlistener, boolean flag, long i, List<MobSpawner> list, boolean flag1, @Nullable RandomSequences randomsequences) {
+- IRegistryCustom.Dimension iregistrycustom_dimension = minecraftserver.registryAccess();
+- Holder holder = worlddimension.type();
++ // CraftBukkit start
++ public final Convertable.ConversionSession convertable;
++ public final UUID uuid;
+
+- Objects.requireNonNull(minecraftserver);
+- super(iworlddataserver, resourcekey, iregistrycustom_dimension, holder, minecraftserver::getProfiler, false, flag, i, minecraftserver.getMaxChainedNeighborUpdates());
++ public Chunk getChunkIfLoaded(int x, int z) {
++ return this.chunkSource.getChunk(x, z, false);
++ }
++
++ @Override
++ public ResourceKey<WorldDimension> getTypeKey() {
++ return convertable.dimensionType;
++ }
++
++ // Add env and gen to constructor, IWorldDataServer -> WorldDataServer
++ public WorldServer(MinecraftServer minecraftserver, Executor executor, Convertable.ConversionSession convertable_conversionsession, WorldDataServer iworlddataserver, ResourceKey<World> resourcekey, WorldDimension worlddimension, WorldLoadListener worldloadlistener, boolean flag, long i, List<MobSpawner> 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 TickListServer<>(this::isPositionTickingWithEntitiesLoaded, this.getProfilerSupplier());
+@@ -221,6 +259,22 @@
+ this.customSpawners = list;
+ this.serverLevelData = iworlddataserver;
+ ChunkGenerator chunkgenerator = worlddimension.generator();
++ // CraftBukkit start
++ serverLevelData.setWorld(this);
++
++ if (biomeProvider != null) {
++ WorldChunkManager worldChunkManager = new CustomWorldChunkManager(getWorld(), biomeProvider, server.registryAccess().registryOrThrow(Registries.BIOME));
++ if (chunkgenerator instanceof ChunkGeneratorAbstract cga) {
++ chunkgenerator = new ChunkGeneratorAbstract(worldChunkManager, cga.settings);
++ } else if (chunkgenerator instanceof ChunkProviderFlat cpf) {
++ chunkgenerator = new ChunkProviderFlat(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);
+@@ -248,9 +302,9 @@
+ 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() == World.END && this.dimensionTypeRegistration().is(BuiltinDimensionTypes.END)) {
+- this.dragonFight = new EnderDragonBattle(this, l, minecraftserver.getWorldData().endDragonFightData());
++ this.structureManager = new StructureManager(this, this.serverLevelData.worldGenOptions(), structureCheck); // CraftBukkit
++ if ((this.dimension() == World.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 EnderDragonBattle(this, this.serverLevelData.worldGenOptions().seed(), this.serverLevelData.endDragonFightData()); // CraftBukkit
+ } else {
+ this.dragonFight = null;
+ }
+@@ -260,6 +314,7 @@
+ this.randomSequences = (RandomSequences) Objects.requireNonNullElseGet(randomsequences, () -> {
+ return (RandomSequences) this.getDataStorage().computeIfAbsent(RandomSequences.factory(l), "random_sequences");
+ });
++ this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit
+ }
+
+ /** @deprecated */
+@@ -304,12 +359,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();
+ }
+@@ -344,7 +407,7 @@
+
+ this.handlingTick = false;
+ gameprofilerfiller.pop();
+- boolean flag1 = !this.players.isEmpty() || !this.getForcedChunks().isEmpty();
++ boolean flag1 = true || !this.players.isEmpty() || !this.getForcedChunks().isEmpty(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players
+
+ if (flag1) {
+ this.resetEmptyTime();
+@@ -360,7 +423,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)) {
+ gameprofilerfiller.push("checkDespawn");
+@@ -432,7 +495,7 @@
+
+ private void wakeUpAllPlayers() {
+ this.sleepStatus.removeAllSleepers();
+- ((List) this.players.stream().filter(EntityLiving::isSleeping).collect(Collectors.toList())).forEach((entityplayer) -> {
++ (this.players.stream().filter(EntityLiving::isSleeping).collect(Collectors.toList())).forEach((entityplayer) -> { // CraftBukkit - decompile error
+ entityplayer.stopSleepInBed(false, false);
+ });
+ }
+@@ -459,7 +522,7 @@
+ entityhorseskeleton.setTrap(true);
+ entityhorseskeleton.setAge(0);
+ entityhorseskeleton.setPos((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ());
+- this.addFreshEntity(entityhorseskeleton);
++ this.addFreshEntity(entityhorseskeleton, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); // CraftBukkit
+ }
+ }
+
+@@ -468,7 +531,7 @@
+ if (entitylightning != null) {
+ entitylightning.moveTo(Vec3D.atBottomCenterOf(blockposition));
+ entitylightning.setVisualOnly(flag1);
+- this.addFreshEntity(entitylightning);
++ this.strikeLightning(entitylightning, org.bukkit.event.weather.LightningStrikeEvent.Cause.WEATHER); // CraftBukkit
+ }
+ }
+ }
+@@ -524,7 +587,7 @@
+ BiomeBase biomebase = (BiomeBase) this.getBiome(blockposition1).value();
+
+ if (biomebase.shouldFreeze(this, blockposition2)) {
+- this.setBlockAndUpdate(blockposition2, Blocks.ICE.defaultBlockState());
++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition2, Blocks.ICE.defaultBlockState(), null); // CraftBukkit
+ }
+
+ if (this.isRaining()) {
+@@ -540,10 +603,10 @@
+ IBlockData iblockdata1 = (IBlockData) iblockdata.setValue(BlockSnow.LAYERS, j + 1);
+
+ Block.pushEntitiesUp(iblockdata, iblockdata1, this, blockposition1);
+- this.setBlockAndUpdate(blockposition1, iblockdata1);
++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition1, iblockdata1, null); // CraftBukkit
+ }
+ } else {
+- this.setBlockAndUpdate(blockposition1, Blocks.SNOW.defaultBlockState());
++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition1, Blocks.SNOW.defaultBlockState(), null); // CraftBukkit
+ }
+ }
+
+@@ -704,6 +767,7 @@
+ this.rainLevel = MathHelper.clamp(this.rainLevel, 0.0F, 1.0F);
+ }
+
++ /* CraftBukkit start
+ if (this.oRainLevel != this.rainLevel) {
+ this.server.getPlayerList().broadcastAll(new PacketPlayOutGameStateChange(PacketPlayOutGameStateChange.RAIN_LEVEL_CHANGE, this.rainLevel), this.dimension());
+ }
+@@ -722,15 +786,48 @@
+ 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 (((EntityPlayer) this.players.get(idx)).level() == this) {
++ ((EntityPlayer) 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 (((EntityPlayer) this.players.get(idx)).level() == this) {
++ ((EntityPlayer) this.players.get(idx)).setPlayerWeather((!flag ? WeatherType.DOWNFALL : WeatherType.CLEAR), false);
++ }
++ }
++ }
++ for (int idx = 0; idx < this.players.size(); ++idx) {
++ if (((EntityPlayer) this.players.get(idx)).level() == this) {
++ ((EntityPlayer) 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() {
+@@ -765,6 +862,7 @@
+ });
+ gameprofilerfiller.incrementCounter("tickNonPassenger");
+ entity.tick();
++ entity.postTick(); // CraftBukkit
+ this.getProfiler().pop();
+ Iterator iterator = entity.getPassengers().iterator();
+
+@@ -788,6 +886,7 @@
+ });
+ gameprofilerfiller.incrementCounter("tickPassenger");
+ entity1.rideTick();
++ entity1.postTick(); // CraftBukkit
+ gameprofilerfiller.pop();
+ Iterator iterator = entity1.getPassengers().iterator();
+
+@@ -812,6 +911,7 @@
+ ChunkProviderServer chunkproviderserver = this.getChunkSource();
+
+ if (!flag1) {
++ org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld())); // CraftBukkit
+ if (iprogressupdate != null) {
+ iprogressupdate.progressStartNoAbort(IChatBaseComponent.translatable("menu.savingLevel"));
+ }
+@@ -829,11 +929,19 @@
+ }
+
+ }
++
++ // CraftBukkit start - moved from MinecraftServer.saveChunks
++ WorldServer 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();
+@@ -898,15 +1006,37 @@
+
+ @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(EntityPlayer entityplayer) {
+@@ -937,24 +1067,37 @@
+ this.entityManager.addNewEntity(entityplayer);
+ }
+
+- private boolean addEntity(Entity entity) {
++ // CraftBukkit start
++ private boolean addEntity(Entity entity, CreatureSpawnEvent.SpawnReason spawnReason) {
+ if (entity.isRemoved()) {
+- WorldServer.LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityTypes.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;
+ }
+ }
+@@ -968,10 +1111,32 @@
+ entityplayer.remove(entity_removalreason);
+ }
+
++ // 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 i, BlockPosition blockposition, int j) {
+ Iterator iterator = this.server.getPlayerList().getPlayers().iterator();
+
++ // CraftBukkit start
++ EntityHuman entityhuman = null;
++ Entity entity = this.getEntity(i);
++ if (entity instanceof EntityHuman) entityhuman = (EntityHuman) entity;
++ // CraftBukkit end
++
+ while (iterator.hasNext()) {
+ EntityPlayer entityplayer = (EntityPlayer) iterator.next();
+
+@@ -980,6 +1145,12 @@
+ double d1 = (double) blockposition.getY() - entityplayer.getY();
+ double d2 = (double) blockposition.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 PacketPlayOutBlockBreakAnimation(i, blockposition, j));
+ }
+@@ -1039,7 +1210,18 @@
+ Iterator iterator = this.navigatingMobs.iterator();
+
+ while (iterator.hasNext()) {
+- EntityInsentient entityinsentient = (EntityInsentient) iterator.next();
++ // CraftBukkit start - fix SPIGOT-6362
++ EntityInsentient entityinsentient;
++ try {
++ entityinsentient = (EntityInsentient) 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(blockposition, iblockdata, iblockdata1, i);
++ return;
++ }
++ // CraftBukkit end
+ NavigationAbstract navigationabstract = entityinsentient.getNavigation();
+
+ if (navigationabstract.shouldRecomputePath(blockposition)) {
+@@ -1101,6 +1283,11 @@
+ @Override
+ public Explosion explode(@Nullable Entity entity, @Nullable DamageSource damagesource, @Nullable ExplosionDamageCalculator explosiondamagecalculator, double d0, double d1, double d2, float f, boolean flag, World.a world_a, ParticleParam particleparam, ParticleParam particleparam1, SoundEffect 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();
+@@ -1173,13 +1360,20 @@
+ }
+
+ public <T extends ParticleParam> int sendParticles(T t0, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6) {
+- PacketPlayOutWorldParticles packetplayoutworldparticles = new PacketPlayOutWorldParticles(t0, false, d0, d1, d2, (float) d3, (float) d4, (float) d5, (float) d6, i);
++ // CraftBukkit - visibility api support
++ return sendParticles(null, t0, d0, d1, d2, i, d3, d4, d5, d6, false);
++ }
++
++ public <T extends ParticleParam> int sendParticles(EntityPlayer sender, T t0, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6, boolean force) {
++ PacketPlayOutWorldParticles packetplayoutworldparticles = new PacketPlayOutWorldParticles(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) {
+ EntityPlayer entityplayer = (EntityPlayer) this.players.get(k);
++ if (sender != null && !entityplayer.getBukkitEntity().canSee(sender.getBukkitEntity())) continue; // CraftBukkit
+
+- if (this.sendParticles(entityplayer, false, d0, d1, d2, packetplayoutworldparticles)) {
++ if (this.sendParticles(entityplayer, force, d0, d1, d2, packetplayoutworldparticles)) { // CraftBukkit
+ ++j;
+ }
+ }
+@@ -1230,7 +1424,7 @@
+
+ @Nullable
+ public BlockPosition findNearestMapStructure(TagKey<Structure> tagkey, BlockPosition blockposition, int i, boolean flag) {
+- if (!this.server.getWorldData().worldGenOptions().generateStructures()) {
++ if (!this.serverLevelData.worldGenOptions().generateStructures()) { // CraftBukkit
+ return null;
+ } else {
+ Optional<HolderSet.Named<Structure>> optional = this.registryAccess().registryOrThrow(Registries.STRUCTURE).getTag(tagkey);
+@@ -1272,11 +1466,22 @@
+ @Nullable
+ @Override
+ public WorldMap getMapData(String s) {
+- return (WorldMap) this.getServer().overworld().getDataStorage().get(WorldMap.factory(), s);
++ // CraftBukkit start
++ WorldMap worldmap = (WorldMap) this.getServer().overworld().getDataStorage().get(WorldMap.factory(), s);
++ if (worldmap != null) {
++ worldmap.id = s;
++ }
++ return worldmap;
++ // CraftBukkit end
+ }
+
+ @Override
+ public void setMapData(String s, WorldMap worldmap) {
++ // CraftBukkit start
++ worldmap.id = s;
++ MapInitializeEvent event = new MapInitializeEvent(worldmap.mapView);
++ Bukkit.getServer().getPluginManager().callEvent(event);
++ // CraftBukkit end
+ this.getServer().overworld().getDataStorage().set(s, worldmap);
+ }
+
+@@ -1574,6 +1779,11 @@
+ @Override
+ public void blockUpdated(BlockPosition blockposition, Block block) {
+ if (!this.isDebug()) {
++ // CraftBukkit start
++ if (populating) {
++ return;
++ }
++ // CraftBukkit end
+ this.updateNeighborsAt(blockposition, block);
+ }
+
+@@ -1593,12 +1803,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
+@@ -1621,7 +1831,7 @@
+ private static <T> String getTypeCount(Iterable<T> iterable, Function<T, String> function) {
+ try {
+ Object2IntOpenHashMap<String> object2intopenhashmap = new Object2IntOpenHashMap();
+- Iterator iterator = iterable.iterator();
++ Iterator<T> iterator = iterable.iterator(); // CraftBukkit - decompile error
+
+ while (iterator.hasNext()) {
+ T t0 = iterator.next();
+@@ -1630,7 +1840,7 @@
+ object2intopenhashmap.addTo(s, 1);
+ }
+
+- return (String) object2intopenhashmap.object2IntEntrySet().stream().sorted(Comparator.comparing(Entry::getIntValue).reversed()).limit(5L).map((entry) -> {
++ return (String) object2intopenhashmap.object2IntEntrySet().stream().sorted(Comparator.comparing(Entry<String>::getIntValue).reversed()).limit(5L).map((entry) -> { // CraftBukkit - decompile error
+ String s1 = (String) entry.getKey();
+
+ return s1 + ":" + entry.getIntValue();
+@@ -1641,17 +1851,33 @@
+ }
+
+ public static void makeObsidianPlatform(WorldServer worldserver) {
++ // CraftBukkit start
++ WorldServer.makeObsidianPlatform(worldserver, null);
++ }
++
++ public static void makeObsidianPlatform(WorldServer worldserver, Entity entity) {
++ // CraftBukkit end
+ BlockPosition blockposition = WorldServer.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);
+ BlockPosition.betweenClosed(i - 2, j + 1, k - 2, i + 2, j + 3, k + 2).forEach((blockposition1) -> {
+- worldserver.setBlockAndUpdate(blockposition1, Blocks.AIR.defaultBlockState());
++ blockList.setBlock(blockposition1, Blocks.AIR.defaultBlockState(), 3);
+ });
+ BlockPosition.betweenClosed(i - 2, j, k - 2, i + 2, j, k + 2).forEach((blockposition1) -> {
+- worldserver.setBlockAndUpdate(blockposition1, Blocks.OBSIDIAN.defaultBlockState());
++ 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
+@@ -1785,6 +2011,8 @@
+ }
+
+ entity.updateDynamicGameEventListener(DynamicGameEventListener::add);
++ entity.inWorld = true; // CraftBukkit - Mark entity as in world
++ entity.valid = true; // CraftBukkit
+ }
+
+ public void onTrackingEnd(Entity entity) {
+@@ -1821,6 +2049,14 @@
+ }
+
+ entity.updateDynamicGameEventListener(DynamicGameEventListener::remove);
++ // CraftBukkit start
++ entity.valid = false;
++ if (!(entity instanceof EntityPlayer)) {
++ for (EntityPlayer player : players) {
++ player.getBukkitEntity().onEntityRemove(entity);
++ }
++ }
++ // CraftBukkit end
+ }
+
+ public void onSectionChange(Entity entity) {