diff --git a/patches/api/0495-add-biome-change-per-player.patch b/patches/api/0495-add-biome-change-per-player.patch new file mode 100644 index 0000000000..452006f5b8 --- /dev/null +++ b/patches/api/0495-add-biome-change-per-player.patch @@ -0,0 +1,275 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Yannick Lamprecht +Date: Sat, 24 Feb 2024 15:29:32 +0100 +Subject: [PATCH] add biome change per player + + +diff --git a/src/main/java/io/papermc/paper/event/packet/PlayerBiomesLoadEvent.java b/src/main/java/io/papermc/paper/event/packet/PlayerBiomesLoadEvent.java +new file mode 100644 +index 0000000000000000000000000000000000000000..cf8e0d297144e12c64cc7a9036ca896c1f4ac7af +--- /dev/null ++++ b/src/main/java/io/papermc/paper/event/packet/PlayerBiomesLoadEvent.java +@@ -0,0 +1,78 @@ ++package io.papermc.paper.event.packet; ++ ++import org.bukkit.BiomesSnapshot; ++import org.bukkit.Chunk; ++import org.bukkit.entity.Player; ++import org.bukkit.event.HandlerList; ++import org.bukkit.event.world.ChunkEvent; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++/** ++ * Is called when a {@link Player} receives {@link org.bukkit.block.Biome}s ++ *

++ * Can for example be used for replacing Biomes when the player receives the Biome list. ++ *

++ * Should only be used for packet/clientside related stuff. ++ * Not intended for modifying server side state. ++ */ ++public class PlayerBiomesLoadEvent extends ChunkEvent { ++ ++ private static final HandlerList HANDLER_LIST = new HandlerList(); ++ ++ private final Player player; ++ private BiomesSnapshot biomesSnapshot; ++ ++ @ApiStatus.Internal ++ public PlayerBiomesLoadEvent(@NotNull final Player player, Chunk chunk) { ++ super(chunk); ++ this.player = player; ++ } ++ ++ @NotNull ++ @Override ++ public HandlerList getHandlers() { ++ return HANDLER_LIST; ++ } ++ ++ public static @NotNull HandlerList getHandlerList() { ++ return HANDLER_LIST; ++ } ++ ++ /** ++ * Returns the player that is receiving the biomes ++ * ++ * @return the player ++ */ ++ public @NotNull Player getPlayer() { ++ return player; ++ } ++ ++ /** ++ * Returns a biomes snapshot for the given chunk, by default it won't be set. ++ * ++ * @return biome snapshot if one is set ++ */ ++ public @Nullable BiomesSnapshot getBiomeSnapshot() { ++ return biomesSnapshot; ++ } ++ ++ /** ++ * Sets the biome snapshot for the given chunk that will be sent as an override to the player ++ * ++ * @param biomesSnapshot the biome override ++ */ ++ public void setBiomeSnapshot(@Nullable final BiomesSnapshot biomesSnapshot) { ++ this.biomesSnapshot = biomesSnapshot; ++ } ++ ++ /** ++ * Returns if chunk biomes were overridden ++ * ++ * @return true if override was made, else false ++ */ ++ public boolean hasOverrides() { ++ return biomesSnapshot != null; ++ } ++} +diff --git a/src/main/java/io/papermc/paper/event/packet/PlayerChunkLoadEvent.java b/src/main/java/io/papermc/paper/event/packet/PlayerChunkLoadEvent.java +index 2815c5802eb38e5a48f9db42b9247e24c27db134..26a361de3eb05a432b0e115e3ed7e038987cf8ac 100644 +--- a/src/main/java/io/papermc/paper/event/packet/PlayerChunkLoadEvent.java ++++ b/src/main/java/io/papermc/paper/event/packet/PlayerChunkLoadEvent.java +@@ -16,7 +16,7 @@ import org.jspecify.annotations.NullMarked; + * Not intended for modifying server side state. + */ + @NullMarked +-public class PlayerChunkLoadEvent extends ChunkEvent { ++public class PlayerChunkLoadEvent extends PlayerBiomesLoadEvent { + + private static final HandlerList HANDLER_LIST = new HandlerList(); + +@@ -24,7 +24,7 @@ public class PlayerChunkLoadEvent extends ChunkEvent { + + @ApiStatus.Internal + public PlayerChunkLoadEvent(final Chunk chunk, final Player player) { +- super(chunk); ++ super(player, chunk); + this.player = player; + } + +diff --git a/src/main/java/org/bukkit/BiomesSnapshot.java b/src/main/java/org/bukkit/BiomesSnapshot.java +new file mode 100644 +index 0000000000000000000000000000000000000000..bb8683127eca2b86b23efa81bff9c54a357e04d4 +--- /dev/null ++++ b/src/main/java/org/bukkit/BiomesSnapshot.java +@@ -0,0 +1,74 @@ ++package org.bukkit; ++ ++import org.bukkit.block.Biome; ++import org.jetbrains.annotations.NotNull; ++ ++/** ++ * Represents a static, thread-safe snapshot of chunk of biomes. ++ *

++ * Purpose is to allow clean, efficient copy of a biome data to be made, and ++ * then handed off for processing in another thread ++ */ ++public interface BiomesSnapshot { ++ ++ /** ++ * Gets the X-coordinate of this chunk ++ * ++ * @return X-coordinate ++ */ ++ int getX(); ++ ++ /** ++ * Gets the Z-coordinate of this chunk ++ * ++ * @return Z-coordinate ++ */ ++ int getZ(); ++ ++ /** ++ * Gets name of the world containing this chunk ++ * ++ * @return Parent World Name ++ */ ++ @NotNull ++ String getWorldName(); ++ ++ /** ++ * Get biome at given coordinates ++ * ++ * @param x X-coordinate (0-15) ++ * @param y Y-coordinate (world minHeight (inclusive) - world maxHeight (exclusive)) ++ * @param z Z-coordinate (0-15) ++ * @return Biome at given coordinate ++ */ ++ @NotNull ++ Biome getBiome(int x, int y, int z); ++ ++ /** ++ * Get biome at given coordinates ++ * ++ * @param x X-coordinate (0-15) ++ * @param y Y-coordinate (world minHeight (inclusive) - world maxHeight (exclusive)) ++ * @param z Z-coordinate (0-15) ++ * @param biome the biome to set at the give coordinate ++ */ ++ void setBiome(int x, int y, int z, @NotNull Biome biome); ++ ++ /** ++ * Get raw biome temperature at given coordinates ++ * ++ * @param x X-coordinate (0-15) ++ * @param y Y-coordinate (world minHeight (inclusive) - world maxHeight (exclusive)) ++ * @param z Z-coordinate (0-15) ++ * @return temperature at given coordinate ++ */ ++ double getRawBiomeTemperature(int x, int y, int z); ++ ++ /** ++ * Tests if this chunk contains the specified biome. ++ * ++ * @param biome biome to test ++ * @return if the biome is contained within ++ */ ++ boolean contains(@NotNull Biome biome); ++} +diff --git a/src/main/java/org/bukkit/ChunkSnapshot.java b/src/main/java/org/bukkit/ChunkSnapshot.java +index dc765dea47a9a1c1520fb16ddb24f81413ed0dd1..983c2d22498ecf668b15d818556e27adba52a955 100644 +--- a/src/main/java/org/bukkit/ChunkSnapshot.java ++++ b/src/main/java/org/bukkit/ChunkSnapshot.java +@@ -10,29 +10,7 @@ import org.jetbrains.annotations.NotNull; + * Purpose is to allow clean, efficient copy of a chunk data to be made, and + * then handed off for processing in another thread (e.g. map rendering) + */ +-public interface ChunkSnapshot { +- +- /** +- * Gets the X-coordinate of this chunk +- * +- * @return X-coordinate +- */ +- int getX(); +- +- /** +- * Gets the Z-coordinate of this chunk +- * +- * @return Z-coordinate +- */ +- int getZ(); +- +- /** +- * Gets name of the world containing this chunk +- * +- * @return Parent World Name +- */ +- @NotNull +- String getWorldName(); ++public interface ChunkSnapshot extends BiomesSnapshot { + + /** + * Get block type for block at corresponding coordinate in the chunk +@@ -110,17 +88,6 @@ public interface ChunkSnapshot { + @Deprecated + Biome getBiome(int x, int z); + +- /** +- * Get biome at given coordinates +- * +- * @param x X-coordinate (0-15) +- * @param y Y-coordinate (world minHeight (inclusive) - world maxHeight (exclusive)) +- * @param z Z-coordinate (0-15) +- * @return Biome at given coordinate +- */ +- @NotNull +- Biome getBiome(int x, int y, int z); +- + /** + * Get raw biome temperature at given coordinates + * +@@ -132,16 +99,6 @@ public interface ChunkSnapshot { + @Deprecated + double getRawBiomeTemperature(int x, int z); + +- /** +- * Get raw biome temperature at given coordinates +- * +- * @param x X-coordinate (0-15) +- * @param y Y-coordinate (world minHeight (inclusive) - world maxHeight (exclusive)) +- * @param z Z-coordinate (0-15) +- * @return temperature at given coordinate +- */ +- double getRawBiomeTemperature(int x, int y, int z); +- + /** + * Get world full time when chunk snapshot was captured + * +@@ -164,12 +121,4 @@ public interface ChunkSnapshot { + * @return if the block is contained within + */ + boolean contains(@NotNull BlockData block); +- +- /** +- * Tests if this chunk contains the specified biome. +- * +- * @param biome biome to test +- * @return if the biome is contained within +- */ +- boolean contains(@NotNull Biome biome); + } diff --git a/patches/server/1061-add-biome-change-per-player.patch b/patches/server/1061-add-biome-change-per-player.patch new file mode 100644 index 0000000000..e915540fac --- /dev/null +++ b/patches/server/1061-add-biome-change-per-player.patch @@ -0,0 +1,378 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Yannick Lamprecht +Date: Sat, 24 Feb 2024 15:29:57 +0100 +Subject: [PATCH] add biome change per player + + +diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java +index 3c1cad5c2b34047cec44734ba4e8348cabec6f80..7d09828808b774e05fae5aabb1b8568a898ea681 100644 +--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java ++++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java +@@ -2,14 +2,23 @@ package net.minecraft.network.protocol.game; + + import io.netty.buffer.ByteBuf; + import io.netty.buffer.Unpooled; ++import io.papermc.paper.event.packet.PlayerBiomesLoadEvent; ++import java.util.ArrayList; + import java.util.List; ++import net.minecraft.core.Holder; + import net.minecraft.network.FriendlyByteBuf; + import net.minecraft.network.codec.StreamCodec; + import net.minecraft.network.protocol.Packet; ++import net.minecraft.world.entity.player.Player; + import net.minecraft.network.protocol.PacketType; + import net.minecraft.world.level.ChunkPos; ++import net.minecraft.world.level.biome.Biome; + import net.minecraft.world.level.chunk.LevelChunk; + import net.minecraft.world.level.chunk.LevelChunkSection; ++import net.minecraft.world.level.chunk.PalettedContainer; ++import org.bukkit.BiomesSnapshot; ++import org.bukkit.craftbukkit.CraftBiomesSnapshot; ++import org.bukkit.craftbukkit.CraftChunk; + + public record ClientboundChunksBiomesPacket(List chunkBiomeData) implements Packet { + public static final StreamCodec STREAM_CODEC = Packet.codec( +@@ -21,8 +30,27 @@ public record ClientboundChunksBiomesPacket(List chunks) { +- return new ClientboundChunksBiomesPacket(chunks.stream().map(ClientboundChunksBiomesPacket.ChunkBiomeData::new).toList()); ++ public static ClientboundChunksBiomesPacket forChunks(Player player, List chunks) { ++ org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.getBukkitEntity(); ++ List biomeData = new ArrayList<>(); ++ for (LevelChunk chunk : chunks) { ++ final PlayerBiomesLoadEvent biomesLoadEvent = new PlayerBiomesLoadEvent(bukkitPlayer, new CraftChunk(chunk)); ++ biomesLoadEvent.callEvent(); ++ if (biomesLoadEvent.hasOverrides()) { ++ biomeData.add(create(chunk, biomesLoadEvent)); ++ } else { ++ biomeData.add(new ChunkBiomeData(chunk)); ++ } ++ } ++ return new ClientboundChunksBiomesPacket(biomeData); ++ } ++ ++ private static ClientboundChunksBiomesPacket.ChunkBiomeData create(LevelChunk levelChunk, PlayerBiomesLoadEvent biomesLoadEvent) { ++ final BiomesSnapshot snapshotAt = biomesLoadEvent.getBiomeSnapshot(); ++ if(snapshotAt != null){ ++ return new ClientboundChunksBiomesPacket.ChunkBiomeData(snapshotAt); ++ } ++ return new ClientboundChunksBiomesPacket.ChunkBiomeData(levelChunk); + } + + private void write(FriendlyByteBuf buf) { +@@ -45,6 +73,11 @@ public record ClientboundChunksBiomesPacket(List> container : ((CraftBiomesSnapshot) biomesSnapshot).getBiome()) { ++ container.write(buf, null, chunkSectionIndex); ++ chunkSectionIndex++; // Paper - Anti-Xray ++ } ++ } ++ ++ private static int calculateChunkSize(BiomesSnapshot biomesSnapshot) { ++ int i = 0; ++ ++ for(final PalettedContainer> container : ((CraftBiomesSnapshot) biomesSnapshot).getBiome()) { ++ i += container.getSerializedSize(); ++ } ++ ++ return i; ++ } ++ ++ + public void write(FriendlyByteBuf buf) { + buf.writeChunkPos(this.pos); + buf.writeByteArray(this.buffer); +diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java +index 5b3a886c624b36557cbfaccdc3fb05a46a4ba36a..5344f816c31617c3061aa6d43e98ced308b2a7b3 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +@@ -1075,7 +1075,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + } + + map.forEach((entityplayer1, list1) -> { +- entityplayer1.connection.send(ClientboundChunksBiomesPacket.forChunks(list1)); ++ entityplayer1.connection.send(ClientboundChunksBiomesPacket.forChunks(entityplayer1, list1)); + }); + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftBiomesSnapshot.java b/src/main/java/org/bukkit/craftbukkit/CraftBiomesSnapshot.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a77495e66c6a5e1da2cc154269c09ce8b0d21d2b +--- /dev/null ++++ b/src/main/java/org/bukkit/craftbukkit/CraftBiomesSnapshot.java +@@ -0,0 +1,105 @@ ++package org.bukkit.craftbukkit; ++ ++import com.google.common.base.Preconditions; ++import com.google.common.base.Predicates; ++import java.util.Objects; ++import java.util.function.Predicate; ++import net.minecraft.core.BlockPos; ++import net.minecraft.core.Holder; ++import net.minecraft.core.Registry; ++import net.minecraft.world.level.chunk.PalettedContainer; ++import net.minecraft.world.level.chunk.PalettedContainerRO; ++import org.bukkit.BiomesSnapshot; ++import org.bukkit.block.Biome; ++import org.bukkit.craftbukkit.block.CraftBiome; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.jetbrains.annotations.NotNull; ++ ++public class CraftBiomesSnapshot implements BiomesSnapshot { ++ ++ private final int x, z; ++ private final String worldname; ++ private final int minHeight, maxHeight, seaLevel; ++ private final Registry biomeRegistry; ++ private final PalettedContainer>[] biome; ++ ++ public CraftBiomesSnapshot(final int x, final int z, final String worldname, final int minHeight, final int maxHeight, int seaLevel, final Registry registry, final PalettedContainer>[] biome) { ++ this.x = x; ++ this.z = z; ++ this.worldname = worldname; ++ this.minHeight = minHeight; ++ this.maxHeight = maxHeight; ++ this.biomeRegistry = registry; ++ this.biome = biome; ++ this.seaLevel = seaLevel; ++ } ++ ++ @Override ++ public int getX() { ++ return this.x; ++ } ++ ++ @Override ++ public int getZ() { ++ return this.z; ++ } ++ ++ @Override ++ public String getWorldName() { ++ return this.worldname; ++ } ++ ++ @Override ++ public void setBiome(final int x, final int y, final int z, @NotNull final Biome biome) { ++ Preconditions.checkState(this.biome != null, "ChunkSnapshot created without biome. Please call getSnapshot with includeBiome=true"); ++ Objects.requireNonNull(biome, "biome cannot be null"); ++ this.validateChunkCoordinates(x, y, z); ++ PalettedContainer> biomeLocal = this.biome[this.getSectionIndex(y)]; ++ biomeLocal.set(x >> 2, (y & 0xF) >> 2, z >> 2, CraftBiome.bukkitToMinecraftHolder(biome)); ++ } ++ ++ @Override ++ public final double getRawBiomeTemperature(int x, int y, int z) { ++ Preconditions.checkState(this.biome != null, "ChunkSnapshot created without biome. Please call getSnapshot with includeBiome=true"); ++ this.validateChunkCoordinates(x, y, z); ++ ++ PalettedContainerRO> biome = this.biome[this.getSectionIndex(y)]; // SPIGOT-7188: Don't need to convert y to biome coordinate scale since it is bound to the block chunk section ++ return biome.get(x >> 2, (y & 0xF) >> 2, z >> 2).value().getTemperature(new BlockPos((this.x << 4) | x, y, (this.z << 4) | z), seaLevel); ++ } ++ ++ @Override ++ public final Biome getBiome(int x, int y, int z) { ++ Preconditions.checkState(this.biome != null, "ChunkSnapshot created without biome. Please call getSnapshot with includeBiome=true"); ++ this.validateChunkCoordinates(x, y, z); ++ ++ PalettedContainerRO> biome = this.biome[this.getSectionIndex(y)]; // SPIGOT-7188: Don't need to convert y to biome coordinate scale since it is bound to the block chunk section ++ return CraftBiome.minecraftHolderToBukkit(biome.get(x >> 2, (y & 0xF) >> 2, z >> 2)); ++ } ++ ++ ++ @Override ++ public boolean contains(@NonNull Biome biome) { ++ Preconditions.checkArgument(biome != null, "Biome cannot be null"); ++ ++ Predicate> nms = Predicates.equalTo(CraftBiome.bukkitToMinecraftHolder(biome)); ++ for (PalettedContainerRO> palette : this.biome) { ++ if (palette.maybeHas(nms)) { ++ return true; ++ } ++ } ++ ++ return false; ++ } ++ ++ public PalettedContainer>[] getBiome(){ ++ return biome; ++ } ++ ++ protected void validateChunkCoordinates(int x, int y, int z) { ++ CraftChunk.validateChunkCoordinates(this.minHeight, this.maxHeight, x, y, z); ++ } ++ ++ protected int getSectionIndex(int y) { ++ return (y - this.minHeight) >> 4; ++ } ++} +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +index 0074bc0e7147dc3a8c538e796f14ac9bf8725896..76032e22191fbafe92f984b25b5b3b652ecadd17 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +@@ -302,7 +302,7 @@ public class CraftChunk implements Chunk { + byte[][] sectionEmitLights = includeLightData ? new byte[cs.length][] : null; + // Paper end - Add getChunkSnapshot includeLightData parameter + boolean[] sectionEmpty = new boolean[cs.length]; +- PalettedContainerRO>[] biome = (includeBiome || includeBiomeTempRain) ? new PalettedContainer[cs.length] : null; ++ PalettedContainer>[] biome = (includeBiome || includeBiomeTempRain) ? new PalettedContainer[cs.length] : null; + + Registry iregistry = this.worldServer.registryAccess().lookupOrThrow(Registries.BIOME); + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java b/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java +index ddcbdbcbfeb6efe0a587b1181505423cc9d2c951..e85bcec5e8825146fa2623588473173dba4b73cb 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java +@@ -3,7 +3,6 @@ package org.bukkit.craftbukkit; + import com.google.common.base.Preconditions; + import com.google.common.base.Predicates; + import java.util.function.Predicate; +-import net.minecraft.core.BlockPos; + import net.minecraft.core.Holder; + import net.minecraft.core.Registry; + import net.minecraft.world.level.block.state.BlockState; +@@ -14,8 +13,6 @@ import org.bukkit.ChunkSnapshot; + import org.bukkit.Material; + import org.bukkit.block.Biome; + import org.bukkit.block.data.BlockData; +-import org.bukkit.craftbukkit.block.CraftBiome; +-import org.bukkit.craftbukkit.block.CraftBlockType; + import org.bukkit.craftbukkit.block.data.CraftBlockData; + import org.bukkit.craftbukkit.util.CraftMagicNumbers; + +@@ -23,49 +20,22 @@ import org.bukkit.craftbukkit.util.CraftMagicNumbers; + * Represents a static, thread-safe snapshot of chunk of blocks + * Purpose is to allow clean, efficient copy of a chunk data to be made, and then handed off for processing in another thread (e.g. map rendering) + */ +-public class CraftChunkSnapshot implements ChunkSnapshot { +- private final int x, z; +- private final int minHeight, maxHeight, seaLevel; +- private final String worldname; ++public class CraftChunkSnapshot extends CraftBiomesSnapshot implements ChunkSnapshot { + private final PalettedContainer[] blockids; + private final byte[][] skylight; + private final byte[][] emitlight; + private final boolean[] empty; + private final Heightmap hmap; // Height map + private final long captureFulltime; +- private final Registry biomeRegistry; +- private final PalettedContainerRO>[] biome; +- +- CraftChunkSnapshot(int x, int z, int minHeight, int maxHeight, int seaLevel, String wname, long wtime, PalettedContainer[] sectionBlockIDs, byte[][] sectionSkyLights, byte[][] sectionEmitLights, boolean[] sectionEmpty, Heightmap hmap, Registry biomeRegistry, PalettedContainerRO>[] biome) { +- this.x = x; +- this.z = z; +- this.minHeight = minHeight; +- this.maxHeight = maxHeight; +- this.seaLevel = seaLevel; +- this.worldname = wname; ++ ++ CraftChunkSnapshot(int x, int z, int minHeight, int maxHeight, int seaLevel, String wname, long wtime, PalettedContainer[] sectionBlockIDs, byte[][] sectionSkyLights, byte[][] sectionEmitLights, boolean[] sectionEmpty, Heightmap hmap, Registry biomeRegistry, PalettedContainer>[] biome) { ++ super(x, z, wname, minHeight, maxHeight, seaLevel, biomeRegistry, biome); + this.captureFulltime = wtime; + this.blockids = sectionBlockIDs; + this.skylight = sectionSkyLights; + this.emitlight = sectionEmitLights; + this.empty = sectionEmpty; + this.hmap = hmap; +- this.biomeRegistry = biomeRegistry; +- this.biome = biome; +- } +- +- @Override +- public int getX() { +- return this.x; +- } +- +- @Override +- public int getZ() { +- return this.z; +- } +- +- @Override +- public String getWorldName() { +- return this.worldname; + } + + @Override +@@ -82,20 +52,6 @@ public class CraftChunkSnapshot implements ChunkSnapshot { + return false; + } + +- @Override +- public boolean contains(Biome biome) { +- Preconditions.checkArgument(biome != null, "Biome cannot be null"); +- +- Predicate> nms = Predicates.equalTo(CraftBiome.bukkitToMinecraftHolder(biome)); +- for (PalettedContainerRO> palette : this.biome) { +- if (palette.maybeHas(nms)) { +- return true; +- } +- } +- +- return false; +- } +- + @Override + public Material getBlockType(int x, int y, int z) { + this.validateChunkCoordinates(x, y, z); +@@ -148,29 +104,11 @@ public class CraftChunkSnapshot implements ChunkSnapshot { + return this.getBiome(x, 0, z); + } + +- @Override +- public final Biome getBiome(int x, int y, int z) { +- Preconditions.checkState(this.biome != null, "ChunkSnapshot created without biome. Please call getSnapshot with includeBiome=true"); +- this.validateChunkCoordinates(x, y, z); +- +- PalettedContainerRO> biome = this.biome[this.getSectionIndex(y)]; // SPIGOT-7188: Don't need to convert y to biome coordinate scale since it is bound to the block chunk section +- return CraftBiome.minecraftHolderToBukkit(biome.get(x >> 2, (y & 0xF) >> 2, z >> 2)); +- } +- + @Override + public final double getRawBiomeTemperature(int x, int z) { + return this.getRawBiomeTemperature(x, 0, z); + } + +- @Override +- public final double getRawBiomeTemperature(int x, int y, int z) { +- Preconditions.checkState(this.biome != null, "ChunkSnapshot created without biome. Please call getSnapshot with includeBiome=true"); +- this.validateChunkCoordinates(x, y, z); +- +- PalettedContainerRO> biome = this.biome[this.getSectionIndex(y)]; // SPIGOT-7188: Don't need to convert y to biome coordinate scale since it is bound to the block chunk section +- return biome.get(x >> 2, (y & 0xF) >> 2, z >> 2).value().getTemperature(new BlockPos((this.x << 4) | x, y, (this.z << 4) | z), this.seaLevel); +- } +- + @Override + public final long getCaptureFullTime() { + return this.captureFulltime; +@@ -180,12 +118,4 @@ public class CraftChunkSnapshot implements ChunkSnapshot { + public final boolean isSectionEmpty(int sy) { + return this.empty[sy]; + } +- +- private int getSectionIndex(int y) { +- return (y - this.minHeight) >> 4; +- } +- +- private void validateChunkCoordinates(int x, int y, int z) { +- CraftChunk.validateChunkCoordinates(this.minHeight, this.maxHeight, x, y, z); +- } + }