mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Yeet lectern cache (#4695)
* attempt to yeet lectern cache * yeet lecternutils usage * properly update lecterns * yeet accidental diff
This commit is contained in:
parent
675faf6bb4
commit
63c84bc25b
13 changed files with 108 additions and 392 deletions
|
@ -25,11 +25,6 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.mod.world;
|
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.gson.GsonComponentSerializer;
|
||||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||||
import net.minecraft.SharedConstants;
|
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.BannerBlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BannerPatternLayers;
|
import net.minecraft.world.level.block.entity.BannerPatternLayers;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
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.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
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.level.GeyserWorldManager;
|
||||||
import org.geysermc.geyser.network.GameProtocol;
|
import org.geysermc.geyser.network.GameProtocol;
|
||||||
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
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.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
@ -121,94 +111,6 @@ public class GeyserModWorldManager extends GeyserWorldManager {
|
||||||
return SharedConstants.getCurrentVersion().getProtocolVersion() == GameProtocol.getJavaProtocolVersion();
|
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
|
@Override
|
||||||
public boolean hasPermission(GeyserSession session, String permission) {
|
public boolean hasPermission(GeyserSession session, String permission) {
|
||||||
ServerPlayer player = getPlayer(session);
|
ServerPlayer player = getPlayer(session);
|
||||||
|
|
|
@ -27,16 +27,12 @@ package org.geysermc.geyser.platform.spigot.world.manager;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Chunk;
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
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.PickBlockUtils;
|
||||||
import org.geysermc.erosion.bukkit.SchedulerUtils;
|
import org.geysermc.erosion.bukkit.SchedulerUtils;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
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.level.WorldManager;
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
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.entity.player.GameMode;
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
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.Objects;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
@ -58,11 +51,9 @@ import java.util.concurrent.CompletableFuture;
|
||||||
*/
|
*/
|
||||||
public class GeyserSpigotWorldManager extends WorldManager {
|
public class GeyserSpigotWorldManager extends WorldManager {
|
||||||
private final Plugin plugin;
|
private final Plugin plugin;
|
||||||
private final BukkitLecterns lecterns;
|
|
||||||
|
|
||||||
public GeyserSpigotWorldManager(Plugin plugin) {
|
public GeyserSpigotWorldManager(Plugin plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.lecterns = new BukkitLecterns(plugin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -95,69 +86,6 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
||||||
return true;
|
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) {
|
public boolean getGameRuleBool(GeyserSession session, GameRule gameRule) {
|
||||||
org.bukkit.GameRule<?> bukkitGameRule = org.bukkit.GameRule.getByName(gameRule.getJavaID());
|
org.bukkit.GameRule<?> bukkitGameRule = org.bukkit.GameRule.getByName(gameRule.getJavaID());
|
||||||
if (bukkitGameRule == null) {
|
if (bukkitGameRule == null) {
|
||||||
|
|
|
@ -28,22 +28,17 @@ package org.geysermc.geyser.level;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
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.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.cloudburstmc.math.vector.Vector3i;
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
import org.cloudburstmc.nbt.NbtMap;
|
import org.geysermc.erosion.packet.backendbound.BackendboundBatchBlockRequestPacket;
|
||||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
import org.geysermc.erosion.packet.backendbound.BackendboundBlockRequestPacket;
|
||||||
import org.geysermc.erosion.packet.backendbound.*;
|
import org.geysermc.erosion.packet.backendbound.BackendboundPickBlockPacket;
|
||||||
import org.geysermc.erosion.util.BlockPositionIterator;
|
import org.geysermc.erosion.util.BlockPositionIterator;
|
||||||
import org.geysermc.erosion.util.LecternUtils;
|
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
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.entity.player.GameMode;
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
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;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class GeyserWorldManager extends WorldManager {
|
public class GeyserWorldManager extends WorldManager {
|
||||||
|
@ -92,51 +87,6 @@ public class GeyserWorldManager extends WorldManager {
|
||||||
return false;
|
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
|
@Override
|
||||||
public void setGameRule(GeyserSession session, String name, Object value) {
|
public void setGameRule(GeyserSession session, String name, Object value) {
|
||||||
super.setGameRule(session, name, 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.DataComponentType;
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
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.item.component.ItemCodecHelper;
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityInfo;
|
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.setting.Difficulty;
|
import org.geysermc.mcprotocollib.protocol.data.game.setting.Difficulty;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
@ -118,40 +116,6 @@ public abstract class WorldManager {
|
||||||
*/
|
*/
|
||||||
public abstract boolean hasOwnChunkCache();
|
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
|
* Updates a gamerule value on the Java server
|
||||||
*
|
*
|
||||||
|
|
|
@ -91,39 +91,35 @@ public class Block {
|
||||||
BlockDefinition definition = session.getBlockMappings().getBedrockBlock(state);
|
BlockDefinition definition = session.getBlockMappings().getBedrockBlock(state);
|
||||||
sendBlockUpdatePacket(session, state, definition, position);
|
sendBlockUpdatePacket(session, state, definition, position);
|
||||||
|
|
||||||
{
|
// Extended collision boxes for custom blocks
|
||||||
// Extended collision boxes for custom blocks
|
if (!session.getBlockMappings().getExtendedCollisionBoxes().isEmpty()) {
|
||||||
if (!session.getBlockMappings().getExtendedCollisionBoxes().isEmpty()) {
|
int aboveBlock = session.getGeyser().getWorldManager().getBlockAt(session, position.getX(), position.getY() + 1, position.getZ());
|
||||||
int aboveBlock = session.getGeyser().getWorldManager().getBlockAt(session, position.getX(), position.getY() + 1, position.getZ());
|
BlockDefinition aboveBedrockExtendedCollisionDefinition = session.getBlockMappings().getExtendedCollisionBoxes().get(state.javaId());
|
||||||
BlockDefinition aboveBedrockExtendedCollisionDefinition = session.getBlockMappings().getExtendedCollisionBoxes().get(state.javaId());
|
int belowBlock = session.getGeyser().getWorldManager().getBlockAt(session, position.getX(), position.getY() - 1, position.getZ());
|
||||||
int belowBlock = session.getGeyser().getWorldManager().getBlockAt(session, position.getX(), position.getY() - 1, position.getZ());
|
BlockDefinition belowBedrockExtendedCollisionDefinition = session.getBlockMappings().getExtendedCollisionBoxes().get(belowBlock);
|
||||||
BlockDefinition belowBedrockExtendedCollisionDefinition = session.getBlockMappings().getExtendedCollisionBoxes().get(belowBlock);
|
if (belowBedrockExtendedCollisionDefinition != null && state.is(Blocks.AIR)) {
|
||||||
if (belowBedrockExtendedCollisionDefinition != null && state.is(Blocks.AIR)) {
|
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
updateBlockPacket.setDataLayer(0);
|
||||||
updateBlockPacket.setDataLayer(0);
|
updateBlockPacket.setBlockPosition(position);
|
||||||
updateBlockPacket.setBlockPosition(position);
|
updateBlockPacket.setDefinition(belowBedrockExtendedCollisionDefinition);
|
||||||
updateBlockPacket.setDefinition(belowBedrockExtendedCollisionDefinition);
|
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
||||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
session.sendUpstreamPacket(updateBlockPacket);
|
||||||
session.sendUpstreamPacket(updateBlockPacket);
|
} else if (aboveBedrockExtendedCollisionDefinition != null && aboveBlock == Block.JAVA_AIR_ID) {
|
||||||
} else if (aboveBedrockExtendedCollisionDefinition != null && aboveBlock == Block.JAVA_AIR_ID) {
|
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
updateBlockPacket.setDataLayer(0);
|
||||||
updateBlockPacket.setDataLayer(0);
|
updateBlockPacket.setBlockPosition(position.add(0, 1, 0));
|
||||||
updateBlockPacket.setBlockPosition(position.add(0, 1, 0));
|
updateBlockPacket.setDefinition(aboveBedrockExtendedCollisionDefinition);
|
||||||
updateBlockPacket.setDefinition(aboveBedrockExtendedCollisionDefinition);
|
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
||||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
session.sendUpstreamPacket(updateBlockPacket);
|
||||||
session.sendUpstreamPacket(updateBlockPacket);
|
} else if (aboveBlock == Block.JAVA_AIR_ID) {
|
||||||
} else if (aboveBlock == Block.JAVA_AIR_ID) {
|
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
updateBlockPacket.setDataLayer(0);
|
||||||
updateBlockPacket.setDataLayer(0);
|
updateBlockPacket.setBlockPosition(position.add(0, 1, 0));
|
||||||
updateBlockPacket.setBlockPosition(position.add(0, 1, 0));
|
updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockAir());
|
||||||
updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockAir());
|
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
||||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
session.sendUpstreamPacket(updateBlockPacket);
|
||||||
session.sendUpstreamPacket(updateBlockPacket);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLecternBlockUpdate(session, state, position);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void sendBlockUpdatePacket(GeyserSession session, BlockState state, BlockDefinition definition, Vector3i position) {
|
protected void sendBlockUpdatePacket(GeyserSession session, BlockState state, BlockDefinition definition, Vector3i position) {
|
||||||
|
@ -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() {
|
public Item asItem() {
|
||||||
if (this.item == null) {
|
if (this.item == null) {
|
||||||
return this.item = Item.byBlock(this);
|
return this.item = Item.byBlock(this);
|
||||||
|
|
|
@ -56,6 +56,15 @@ public final class BlockState {
|
||||||
return (T) get(property);
|
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) {
|
public boolean getValue(Property<Boolean> property, boolean def) {
|
||||||
var value = get(property);
|
var value = get(property);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
|
|
|
@ -27,35 +27,67 @@ package org.geysermc.geyser.level.block.type;
|
||||||
|
|
||||||
import org.cloudburstmc.math.vector.Vector3i;
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
import org.cloudburstmc.nbt.NbtMap;
|
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.WorldManager;
|
||||||
import org.geysermc.geyser.level.block.property.Properties;
|
import org.geysermc.geyser.level.block.property.Properties;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
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;
|
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) {
|
public LecternBlock(String javaIdentifier, Builder builder) {
|
||||||
super(javaIdentifier, builder);
|
super(javaIdentifier, builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void handleLecternBlockUpdate(GeyserSession session, BlockState state, Vector3i position) {
|
public NbtMap createTag(GeyserSession session, Vector3i position, BlockState blockState) {
|
||||||
WorldManager worldManager = session.getGeyser().getWorldManager();
|
return getBaseLecternTag(position, blockState.getValue(Properties.HAS_BOOK));
|
||||||
if (worldManager.shouldExpectLecternHandled(session)) {
|
}
|
||||||
worldManager.sendLecternData(session, position.getX(), position.getY(), position.getZ());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateBlock(GeyserSession session, BlockState state, Vector3i position) {
|
||||||
|
WorldManager worldManager = session.getGeyser().getWorldManager();
|
||||||
boolean currentHasBook = state.getValue(Properties.HAS_BOOK);
|
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
|
Boolean previousHasBook = worldManager.blockAt(session, position).getValueNullable(Properties.HAS_BOOK); // Can be null if not a lectern, watch out
|
||||||
if (currentHasBook != previousHasBook) {
|
if (previousHasBook == null || currentHasBook != previousHasBook) {
|
||||||
if (currentHasBook) {
|
BlockEntityUtils.updateBlockEntity(session, getBaseLecternTag(position, currentHasBook), position);
|
||||||
worldManager.sendLecternData(session, position.getX(), position.getY(), position.getZ());
|
}
|
||||||
} else {
|
super.updateBlock(session, state, position);
|
||||||
session.getLecternCache().remove(position);
|
}
|
||||||
NbtMap newLecternTag = LecternUtils.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 0).build();
|
|
||||||
BlockEntityUtils.updateBlockEntity(session, newLecternTag, 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 {
|
||||||
|
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.Items;
|
||||||
import org.geysermc.geyser.item.type.BlockItem;
|
import org.geysermc.geyser.item.type.BlockItem;
|
||||||
import org.geysermc.geyser.level.JavaDimension;
|
import org.geysermc.geyser.level.JavaDimension;
|
||||||
import org.geysermc.geyser.level.WorldManager;
|
|
||||||
import org.geysermc.geyser.level.physics.CollisionManager;
|
import org.geysermc.geyser.level.physics.CollisionManager;
|
||||||
import org.geysermc.geyser.network.netty.LocalSession;
|
import org.geysermc.geyser.network.netty.LocalSession;
|
||||||
import org.geysermc.geyser.registry.Registries;
|
import org.geysermc.geyser.registry.Registries;
|
||||||
|
@ -257,13 +256,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||||
*/
|
*/
|
||||||
private final Map<Vector3i, ItemFrameEntity> itemFrameCache = new Object2ObjectOpenHashMap<>();
|
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.
|
* 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.
|
* 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.spawned = false;
|
||||||
this.loggedIn = 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) {
|
if (geyser.getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.NO_EMOTES) {
|
||||||
this.emotes = new HashSet<>();
|
this.emotes = new HashSet<>();
|
||||||
geyser.getSessionManager().getSessions().values().forEach(player -> this.emotes.addAll(player.getEmotes()));
|
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.math.vector.Vector3i;
|
||||||
import org.cloudburstmc.nbt.NbtMap;
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||||
import org.cloudburstmc.nbt.NbtType;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
||||||
import org.geysermc.erosion.util.LecternUtils;
|
import org.geysermc.erosion.util.LecternUtils;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
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.inventory.updater.ContainerInventoryUpdater;
|
||||||
import org.geysermc.geyser.level.block.Blocks;
|
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.session.GeyserSession;
|
||||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||||
import org.geysermc.geyser.util.InventoryUtils;
|
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.data.game.item.component.WrittenBookContent;
|
||||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerButtonClickPacket;
|
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerButtonClickPacket;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
public class LecternInventoryTranslator extends AbstractBlockInventoryTranslator {
|
public class LecternInventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -95,7 +98,10 @@ public class LecternInventoryTranslator extends AbstractBlockInventoryTranslator
|
||||||
|
|
||||||
// Now: Restore the lectern, if it actually exists
|
// Now: Restore the lectern, if it actually exists
|
||||||
if (lecternContainer.isUsingRealBlock()) {
|
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);
|
session.setDroppingLecternBook(false);
|
||||||
InventoryUtils.closeInventory(session, inventory.getJavaId(), false);
|
InventoryUtils.closeInventory(session, inventory.getJavaId(), false);
|
||||||
} else if (lecternContainer.getBlockEntityTag() == null) {
|
} else if (lecternContainer.getBlockEntityTag() == null) {
|
||||||
Vector3i position = lecternContainer.isUsingRealBlock() ? session.getLastInteractionBlockPosition() : inventory.getHolderPosition();
|
Vector3i position = lecternContainer.isUsingRealBlock() ?
|
||||||
|
session.getLastInteractionBlockPosition() : inventory.getHolderPosition();
|
||||||
|
|
||||||
NbtMap blockEntityTag;
|
NbtMap blockEntityTag;
|
||||||
if (book.getComponents() != null) {
|
if (book.getComponents() != null) {
|
||||||
|
@ -164,7 +171,7 @@ public class LecternInventoryTranslator extends AbstractBlockInventoryTranslator
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemData itemData = book.getItemData(session);
|
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()
|
lecternTag.putCompound("book", NbtMap.builder()
|
||||||
.putByte("Count", (byte) itemData.getCount())
|
.putByte("Count", (byte) itemData.getCount())
|
||||||
.putShort("Damage", (short) 0)
|
.putShort("Damage", (short) 0)
|
||||||
|
@ -175,19 +182,7 @@ public class LecternInventoryTranslator extends AbstractBlockInventoryTranslator
|
||||||
blockEntityTag = lecternTag.build();
|
blockEntityTag = lecternTag.build();
|
||||||
} else {
|
} else {
|
||||||
// There is *a* book here, but... no NBT.
|
// There is *a* book here, but... no NBT.
|
||||||
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 1);
|
blockEntityTag = LecternBlock.getBaseLecternTag(position, true);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Even with serverside access to lecterns, we don't easily know which lectern this is, so we need to rebuild
|
// 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 org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundForgetLevelChunkPacket;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Translator(packet = ClientboundForgetLevelChunkPacket.class)
|
@Translator(packet = ClientboundForgetLevelChunkPacket.class)
|
||||||
|
@ -52,17 +51,6 @@ public class JavaForgetLevelChunkTranslator extends PacketTranslator<Clientbound
|
||||||
}
|
}
|
||||||
removedSkulls.forEach(session.getSkullCache()::removeSkull);
|
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);
|
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.math.vector.Vector3i;
|
||||||
import org.cloudburstmc.nbt.NBTOutputStream;
|
import org.cloudburstmc.nbt.NBTOutputStream;
|
||||||
import org.cloudburstmc.nbt.NbtMap;
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
|
||||||
import org.cloudburstmc.nbt.NbtUtils;
|
import org.cloudburstmc.nbt.NbtUtils;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.LevelChunkPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.LevelChunkPacket;
|
||||||
import org.geysermc.erosion.util.LecternUtils;
|
|
||||||
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
||||||
import org.geysermc.geyser.level.BedrockDimension;
|
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.Block;
|
||||||
import org.geysermc.geyser.level.block.type.BlockState;
|
import org.geysermc.geyser.level.block.type.BlockState;
|
||||||
import org.geysermc.geyser.level.chunk.BlockStorage;
|
import org.geysermc.geyser.level.chunk.BlockStorage;
|
||||||
|
@ -98,7 +95,6 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
|
||||||
|
|
||||||
final BlockEntityInfo[] blockEntities = packet.getBlockEntities();
|
final BlockEntityInfo[] blockEntities = packet.getBlockEntities();
|
||||||
final List<NbtMap> bedrockBlockEntities = new ObjectArrayList<>(blockEntities.length);
|
final List<NbtMap> bedrockBlockEntities = new ObjectArrayList<>(blockEntities.length);
|
||||||
final List<BlockEntityInfo> lecterns = new ObjectArrayList<>();
|
|
||||||
|
|
||||||
BitSet waterloggedPaletteIds = new BitSet();
|
BitSet waterloggedPaletteIds = new BitSet();
|
||||||
BitSet bedrockOnlyBlockEntityIds = new BitSet();
|
BitSet bedrockOnlyBlockEntityIds = new BitSet();
|
||||||
|
@ -318,7 +314,7 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
|
||||||
session.getBlockMappings().getBedrockWater().getRuntimeId());
|
session.getBlockMappings().getBedrockWater().getRuntimeId());
|
||||||
|
|
||||||
layers = new BlockStorage[]{ layer0, new BlockStorage(BitArrayVersion.V1.createArray(BlockStorage.SIZE, layer1Data), layer1Palette) };
|
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++) {
|
for (int yzx = 0; yzx < BlockStorage.SIZE; yzx++) {
|
||||||
int paletteId = javaData.get(yzx);
|
int paletteId = javaData.get(yzx);
|
||||||
int xzy = indexYZXtoXZY(yzx);
|
int xzy = indexYZXtoXZY(yzx);
|
||||||
|
@ -404,20 +400,6 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
|
||||||
DataPalette section = javaChunks[(y >> 4) - yOffset];
|
DataPalette section = javaChunks[(y >> 4) - yOffset];
|
||||||
BlockState blockState = BlockRegistries.BLOCK_STATES.get(section.get(x, y & 0xF, z));
|
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
|
// 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.
|
// Also, some properties - like banner base colors - are part of the tag and is processed here.
|
||||||
BlockEntityTranslator blockEntityTranslator = BlockEntityUtils.getBlockEntityTranslator(type);
|
BlockEntityTranslator blockEntityTranslator = BlockEntityUtils.getBlockEntityTranslator(type);
|
||||||
|
@ -525,10 +507,6 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
|
||||||
levelChunkPacket.setDimension(DimensionUtils.javaToBedrock(session.getChunkCache().getBedrockDimension()));
|
levelChunkPacket.setDimension(DimensionUtils.javaToBedrock(session.getChunkCache().getBedrockDimension()));
|
||||||
session.sendUpstreamPacket(levelChunkPacket);
|
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()) {
|
for (Map.Entry<Vector3i, ItemFrameEntity> entry : session.getItemFrameCache().entrySet()) {
|
||||||
Vector3i position = entry.getKey();
|
Vector3i position = entry.getKey();
|
||||||
if ((position.getX() >> 4) == packet.getX() && (position.getZ() >> 4) == packet.getZ()) {
|
if ((position.getX() >> 4) == packet.getX() && (position.getZ() >> 4) == packet.getZ()) {
|
||||||
|
|
|
@ -65,9 +65,6 @@ public class DimensionUtils {
|
||||||
session.getChunkCache().clear();
|
session.getChunkCache().clear();
|
||||||
session.getEntityCache().removeAllEntities();
|
session.getEntityCache().removeAllEntities();
|
||||||
session.getItemFrameCache().clear();
|
session.getItemFrameCache().clear();
|
||||||
if (session.getLecternCache() != null) {
|
|
||||||
session.getLecternCache().clear();
|
|
||||||
}
|
|
||||||
session.getLodestoneCache().clear();
|
session.getLodestoneCache().clear();
|
||||||
session.getPistonCache().clear();
|
session.getPistonCache().clear();
|
||||||
session.getSkullCache().clear();
|
session.getSkullCache().clear();
|
||||||
|
|
|
@ -479,7 +479,6 @@ public class InventoryUtils {
|
||||||
for (int col = firstCol; col < width + firstCol; col++) {
|
for (int col = firstCol; col < width + firstCol; col++) {
|
||||||
GeyserItemStack geyserItemStack = inventoryGetter.apply(col + (row * gridDimensions) + 1);
|
GeyserItemStack geyserItemStack = inventoryGetter.apply(col + (row * gridDimensions) + 1);
|
||||||
if (geyserItemStack.isEmpty()) {
|
if (geyserItemStack.isEmpty()) {
|
||||||
//noinspection ConstantValue
|
|
||||||
inventoryHasItem = itemStack == null || itemStack.getId() == 0;
|
inventoryHasItem = itemStack == null || itemStack.getId() == 0;
|
||||||
if (inventoryHasItem) {
|
if (inventoryHasItem) {
|
||||||
break crafting;
|
break crafting;
|
||||||
|
|
Loading…
Reference in a new issue