Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-12-25 15:50:14 +01:00
Yeet lectern cache (#4695)
* attempt to yeet lectern cache * yeet lecternutils usage * properly update lecterns * yeet accidental diff
Dieser Commit ist enthalten in:
Ursprung
675faf6bb4
Commit
63c84bc25b
@ -25,11 +25,6 @@
|
||||
|
||||
package org.geysermc.geyser.platform.mod.world;
|
||||
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.BannerPatternLayer;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityInfo;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.minecraft.SharedConstants;
|
||||
@ -48,24 +43,19 @@ import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BannerBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BannerPatternLayers;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.LecternBlockEntity;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.geysermc.erosion.util.LecternUtils;
|
||||
import org.geysermc.geyser.level.GeyserWorldManager;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.BannerPatternLayer;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@ -121,94 +111,6 @@ public class GeyserModWorldManager extends GeyserWorldManager {
|
||||
return SharedConstants.getCurrentVersion().getProtocolVersion() == GameProtocol.getJavaProtocolVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExpectLecternHandled(GeyserSession session) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendLecternData(GeyserSession session, int x, int z, List<BlockEntityInfo> blockEntityInfos) {
|
||||
server.execute(() -> {
|
||||
ServerPlayer player = getPlayer(session);
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
//noinspection resource - level() is just a getter
|
||||
LevelChunk chunk = player.level().getChunk(x, z);
|
||||
final int chunkBlockX = x << 4;
|
||||
final int chunkBlockZ = z << 4;
|
||||
//noinspection ForLoopReplaceableByForEach - avoid constructing iterator
|
||||
for (int i = 0; i < blockEntityInfos.size(); i++) {
|
||||
BlockEntityInfo blockEntityInfo = blockEntityInfos.get(i);
|
||||
BlockEntity blockEntity = chunk.getBlockEntity(new BlockPos(chunkBlockX + blockEntityInfo.getX(),
|
||||
blockEntityInfo.getY(), chunkBlockZ + blockEntityInfo.getZ()));
|
||||
sendLecternData(session, blockEntity, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendLecternData(GeyserSession session, int x, int y, int z) {
|
||||
server.execute(() -> {
|
||||
ServerPlayer player = getPlayer(session);
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
//noinspection resource - level() is just a getter
|
||||
BlockEntity blockEntity = player.level().getBlockEntity(new BlockPos(x, y, z));
|
||||
sendLecternData(session, blockEntity, false);
|
||||
});
|
||||
}
|
||||
|
||||
private void sendLecternData(GeyserSession session, BlockEntity blockEntity, boolean isChunkLoad) {
|
||||
if (!(blockEntity instanceof LecternBlockEntity lectern)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int x = blockEntity.getBlockPos().getX();
|
||||
int y = blockEntity.getBlockPos().getY();
|
||||
int z = blockEntity.getBlockPos().getZ();
|
||||
|
||||
if (!lectern.hasBook()) {
|
||||
if (!isChunkLoad) {
|
||||
BlockEntityUtils.updateBlockEntity(session, LecternUtils.getBaseLecternTag(x, y, z, 0).build(), Vector3i.from(x, y, z));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack book = lectern.getBook();
|
||||
int pageCount = getPageCount(book);
|
||||
boolean hasBookPages = pageCount > 0;
|
||||
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(x, y, z, hasBookPages ? pageCount : 1);
|
||||
lecternTag.putInt("page", lectern.getPage() / 2);
|
||||
NbtMapBuilder bookTag = NbtMap.builder()
|
||||
.putByte("Count", (byte) book.getCount())
|
||||
.putShort("Damage", (short) 0)
|
||||
.putString("Name", "minecraft:writable_book");
|
||||
List<NbtMap> pages = new ArrayList<>(hasBookPages ? pageCount : 1);
|
||||
if (hasBookPages) {
|
||||
List<String> bookPages = getPages(book);
|
||||
for (String page : bookPages) {
|
||||
NbtMapBuilder pageBuilder = NbtMap.builder()
|
||||
.putString("photoname", "")
|
||||
.putString("text", page);
|
||||
pages.add(pageBuilder.build());
|
||||
}
|
||||
} else {
|
||||
// Empty page
|
||||
NbtMapBuilder pageBuilder = NbtMap.builder()
|
||||
.putString("photoname", "")
|
||||
.putString("text", "");
|
||||
pages.add(pageBuilder.build());
|
||||
}
|
||||
|
||||
bookTag.putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, pages).build());
|
||||
lecternTag.putCompound("book", bookTag.build());
|
||||
NbtMap blockEntityTag = lecternTag.build();
|
||||
BlockEntityUtils.updateBlockEntity(session, blockEntityTag, Vector3i.from(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(GeyserSession session, String permission) {
|
||||
ServerPlayer player = getPlayer(session);
|
||||
|
@ -27,16 +27,12 @@ package org.geysermc.geyser.platform.spigot.world.manager;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.geysermc.erosion.bukkit.BukkitLecterns;
|
||||
import org.geysermc.erosion.bukkit.BukkitUtils;
|
||||
import org.geysermc.erosion.bukkit.PickBlockUtils;
|
||||
import org.geysermc.erosion.bukkit.SchedulerUtils;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
@ -44,12 +40,9 @@ import org.geysermc.geyser.level.GameRule;
|
||||
import org.geysermc.geyser.level.WorldManager;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@ -58,11 +51,9 @@ import java.util.concurrent.CompletableFuture;
|
||||
*/
|
||||
public class GeyserSpigotWorldManager extends WorldManager {
|
||||
private final Plugin plugin;
|
||||
private final BukkitLecterns lecterns;
|
||||
|
||||
public GeyserSpigotWorldManager(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.lecterns = new BukkitLecterns(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -95,69 +86,6 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendLecternData(GeyserSession session, int x, int y, int z) {
|
||||
Player bukkitPlayer;
|
||||
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
|
||||
// Run as a task to prevent async issues
|
||||
SchedulerUtils.runTask(this.plugin, () -> sendLecternData(session, block, false), block);
|
||||
}
|
||||
|
||||
public void sendLecternData(GeyserSession session, int x, int z, List<BlockEntityInfo> blockEntityInfos) {
|
||||
Player bukkitPlayer;
|
||||
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
|
||||
return;
|
||||
}
|
||||
if (SchedulerUtils.FOLIA) {
|
||||
Chunk chunk = getChunk(bukkitPlayer.getWorld(), x, z);
|
||||
if (chunk == null) {
|
||||
return;
|
||||
}
|
||||
Bukkit.getRegionScheduler().execute(this.plugin, bukkitPlayer.getWorld(), x, z, () ->
|
||||
sendLecternData(session, chunk, blockEntityInfos));
|
||||
} else {
|
||||
Bukkit.getScheduler().runTask(this.plugin, () -> {
|
||||
Chunk chunk = getChunk(bukkitPlayer.getWorld(), x, z);
|
||||
if (chunk == null) {
|
||||
return;
|
||||
}
|
||||
sendLecternData(session, chunk, blockEntityInfos);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable Chunk getChunk(World world, int x, int z) {
|
||||
if (!world.isChunkLoaded(x, z)) {
|
||||
return null;
|
||||
}
|
||||
return world.getChunkAt(x, z);
|
||||
}
|
||||
|
||||
private void sendLecternData(GeyserSession session, Chunk chunk, List<BlockEntityInfo> blockEntityInfos) {
|
||||
//noinspection ForLoopReplaceableByForEach - avoid constructing Iterator
|
||||
for (int i = 0; i < blockEntityInfos.size(); i++) {
|
||||
BlockEntityInfo info = blockEntityInfos.get(i);
|
||||
Block block = chunk.getBlock(info.getX(), info.getY(), info.getZ());
|
||||
sendLecternData(session, block, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendLecternData(GeyserSession session, Block block, boolean isChunkLoad) {
|
||||
NbtMap blockEntityTag = this.lecterns.getLecternData(block, isChunkLoad);
|
||||
if (blockEntityTag != null) {
|
||||
BlockEntityUtils.updateBlockEntity(session, blockEntityTag, BukkitUtils.getVector(block.getLocation()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExpectLecternHandled(GeyserSession session) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean getGameRuleBool(GeyserSession session, GameRule gameRule) {
|
||||
org.bukkit.GameRule<?> bukkitGameRule = org.bukkit.GameRule.getByName(gameRule.getJavaID());
|
||||
if (bukkitGameRule == null) {
|
||||
|
@ -28,22 +28,17 @@ package org.geysermc.geyser.level;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.geysermc.erosion.packet.backendbound.*;
|
||||
import org.geysermc.erosion.packet.backendbound.BackendboundBatchBlockRequestPacket;
|
||||
import org.geysermc.erosion.packet.backendbound.BackendboundBlockRequestPacket;
|
||||
import org.geysermc.erosion.packet.backendbound.BackendboundPickBlockPacket;
|
||||
import org.geysermc.erosion.util.BlockPositionIterator;
|
||||
import org.geysermc.erosion.util.LecternUtils;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class GeyserWorldManager extends WorldManager {
|
||||
@ -92,51 +87,6 @@ public class GeyserWorldManager extends WorldManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendLecternData(GeyserSession session, int x, int z, List<BlockEntityInfo> blockEntityInfos) {
|
||||
var erosionHandler = session.getErosionHandler().getAsActive();
|
||||
if (erosionHandler == null) {
|
||||
// No-op - don't send any additional information other than what the chunk has already sent
|
||||
return;
|
||||
}
|
||||
List<Vector3i> vectors = new ObjectArrayList<>(blockEntityInfos.size());
|
||||
//noinspection ForLoopReplaceableByForEach - avoid constructing iterator
|
||||
for (int i = 0; i < blockEntityInfos.size(); i++) {
|
||||
BlockEntityInfo info = blockEntityInfos.get(i);
|
||||
vectors.add(Vector3i.from(info.getX(), info.getY(), info.getZ()));
|
||||
}
|
||||
erosionHandler.sendPacket(new BackendboundBatchBlockEntityPacket(x, z, vectors));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendLecternData(GeyserSession session, int x, int y, int z) {
|
||||
var erosionHandler = session.getErosionHandler().getAsActive();
|
||||
if (erosionHandler != null) {
|
||||
erosionHandler.sendPacket(new BackendboundBlockEntityPacket(Vector3i.from(x, y, z)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Without direct server access, we can't get lectern information on-the-fly.
|
||||
// I should have set this up so it's only called when there is a book in the block state. - Camotoy
|
||||
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(x, y, z, 1);
|
||||
lecternTag.putCompound("book", NbtMap.builder()
|
||||
.putByte("Count", (byte) 1)
|
||||
.putShort("Damage", (short) 0)
|
||||
.putString("Name", "minecraft:written_book")
|
||||
.putCompound("tag", NbtMap.builder()
|
||||
.putString("photoname", "")
|
||||
.putString("text", "")
|
||||
.build())
|
||||
.build());
|
||||
lecternTag.putInt("page", -1); // I'm surprisingly glad this exists - it forces Bedrock to stop reading immediately. Usually.
|
||||
BlockEntityUtils.updateBlockEntity(session, lecternTag.build(), Vector3i.from(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExpectLecternHandled(GeyserSession session) {
|
||||
return session.getErosionHandler().isActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGameRule(GeyserSession session, String name, Object value) {
|
||||
super.setGameRule(session, name, value);
|
||||
|
@ -40,11 +40,9 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponen
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemCodecHelper;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityInfo;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.setting.Difficulty;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@ -118,40 +116,6 @@ public abstract class WorldManager {
|
||||
*/
|
||||
public abstract boolean hasOwnChunkCache();
|
||||
|
||||
/**
|
||||
* Sigh. <br>
|
||||
*
|
||||
* So, on Java Edition, the lectern is an inventory. Java opens it and gets the contents of the book there.
|
||||
* On Bedrock, the lectern contents are part of the block entity tag. Therefore, Bedrock expects to have the contents
|
||||
* of the lectern ready and present in the world. If the contents are not there, it takes at least two clicks for the
|
||||
* lectern to update the tag and then present itself. <br>
|
||||
*
|
||||
* We solve this problem by querying all loaded lecterns, where possible, and sending their information in a block entity
|
||||
* tag.
|
||||
* <p>
|
||||
* Note that the lectern data may be sent asynchronously.
|
||||
*
|
||||
* @param session the session of the player
|
||||
* @param x the x coordinate of the lectern
|
||||
* @param y the y coordinate of the lectern
|
||||
* @param z the z coordinate of the lectern
|
||||
*/
|
||||
public abstract void sendLecternData(GeyserSession session, int x, int y, int z);
|
||||
|
||||
/**
|
||||
* {@link #sendLecternData(GeyserSession, int, int, int)} but batched for chunks.
|
||||
*
|
||||
* @param x chunk x
|
||||
* @param z chunk z
|
||||
* @param blockEntityInfos a list of coordinates (chunk local) to grab lecterns from.
|
||||
*/
|
||||
public abstract void sendLecternData(GeyserSession session, int x, int z, List<BlockEntityInfo> blockEntityInfos);
|
||||
|
||||
/**
|
||||
* @return whether we should expect lectern data to update, or if we have to fall back on a workaround.
|
||||
*/
|
||||
public abstract boolean shouldExpectLecternHandled(GeyserSession session);
|
||||
|
||||
/**
|
||||
* Updates a gamerule value on the Java server
|
||||
*
|
||||
|
@ -91,7 +91,6 @@ public class Block {
|
||||
BlockDefinition definition = session.getBlockMappings().getBedrockBlock(state);
|
||||
sendBlockUpdatePacket(session, state, definition, position);
|
||||
|
||||
{
|
||||
// Extended collision boxes for custom blocks
|
||||
if (!session.getBlockMappings().getExtendedCollisionBoxes().isEmpty()) {
|
||||
int aboveBlock = session.getGeyser().getWorldManager().getBlockAt(session, position.getX(), position.getY() + 1, position.getZ());
|
||||
@ -123,9 +122,6 @@ public class Block {
|
||||
}
|
||||
}
|
||||
|
||||
handleLecternBlockUpdate(session, state, position);
|
||||
}
|
||||
|
||||
protected void sendBlockUpdatePacket(GeyserSession session, BlockState state, BlockDefinition definition, Vector3i position) {
|
||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||
updateBlockPacket.setDataLayer(0);
|
||||
@ -153,13 +149,6 @@ public class Block {
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleLecternBlockUpdate(GeyserSession session, BlockState state, Vector3i position) {
|
||||
// Block state is out of bounds of this map - lectern has been destroyed, if it existed
|
||||
if (!session.getGeyser().getWorldManager().shouldExpectLecternHandled(session)) {
|
||||
session.getLecternCache().remove(position);
|
||||
}
|
||||
}
|
||||
|
||||
public Item asItem() {
|
||||
if (this.item == null) {
|
||||
return this.item = Item.byBlock(this);
|
||||
|
@ -56,6 +56,15 @@ public final class BlockState {
|
||||
return (T) get(property);
|
||||
}
|
||||
|
||||
public <T extends Comparable<T>> T getValueNullable(Property<T> property) {
|
||||
var value = get(property);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (T) get(property);
|
||||
}
|
||||
|
||||
public boolean getValue(Property<Boolean> property, boolean def) {
|
||||
var value = get(property);
|
||||
if (value == null) {
|
||||
|
@ -27,35 +27,67 @@ package org.geysermc.geyser.level.block.type;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.geysermc.erosion.util.LecternUtils;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.geysermc.geyser.level.WorldManager;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.level.block.entity.BedrockChunkWantsBlockEntityTag;
|
||||
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
|
||||
public class LecternBlock extends Block {
|
||||
import java.util.Collections;
|
||||
|
||||
public class LecternBlock extends Block implements BedrockChunkWantsBlockEntityTag {
|
||||
public LecternBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleLecternBlockUpdate(GeyserSession session, BlockState state, Vector3i position) {
|
||||
WorldManager worldManager = session.getGeyser().getWorldManager();
|
||||
if (worldManager.shouldExpectLecternHandled(session)) {
|
||||
worldManager.sendLecternData(session, position.getX(), position.getY(), position.getZ());
|
||||
return;
|
||||
public NbtMap createTag(GeyserSession session, Vector3i position, BlockState blockState) {
|
||||
return getBaseLecternTag(position, blockState.getValue(Properties.HAS_BOOK));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBlock(GeyserSession session, BlockState state, Vector3i position) {
|
||||
WorldManager worldManager = session.getGeyser().getWorldManager();
|
||||
boolean currentHasBook = state.getValue(Properties.HAS_BOOK);
|
||||
Boolean previousHasBook = worldManager.blockAt(session, position).getValue(Properties.HAS_BOOK); // Can be null if not a lectern, watch out
|
||||
if (currentHasBook != previousHasBook) {
|
||||
if (currentHasBook) {
|
||||
worldManager.sendLecternData(session, position.getX(), position.getY(), position.getZ());
|
||||
Boolean previousHasBook = worldManager.blockAt(session, position).getValueNullable(Properties.HAS_BOOK); // Can be null if not a lectern, watch out
|
||||
if (previousHasBook == null || currentHasBook != previousHasBook) {
|
||||
BlockEntityUtils.updateBlockEntity(session, getBaseLecternTag(position, currentHasBook), position);
|
||||
}
|
||||
super.updateBlock(session, state, position);
|
||||
}
|
||||
|
||||
public static NbtMap getBaseLecternTag(Vector3i position, boolean hasBook) {
|
||||
if (hasBook) {
|
||||
return getBaseLecternTag(position, 1)
|
||||
.putCompound("book", NbtMap.builder()
|
||||
.putByte("Count", (byte) 1)
|
||||
.putShort("Damage", (short) 0)
|
||||
.putString("Name", "minecraft:writable_book")
|
||||
.putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, Collections.singletonList(
|
||||
NbtMap.builder()
|
||||
.putString("photoname", "")
|
||||
.putString("text", "")
|
||||
.build()
|
||||
)).build())
|
||||
.build())
|
||||
.build();
|
||||
} else {
|
||||
session.getLecternCache().remove(position);
|
||||
NbtMap newLecternTag = LecternUtils.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 0).build();
|
||||
BlockEntityUtils.updateBlockEntity(session, newLecternTag, position);
|
||||
return getBaseLecternTag(position, 0).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static NbtMapBuilder getBaseLecternTag(Vector3i position, int pages) {
|
||||
NbtMapBuilder builder = BlockEntityTranslator.getConstantBedrockTag("Lectern", position);
|
||||
builder.putBoolean("isMovable", true);
|
||||
|
||||
if (pages != 0) {
|
||||
builder.putByte("hasBook", (byte) 1);
|
||||
builder.putInt("totalPages", 1); // we'll override it anyway
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +99,6 @@ import org.geysermc.geyser.inventory.recipe.GeyserStonecutterData;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.type.BlockItem;
|
||||
import org.geysermc.geyser.level.JavaDimension;
|
||||
import org.geysermc.geyser.level.WorldManager;
|
||||
import org.geysermc.geyser.level.physics.CollisionManager;
|
||||
import org.geysermc.geyser.network.netty.LocalSession;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
@ -257,13 +256,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
*/
|
||||
private final Map<Vector3i, ItemFrameEntity> itemFrameCache = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
/**
|
||||
* Stores a list of all lectern locations and their block entity tags.
|
||||
* See {@link WorldManager#sendLecternData(GeyserSession, int, int, int)}
|
||||
* for more information.
|
||||
*/
|
||||
private final @Nullable Set<Vector3i> lecternCache;
|
||||
|
||||
/**
|
||||
* A list of all players that have a player head on with a custom texture.
|
||||
* Our workaround for these players is to give them a custom skin and geometry to emulate wearing a custom skull.
|
||||
@ -609,13 +601,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
this.spawned = false;
|
||||
this.loggedIn = false;
|
||||
|
||||
if (geyser.getWorldManager().shouldExpectLecternHandled(this)) {
|
||||
// Unneeded on these platforms
|
||||
this.lecternCache = null;
|
||||
} else {
|
||||
this.lecternCache = new ObjectOpenHashSet<>();
|
||||
}
|
||||
|
||||
if (geyser.getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.NO_EMOTES) {
|
||||
this.emotes = new HashSet<>();
|
||||
geyser.getSessionManager().getSessions().values().forEach(player -> this.emotes.addAll(player.getEmotes()));
|
||||
|
@ -28,13 +28,18 @@ package org.geysermc.geyser.translator.inventory;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
||||
import org.geysermc.erosion.util.LecternUtils;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.inventory.*;
|
||||
import org.geysermc.geyser.inventory.Container;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.inventory.Inventory;
|
||||
import org.geysermc.geyser.inventory.LecternContainer;
|
||||
import org.geysermc.geyser.inventory.PlayerInventory;
|
||||
import org.geysermc.geyser.inventory.updater.ContainerInventoryUpdater;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.LecternBlock;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
import org.geysermc.geyser.util.InventoryUtils;
|
||||
@ -44,8 +49,6 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.WritableBook
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.WrittenBookContent;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerButtonClickPacket;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public class LecternInventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||
|
||||
/**
|
||||
@ -95,7 +98,10 @@ public class LecternInventoryTranslator extends AbstractBlockInventoryTranslator
|
||||
|
||||
// Now: Restore the lectern, if it actually exists
|
||||
if (lecternContainer.isUsingRealBlock()) {
|
||||
GeyserImpl.getInstance().getWorldManager().sendLecternData(session, position.getX(), position.getY(), position.getZ());
|
||||
boolean hasBook = session.getGeyser().getWorldManager().blockAt(session, position).getValue(Properties.HAS_BOOK, false);
|
||||
|
||||
NbtMap map = LecternBlock.getBaseLecternTag(position, hasBook);
|
||||
BlockEntityUtils.updateBlockEntity(session, map, position);
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,7 +154,8 @@ public class LecternInventoryTranslator extends AbstractBlockInventoryTranslator
|
||||
session.setDroppingLecternBook(false);
|
||||
InventoryUtils.closeInventory(session, inventory.getJavaId(), false);
|
||||
} else if (lecternContainer.getBlockEntityTag() == null) {
|
||||
Vector3i position = lecternContainer.isUsingRealBlock() ? session.getLastInteractionBlockPosition() : inventory.getHolderPosition();
|
||||
Vector3i position = lecternContainer.isUsingRealBlock() ?
|
||||
session.getLastInteractionBlockPosition() : inventory.getHolderPosition();
|
||||
|
||||
NbtMap blockEntityTag;
|
||||
if (book.getComponents() != null) {
|
||||
@ -164,7 +171,7 @@ public class LecternInventoryTranslator extends AbstractBlockInventoryTranslator
|
||||
}
|
||||
|
||||
ItemData itemData = book.getItemData(session);
|
||||
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), pages);
|
||||
NbtMapBuilder lecternTag = LecternBlock.getBaseLecternTag(position, pages);
|
||||
lecternTag.putCompound("book", NbtMap.builder()
|
||||
.putByte("Count", (byte) itemData.getCount())
|
||||
.putShort("Damage", (short) 0)
|
||||
@ -175,19 +182,7 @@ public class LecternInventoryTranslator extends AbstractBlockInventoryTranslator
|
||||
blockEntityTag = lecternTag.build();
|
||||
} else {
|
||||
// There is *a* book here, but... no NBT.
|
||||
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 1);
|
||||
NbtMapBuilder bookTag = NbtMap.builder()
|
||||
.putByte("Count", (byte) 1)
|
||||
.putShort("Damage", (short) 0)
|
||||
.putString("Name", "minecraft:writable_book")
|
||||
.putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, Collections.singletonList(
|
||||
NbtMap.builder()
|
||||
.putString("photoname", "")
|
||||
.putString("text", "")
|
||||
.build()
|
||||
)).build());
|
||||
|
||||
blockEntityTag = lecternTag.putCompound("book", bookTag.build()).build();
|
||||
blockEntityTag = LecternBlock.getBaseLecternTag(position, true);
|
||||
}
|
||||
|
||||
// Even with serverside access to lecterns, we don't easily know which lectern this is, so we need to rebuild
|
||||
|
@ -33,7 +33,6 @@ import org.geysermc.geyser.util.ChunkUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundForgetLevelChunkPacket;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
@Translator(packet = ClientboundForgetLevelChunkPacket.class)
|
||||
@ -52,17 +51,6 @@ public class JavaForgetLevelChunkTranslator extends PacketTranslator<Clientbound
|
||||
}
|
||||
removedSkulls.forEach(session.getSkullCache()::removeSkull);
|
||||
|
||||
if (!session.getGeyser().getWorldManager().shouldExpectLecternHandled(session)) {
|
||||
// Do the same thing with lecterns
|
||||
Iterator<Vector3i> iterator = session.getLecternCache().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Vector3i position = iterator.next();
|
||||
if ((position.getX() >> 4) == packet.getX() && (position.getZ() >> 4) == packet.getZ()) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ChunkUtils.sendEmptyChunk(session, packet.getX(), packet.getZ(), false);
|
||||
}
|
||||
}
|
||||
|
@ -34,14 +34,11 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NBTOutputStream;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.nbt.NbtUtils;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelChunkPacket;
|
||||
import org.geysermc.erosion.util.LecternUtils;
|
||||
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
||||
import org.geysermc.geyser.level.BedrockDimension;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.chunk.BlockStorage;
|
||||
@ -98,7 +95,6 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
|
||||
|
||||
final BlockEntityInfo[] blockEntities = packet.getBlockEntities();
|
||||
final List<NbtMap> bedrockBlockEntities = new ObjectArrayList<>(blockEntities.length);
|
||||
final List<BlockEntityInfo> lecterns = new ObjectArrayList<>();
|
||||
|
||||
BitSet waterloggedPaletteIds = new BitSet();
|
||||
BitSet bedrockOnlyBlockEntityIds = new BitSet();
|
||||
@ -318,7 +314,7 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
|
||||
session.getBlockMappings().getBedrockWater().getRuntimeId());
|
||||
|
||||
layers = new BlockStorage[]{ layer0, new BlockStorage(BitArrayVersion.V1.createArray(BlockStorage.SIZE, layer1Data), layer1Palette) };
|
||||
} else if (waterloggedPaletteIds.isEmpty() && extendedCollision) {
|
||||
} else if (waterloggedPaletteIds.isEmpty()) {
|
||||
for (int yzx = 0; yzx < BlockStorage.SIZE; yzx++) {
|
||||
int paletteId = javaData.get(yzx);
|
||||
int xzy = indexYZXtoXZY(yzx);
|
||||
@ -404,20 +400,6 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
|
||||
DataPalette section = javaChunks[(y >> 4) - yOffset];
|
||||
BlockState blockState = BlockRegistries.BLOCK_STATES.get(section.get(x, y & 0xF, z));
|
||||
|
||||
if (type == BlockEntityType.LECTERN && blockState.getValue(Properties.HAS_BOOK)) {
|
||||
// If getLecternBookStates is false, let's just treat it like a normal block entity
|
||||
// Fill in tag with a default value
|
||||
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(x + chunkBlockX, y, z + chunkBlockZ, 1);
|
||||
lecternTag.putCompound("book", NbtMap.builder()
|
||||
.putByte("Count", (byte) 1)
|
||||
.putShort("Damage", (short) 0)
|
||||
.putString("Name", "minecraft:written_book").build());
|
||||
lecternTag.putInt("page", -1);
|
||||
bedrockBlockEntities.add(lecternTag.build());
|
||||
lecterns.add(blockEntity);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Note that, since 1.20.5, tags can be null, but Bedrock still needs a default tag to render the item
|
||||
// Also, some properties - like banner base colors - are part of the tag and is processed here.
|
||||
BlockEntityTranslator blockEntityTranslator = BlockEntityUtils.getBlockEntityTranslator(type);
|
||||
@ -525,10 +507,6 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
|
||||
levelChunkPacket.setDimension(DimensionUtils.javaToBedrock(session.getChunkCache().getBedrockDimension()));
|
||||
session.sendUpstreamPacket(levelChunkPacket);
|
||||
|
||||
if (!lecterns.isEmpty()) {
|
||||
session.getGeyser().getWorldManager().sendLecternData(session, packet.getX(), packet.getZ(), lecterns);
|
||||
}
|
||||
|
||||
for (Map.Entry<Vector3i, ItemFrameEntity> entry : session.getItemFrameCache().entrySet()) {
|
||||
Vector3i position = entry.getKey();
|
||||
if ((position.getX() >> 4) == packet.getX() && (position.getZ() >> 4) == packet.getZ()) {
|
||||
|
@ -65,9 +65,6 @@ public class DimensionUtils {
|
||||
session.getChunkCache().clear();
|
||||
session.getEntityCache().removeAllEntities();
|
||||
session.getItemFrameCache().clear();
|
||||
if (session.getLecternCache() != null) {
|
||||
session.getLecternCache().clear();
|
||||
}
|
||||
session.getLodestoneCache().clear();
|
||||
session.getPistonCache().clear();
|
||||
session.getSkullCache().clear();
|
||||
|
@ -479,7 +479,6 @@ public class InventoryUtils {
|
||||
for (int col = firstCol; col < width + firstCol; col++) {
|
||||
GeyserItemStack geyserItemStack = inventoryGetter.apply(col + (row * gridDimensions) + 1);
|
||||
if (geyserItemStack.isEmpty()) {
|
||||
//noinspection ConstantValue
|
||||
inventoryHasItem = itemStack == null || itemStack.getId() == 0;
|
||||
if (inventoryHasItem) {
|
||||
break crafting;
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren