mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-25 14:00:15 +01:00
add biome change per player
This commit is contained in:
parent
aa2c52baac
commit
4151472c45
7 changed files with 263 additions and 128 deletions
|
@ -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.Nullable;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Is called when a {@link Player} receives {@link org.bukkit.block.Biome}s
|
||||
* <p>
|
||||
* Can for example be used for replacing Biomes when the player receives the Biome list.
|
||||
* <p>
|
||||
* Should only be used for packet/clientside related stuff.
|
||||
* Not intended for modifying server side state.
|
||||
*/
|
||||
@NullMarked
|
||||
public class PlayerBiomesLoadEvent extends ChunkEvent {
|
||||
|
||||
private static final HandlerList HANDLER_LIST = new HandlerList();
|
||||
|
||||
private final Player player;
|
||||
private @Nullable BiomesSnapshot biomesSnapshot;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public PlayerBiomesLoadEvent(final Player player, Chunk chunk) {
|
||||
super(chunk);
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return HANDLER_LIST;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return HANDLER_LIST;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the player that is receiving the biomes
|
||||
*
|
||||
* @return the player
|
||||
*/
|
||||
public 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
73
paper-api/src/main/java/org/bukkit/BiomesSnapshot.java
Normal file
73
paper-api/src/main/java/org/bukkit/BiomesSnapshot.java
Normal file
|
@ -0,0 +1,73 @@
|
|||
package org.bukkit;
|
||||
|
||||
import org.bukkit.block.Biome;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Represents a static, thread-safe snapshot of chunk of biomes.
|
||||
* <p>
|
||||
* Purpose is to allow clean, efficient copy of a biome data to be made, and
|
||||
* then handed off for processing in another thread
|
||||
*/
|
||||
@NullMarked
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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, 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(Biome biome);
|
||||
}
|
|
@ -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(since = "1.15")
|
||||
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(since = "1.15")
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class CraftBiomesSnapshot implements BiomesSnapshot {
|
||||
|
||||
private final int x, z;
|
||||
private final String worldname;
|
||||
private final int minHeight, maxHeight, seaLevel;
|
||||
private final Registry<net.minecraft.world.level.biome.Biome> biomeRegistry;
|
||||
private final PalettedContainer<Holder<net.minecraft.world.level.biome.Biome>>[] biome;
|
||||
|
||||
public CraftBiomesSnapshot(final int x, final int z, final String worldname, final int minHeight, final int maxHeight, int seaLevel, final Registry<net.minecraft.world.level.biome.Biome> registry, final PalettedContainer<Holder<net.minecraft.world.level.biome.Biome>>[] 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, 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<Holder<net.minecraft.world.level.biome.Biome>> 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<Holder<net.minecraft.world.level.biome.Biome>> 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<Holder<net.minecraft.world.level.biome.Biome>> 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(Biome biome) {
|
||||
Preconditions.checkArgument(biome != null, "Biome cannot be null");
|
||||
|
||||
Predicate<Holder<net.minecraft.world.level.biome.Biome>> nms = Predicates.equalTo(CraftBiome.bukkitToMinecraftHolder(biome));
|
||||
for (PalettedContainerRO<Holder<net.minecraft.world.level.biome.Biome>> palette : this.biome) {
|
||||
if (palette.maybeHas(nms)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public PalettedContainer<Holder<net.minecraft.world.level.biome.Biome>>[] 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;
|
||||
}
|
||||
}
|
|
@ -303,7 +303,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<Holder<net.minecraft.world.level.biome.Biome>>[] biome = (includeBiome || includeBiomeTempRain) ? new PalettedContainer[cs.length] : null;
|
||||
PalettedContainer<Holder<net.minecraft.world.level.biome.Biome>>[] biome = (includeBiome || includeBiomeTempRain) ? new PalettedContainer[cs.length] : null;
|
||||
|
||||
Registry<net.minecraft.world.level.biome.Biome> iregistry = this.worldServer.registryAccess().lookupOrThrow(Registries.BIOME);
|
||||
|
||||
|
|
|
@ -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<BlockState>[] 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<net.minecraft.world.level.biome.Biome> biomeRegistry;
|
||||
private final PalettedContainerRO<Holder<net.minecraft.world.level.biome.Biome>>[] biome;
|
||||
|
||||
CraftChunkSnapshot(int x, int z, int minHeight, int maxHeight, int seaLevel, String wname, long wtime, PalettedContainer<BlockState>[] sectionBlockIDs, byte[][] sectionSkyLights, byte[][] sectionEmitLights, boolean[] sectionEmpty, Heightmap hmap, Registry<net.minecraft.world.level.biome.Biome> biomeRegistry, PalettedContainerRO<Holder<net.minecraft.world.level.biome.Biome>>[] 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<BlockState>[] sectionBlockIDs, byte[][] sectionSkyLights, byte[][] sectionEmitLights, boolean[] sectionEmpty, Heightmap hmap, Registry<net.minecraft.world.level.biome.Biome> biomeRegistry, PalettedContainer<Holder<net.minecraft.world.level.biome.Biome>>[] 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<Holder<net.minecraft.world.level.biome.Biome>> nms = Predicates.equalTo(CraftBiome.bukkitToMinecraftHolder(biome));
|
||||
for (PalettedContainerRO<Holder<net.minecraft.world.level.biome.Biome>> 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<Holder<net.minecraft.world.level.biome.Biome>> 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<Holder<net.minecraft.world.level.biome.Biome>> 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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue