mirror of https://github.com/GeyserMC/Geyser.git
ported camotoy's attempt of implementing structure blocks, removal of a few TODO's
This commit is contained in:
parent
867cf6da05
commit
eabed1f0b0
|
@ -32,7 +32,6 @@ Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Ge
|
|||
## What's Left to be Added/Fixed
|
||||
- Near-perfect movement (to the point where anticheat on large servers is unlikely to ban you)
|
||||
- Some Entity Flags
|
||||
- Structure block UI
|
||||
|
||||
## What can't be fixed
|
||||
There are a few things Geyser is unable to support due to various differences between Minecraft Bedrock and Java. For a list of these limitations, see the [Current Limitations](https://wiki.geysermc.org/geyser/current-limitations/) page.
|
||||
|
|
|
@ -27,8 +27,12 @@ package org.geysermc.geyser.platform.mod.world;
|
|||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
||||
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.nbt.ByteArrayTag;
|
||||
import net.minecraft.nbt.ByteTag;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
@ -40,10 +44,12 @@ import net.minecraft.nbt.IntTag;
|
|||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.LongArrayTag;
|
||||
import net.minecraft.nbt.LongTag;
|
||||
import net.minecraft.nbt.NbtUtils;
|
||||
import net.minecraft.nbt.ShortTag;
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.nbt.TagVisitor;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
@ -54,15 +60,23 @@ import net.minecraft.world.level.block.Block;
|
|||
import net.minecraft.world.level.block.entity.BannerBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.LecternBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.StructureBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtList;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.structure.StructureTemplateResponseType;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.StructureTemplateDataRequestPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.StructureTemplateDataResponsePacket;
|
||||
import org.geysermc.erosion.util.LecternUtils;
|
||||
import org.geysermc.geyser.level.GeyserWorldManager;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
|
@ -73,6 +87,7 @@ import org.geysermc.geyser.util.BlockEntityUtils;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class GeyserModWorldManager extends GeyserWorldManager {
|
||||
|
@ -212,6 +227,131 @@ public class GeyserModWorldManager extends GeyserWorldManager {
|
|||
NbtMap blockEntityTag = lecternTag.build();
|
||||
BlockEntityUtils.updateBlockEntity(session, blockEntityTag, Vector3i.from(x, y, z));
|
||||
}
|
||||
@Override
|
||||
public void handleStructureDataRequest(StructureTemplateDataRequestPacket packet, GeyserSession session) {
|
||||
server.execute(() -> {
|
||||
ResourceLocation location = ResourceLocation.tryParse(packet.getName());
|
||||
if (location == null) {
|
||||
sendNoStructureFound(session);
|
||||
return;
|
||||
}
|
||||
|
||||
Optional<StructureTemplate> structure = server.getStructureManager().get(location);
|
||||
if (structure.isEmpty()) {
|
||||
sendNoStructureFound(session);
|
||||
return;
|
||||
}
|
||||
|
||||
// Required to find structure block
|
||||
ServerPlayer player = this.getPlayer(session);
|
||||
if (player == null) {
|
||||
sendNoStructureFound(session);
|
||||
return;
|
||||
}
|
||||
BlockEntity blockEntity = player.level().getBlockEntity(new BlockPos(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ()));
|
||||
if (!(blockEntity instanceof StructureBlockEntity)) {
|
||||
sendNoStructureFound(session);
|
||||
return;
|
||||
}
|
||||
|
||||
StructureTemplate structureTemplate = structure.get();
|
||||
// Easiest to use a tag, I guess
|
||||
CompoundTag tag = structure.get().save(new CompoundTag());
|
||||
int sizeX = structureTemplate.getSize().getX();
|
||||
int sizeY = structureTemplate.getSize().getY();
|
||||
int sizeZ = structureTemplate.getSize().getZ();
|
||||
|
||||
NbtMapBuilder builder = NbtMap.builder();
|
||||
builder.putInt("format_version", 1);
|
||||
builder.putList("structure_world_origin", NbtType.INT, packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ()); // ???
|
||||
builder.putList("size", NbtType.INT, sizeX, sizeY, sizeZ);
|
||||
|
||||
IntList layerZero = new IntArrayList();
|
||||
//layerZero.add(0); // ??
|
||||
IntList layerOne = new IntArrayList();
|
||||
//layerOne.add(-1); // palette header???
|
||||
List<NbtMap> bedrockPalette = new ObjectArrayList<>();
|
||||
|
||||
ListTag javaPalette = tag.getList("palette", 10);
|
||||
ListTag blocks = tag.getList("blocks", 10);
|
||||
for (int y = 0; y < sizeY; y++) {
|
||||
for (int x = 0; x < sizeX; x++) {
|
||||
for (int z = 0; z < sizeZ; z++) {
|
||||
boolean hasAppliedBlock = false;
|
||||
for (int i = 0; i < blocks.size(); i++) {
|
||||
CompoundTag currentBlock = blocks.getCompound(i);
|
||||
ListTag pos = currentBlock.getList("pos", 6);
|
||||
if (pos.getInt(0) != x && pos.getInt(1) != y && pos.getInt(2) != z) {
|
||||
// Position doesn't match
|
||||
continue;
|
||||
}
|
||||
// Lookup the table of which Java block this is mapped to
|
||||
CompoundTag javaBlock = javaPalette.getCompound(currentBlock.getInt("state"));
|
||||
BlockState blockState = NbtUtils.readBlockState(BuiltInRegistries.BLOCK.asLookup(), javaBlock);
|
||||
int javaId = Block.getId(blockState);
|
||||
int bedrockIndex = getOrAddToBedrockPalette(session, javaId, bedrockPalette);
|
||||
layerZero.add(bedrockIndex);
|
||||
|
||||
var optional = blockState.getOptionalValue(BlockStateProperties.WATERLOGGED);
|
||||
if (optional.isPresent() && optional.get()) {
|
||||
layerOne.add(1);
|
||||
} else {
|
||||
layerOne.add(0);
|
||||
}
|
||||
|
||||
blocks.remove(i); // Already used
|
||||
hasAppliedBlock = true;
|
||||
break;
|
||||
}
|
||||
if (!hasAppliedBlock) {
|
||||
// Not found - air?
|
||||
layerZero.add(getOrAddToBedrockPalette(session,0, bedrockPalette));
|
||||
layerOne.add(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NbtMapBuilder structureBuilder = NbtMap.builder();
|
||||
structureBuilder.putList("block_indices", NbtType.LIST, new NbtList<>(NbtType.INT, layerZero), new NbtList<>(NbtType.INT, layerOne));
|
||||
|
||||
structureBuilder.putList("entities", NbtType.COMPOUND);
|
||||
|
||||
structureBuilder.putCompound("palette", NbtMap.builder().putCompound("default",
|
||||
NbtMap.builder().putList("block_palette", NbtType.COMPOUND, bedrockPalette)
|
||||
.putCompound("block_position_data", NbtMap.EMPTY).build()).build());
|
||||
|
||||
builder.putCompound("structure", structureBuilder.build());
|
||||
|
||||
System.out.println(builder);
|
||||
|
||||
StructureTemplateDataResponsePacket responsePacket = new StructureTemplateDataResponsePacket();
|
||||
responsePacket.setName(packet.getName());
|
||||
responsePacket.setSave(true);
|
||||
responsePacket.setTag(builder.build());
|
||||
responsePacket.setType(StructureTemplateResponseType.QUERY);
|
||||
session.sendUpstreamPacket(responsePacket);
|
||||
});
|
||||
}
|
||||
protected int getOrAddToBedrockPalette(GeyserSession session, int javaState, List<NbtMap> bedrockPalette) {
|
||||
NbtMap tag = session.getBlockMappings().getBedrockBlock(javaState).getState();
|
||||
|
||||
for (int i = 0; i < bedrockPalette.size(); i++) {
|
||||
if (tag == bedrockPalette.get(i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
bedrockPalette.add(tag);
|
||||
return bedrockPalette.size() - 1;
|
||||
}
|
||||
|
||||
protected void sendNoStructureFound(GeyserSession session) {
|
||||
StructureTemplateDataResponsePacket packet = new StructureTemplateDataResponsePacket();
|
||||
packet.setName("");
|
||||
packet.setSave(false);
|
||||
packet.setType(StructureTemplateResponseType.NONE);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(GeyserSession session, String permission) {
|
||||
|
|
|
@ -36,7 +36,13 @@ 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.NbtList;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.structure.StructureTemplateResponseType;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.StructureTemplateDataRequestPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.StructureTemplateDataResponsePacket;
|
||||
import org.geysermc.erosion.bukkit.BukkitLecterns;
|
||||
import org.geysermc.erosion.bukkit.BukkitUtils;
|
||||
import org.geysermc.erosion.bukkit.PickBlockUtils;
|
||||
|
@ -59,6 +65,19 @@ import java.util.concurrent.CompletableFuture;
|
|||
public class GeyserSpigotWorldManager extends WorldManager {
|
||||
private final Plugin plugin;
|
||||
private final BukkitLecterns lecterns;
|
||||
private static final NbtMap EMPTY_STRUCTURE_DATA;
|
||||
|
||||
static {
|
||||
NbtMapBuilder builder = NbtMap.builder();
|
||||
builder.putInt("format_version", 1);
|
||||
builder.putCompound("structure", NbtMap.builder()
|
||||
.putList("block_indices", NbtType.LIST, NbtList.EMPTY, NbtList.EMPTY)
|
||||
.putList("entities", NbtType.COMPOUND)
|
||||
.putCompound("palette", NbtMap.EMPTY)
|
||||
.build());
|
||||
builder.putList("structure_world_origin", NbtType.INT, 0, 0, 0);
|
||||
EMPTY_STRUCTURE_DATA = builder.build();
|
||||
}
|
||||
|
||||
public GeyserSpigotWorldManager(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
|
@ -130,6 +149,27 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO adapt to actually send the requests
|
||||
@Override
|
||||
public void handleStructureDataRequest(StructureTemplateDataRequestPacket packet, GeyserSession session) {
|
||||
// Always send a blank form
|
||||
sendEmptyStructureData(session, packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an empty structure data that tricks Bedrock into thinking it loaded successfully
|
||||
*/
|
||||
protected void sendEmptyStructureData(GeyserSession session, StructureTemplateDataRequestPacket packet) {
|
||||
StructureTemplateDataResponsePacket responsePacket = new StructureTemplateDataResponsePacket();
|
||||
responsePacket.setName(packet.getName());
|
||||
responsePacket.setSave(true);
|
||||
responsePacket.setTag(EMPTY_STRUCTURE_DATA.toBuilder()
|
||||
.putList("size", NbtType.INT, packet.getSettings().getSize().getX(), packet.getSettings().getSize().getY(), packet.getSettings().getSize().getZ())
|
||||
.build());
|
||||
responsePacket.setType(StructureTemplateResponseType.QUERY);
|
||||
session.sendUpstreamPacket(responsePacket);
|
||||
}
|
||||
|
||||
private @Nullable Chunk getChunk(World world, int x, int z) {
|
||||
if (!world.isChunkLoaded(x, z)) {
|
||||
return null;
|
||||
|
|
|
@ -34,9 +34,18 @@ 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.NbtList;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.geysermc.erosion.packet.backendbound.*;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.structure.StructureTemplateResponseType;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.StructureTemplateDataRequestPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.StructureTemplateDataResponsePacket;
|
||||
import org.geysermc.erosion.packet.backendbound.BackendboundBatchBlockEntityPacket;
|
||||
import org.geysermc.erosion.packet.backendbound.BackendboundBatchBlockRequestPacket;
|
||||
import org.geysermc.erosion.packet.backendbound.BackendboundBlockEntityPacket;
|
||||
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;
|
||||
|
@ -48,6 +57,20 @@ import java.util.concurrent.CompletableFuture;
|
|||
public class GeyserWorldManager extends WorldManager {
|
||||
private final Object2ObjectMap<String, String> gameruleCache = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
private static final NbtMap EMPTY_STRUCTURE_DATA;
|
||||
|
||||
static {
|
||||
NbtMapBuilder builder = NbtMap.builder();
|
||||
builder.putInt("format_version", 1);
|
||||
builder.putCompound("structure", NbtMap.builder()
|
||||
.putList("block_indices", NbtType.LIST, NbtList.EMPTY, NbtList.EMPTY)
|
||||
.putList("entities", NbtType.COMPOUND)
|
||||
.putCompound("palette", NbtMap.EMPTY)
|
||||
.build());
|
||||
builder.putList("structure_world_origin", NbtType.INT, 0, 0, 0);
|
||||
EMPTY_STRUCTURE_DATA = builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
||||
var erosionHandler = session.getErosionHandler().getAsActive();
|
||||
|
@ -131,6 +154,26 @@ public class GeyserWorldManager extends WorldManager {
|
|||
BlockEntityUtils.updateBlockEntity(session, lecternTag.build(), Vector3i.from(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleStructureDataRequest(StructureTemplateDataRequestPacket packet, GeyserSession session) {
|
||||
// Always send a blank form
|
||||
sendEmptyStructureData(session, packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an empty structure data that tricks Bedrock into thinking it loaded successfully
|
||||
*/
|
||||
protected void sendEmptyStructureData(GeyserSession session, StructureTemplateDataRequestPacket packet) {
|
||||
StructureTemplateDataResponsePacket responsePacket = new StructureTemplateDataResponsePacket();
|
||||
responsePacket.setName(packet.getName());
|
||||
responsePacket.setSave(true);
|
||||
responsePacket.setTag(EMPTY_STRUCTURE_DATA.toBuilder()
|
||||
.putList("size", NbtType.INT, packet.getSettings().getSize().getX(), packet.getSettings().getSize().getY(), packet.getSettings().getSize().getZ())
|
||||
.build());
|
||||
responsePacket.setType(StructureTemplateResponseType.QUERY);
|
||||
session.sendUpstreamPacket(responsePacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExpectLecternHandled(GeyserSession session) {
|
||||
return session.getErosionHandler().isActive();
|
||||
|
|
|
@ -32,6 +32,7 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
|||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.StructureTemplateDataRequestPacket;
|
||||
import org.geysermc.erosion.util.BlockPositionIterator;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
|
@ -126,6 +127,14 @@ public abstract class WorldManager {
|
|||
*/
|
||||
public abstract void sendLecternData(GeyserSession session, int x, int z, List<BlockEntityInfo> blockEntityInfos);
|
||||
|
||||
/**
|
||||
* Handle Bedrock requesting data for a structure.
|
||||
*
|
||||
* @param session the session of the player requested structure information
|
||||
* @param packet the packet the Bedrock client has sent
|
||||
*/
|
||||
public abstract void handleStructureDataRequest(StructureTemplateDataRequestPacket packet, GeyserSession session);
|
||||
|
||||
/**
|
||||
* @return whether we should expect lectern data to update, or if we have to fall back on a workaround.
|
||||
*/
|
||||
|
|
|
@ -144,7 +144,7 @@ public final class BlockRegistryPopulator {
|
|||
builder.remove("version"); // Remove all nbt tags which are not needed for differentiating states
|
||||
builder.remove("name_hash"); // Quick workaround - was added in 1.19.20
|
||||
builder.remove("network_id"); // Added in 1.19.80 - ????
|
||||
builder.remove("block_id"); // Added in 1.20.60 //TODO verify this can be just removed
|
||||
builder.remove("block_id"); // Added in 1.20.60
|
||||
//noinspection UnstableApiUsage
|
||||
builder.putCompound("states", statesInterner.intern((NbtMap) builder.remove("states")));
|
||||
vanillaBlockStates.set(i, builder.build());
|
||||
|
@ -229,6 +229,7 @@ public final class BlockRegistryPopulator {
|
|||
Map<NbtMap, BlockDefinition> itemFrames = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
Set<BlockDefinition> jigsawDefinitions = new ObjectOpenHashSet<>();
|
||||
Set<BlockDefinition> structureBlockDefinitions = new ObjectOpenHashSet<>();
|
||||
|
||||
BlockMappings.BlockMappingsBuilder builder = BlockMappings.builder();
|
||||
while (blocksIterator.hasNext()) {
|
||||
|
@ -272,6 +273,10 @@ public final class BlockRegistryPopulator {
|
|||
jigsawDefinitions.add(bedrockDefinition);
|
||||
}
|
||||
|
||||
if (javaId.contains("structure_block")) {
|
||||
structureBlockDefinitions.add(bedrockDefinition);
|
||||
}
|
||||
|
||||
boolean waterlogged = entry.getKey().contains("waterlogged=true")
|
||||
|| javaId.contains("minecraft:bubble_column") || javaId.contains("minecraft:kelp") || javaId.contains("seagrass");
|
||||
|
||||
|
@ -358,6 +363,7 @@ public final class BlockRegistryPopulator {
|
|||
.itemFrames(itemFrames)
|
||||
.flowerPotBlocks(flowerPotBlocks)
|
||||
.jigsawStates(jigsawDefinitions)
|
||||
.structureBlockStates(structureBlockDefinitions)
|
||||
.remappedVanillaIds(remappedVanillaIds)
|
||||
.blockProperties(customBlockProperties)
|
||||
.customBlockStateDefinitions(customBlockStateDefinitions)
|
||||
|
|
|
@ -61,6 +61,7 @@ public class BlockMappings implements DefinitionRegistry<GeyserBedrockBlock> {
|
|||
Map<String, NbtMap> flowerPotBlocks;
|
||||
|
||||
Set<BlockDefinition> jigsawStates;
|
||||
Set<BlockDefinition> structureBlockStates;
|
||||
|
||||
List<BlockPropertyData> blockProperties;
|
||||
Object2ObjectMap<CustomBlockState, GeyserBedrockBlock> customBlockStateDefinitions;
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.translator.level.block.entity;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType;
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.protocol.bedrock.data.structure.StructureMirror;
|
||||
import org.cloudburstmc.protocol.bedrock.data.structure.StructureRotation;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
|
||||
@BlockEntity(type = BlockEntityType.STRUCTURE_BLOCK)
|
||||
public class StructureBlockBlockEntityTranslator extends BlockEntityTranslator {
|
||||
|
||||
@Override
|
||||
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
|
||||
GeyserImpl.getInstance().getLogger().info("structure block tag: " +
|
||||
tag.toString() + " " + builder.toString() + " " + blockState);
|
||||
if (tag.size() < 5) {
|
||||
return; // These values aren't here
|
||||
}
|
||||
|
||||
builder.putString("structureName", getOrDefault(tag.get("name"), ""));
|
||||
|
||||
String mode = getOrDefault(tag.get("mode"), "");
|
||||
int bedrockData = switch (mode) {
|
||||
case "LOAD" -> 2;
|
||||
case "CORNER" -> 3;
|
||||
case "DATA" -> 4;
|
||||
default -> 1; // SAVE
|
||||
};
|
||||
|
||||
builder.putInt("data", bedrockData);
|
||||
builder.putString("dataField", ""); // ??? possibly related to Java's "metadata"
|
||||
|
||||
// Mirror behaves different in Java and Bedrock - it requires modifying the position in space as well
|
||||
String mirror = getOrDefault(tag.get("mirror"), "");
|
||||
byte bedrockMirror = switch (mirror) {
|
||||
case "LEFT_RIGHT" -> 1;
|
||||
case "FRONT_BACK" -> 2;
|
||||
default -> 0; // Or NONE
|
||||
};
|
||||
builder.putByte("mirror", bedrockMirror);
|
||||
|
||||
builder.putByte("ignoreEntities", getOrDefault(tag.get("ignoreEntities"), (byte) 0));
|
||||
builder.putByte("isPowered", getOrDefault(tag.get("powered"), (byte) 0));
|
||||
builder.putLong("seed", getOrDefault(tag.get("seed"), 0L));
|
||||
builder.putByte("showBoundingBox", getOrDefault(tag.get("showboundingbox"), (byte) 0));
|
||||
|
||||
String rotation = getOrDefault(tag.get("rotation"), "");
|
||||
byte bedrockRotation = switch (rotation) {
|
||||
case "CLOCKWISE_90" -> 1;
|
||||
case "CLOCKWISE_180" -> 2;
|
||||
case "COUNTERCLOCKWISE_90" -> 3;
|
||||
default -> 0; // Or NONE keep it as 0
|
||||
};
|
||||
builder.putByte("rotation", bedrockRotation);
|
||||
|
||||
int xStructureSize = getOrDefault(tag.get("sizeX"), 0);
|
||||
int zStructureSize = getOrDefault(tag.get("sizeZ"), 0);
|
||||
int newXStructureSize = xStructureSize;
|
||||
int newZStructureSize = zStructureSize;
|
||||
|
||||
// The "positions" are also offsets on Java
|
||||
int posX = getOrDefault(tag.get("posX"), 0);
|
||||
int posZ = getOrDefault(tag.get("posZ"), 0);
|
||||
|
||||
// Modify positions if mirrored - Bedrock doesn't have this
|
||||
if (bedrockMirror == (byte) StructureMirror.Z.ordinal()) {
|
||||
posX = posX + xStructureSize;
|
||||
newXStructureSize = xStructureSize * -1;
|
||||
} else if (bedrockMirror == (byte) StructureMirror.X.ordinal()) {
|
||||
posZ = posZ + zStructureSize;
|
||||
newZStructureSize = zStructureSize * -1;
|
||||
}
|
||||
|
||||
// Bedrock rotates with the same origin; Java does not
|
||||
StructureRotation structureRotation = StructureRotation.values()[bedrockRotation];
|
||||
switch (structureRotation) {
|
||||
case ROTATE_90 -> {
|
||||
if (xStructureSize >= 0) {
|
||||
posX += 1;
|
||||
}
|
||||
if (zStructureSize < 0) {
|
||||
posZ += 1;
|
||||
}
|
||||
posX -= zStructureSize;
|
||||
}
|
||||
case ROTATE_180 -> {
|
||||
if (xStructureSize >= 0) {
|
||||
posX += 1;
|
||||
}
|
||||
if (zStructureSize >= 0) {
|
||||
posZ += 1;
|
||||
}
|
||||
posX -= xStructureSize;
|
||||
posZ -= zStructureSize;
|
||||
}
|
||||
case ROTATE_270 -> {
|
||||
if (xStructureSize < 0) {
|
||||
posX += 1;
|
||||
}
|
||||
if (zStructureSize >= 0) {
|
||||
posZ += 1;
|
||||
}
|
||||
posZ -= xStructureSize;
|
||||
}
|
||||
default -> {
|
||||
if (xStructureSize < 0) {
|
||||
posX += 1;
|
||||
}
|
||||
if (zStructureSize < 0) {
|
||||
posZ += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
builder.putInt("xStructureSize", newXStructureSize);
|
||||
builder.putInt("yStructureSize", getOrDefault(tag.get("sizeY"), 0));
|
||||
builder.putInt("zStructureSize", newZStructureSize);
|
||||
|
||||
builder.putInt("xStructureOffset", posX);
|
||||
builder.putInt("yStructureOffset", getOrDefault(tag.get("posY"), 0));
|
||||
builder.putInt("zStructureOffset", posZ);
|
||||
|
||||
builder.putFloat("integrity", getOrDefault(tag.get("integrity"), 0f)); // Is 1.0f by default on Java but 100.0f on Bedrock
|
||||
|
||||
// Java's "showair" is unrepresented
|
||||
}
|
||||
}
|
|
@ -348,6 +348,13 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||
openPacket.setType(ContainerType.JIGSAW_EDITOR);
|
||||
openPacket.setUniqueEntityId(-1);
|
||||
session.sendUpstreamPacket(openPacket);
|
||||
} else if (session.getBlockMappings().getStructureBlockStates().contains(packet.getBlockDefinition())) {
|
||||
ContainerOpenPacket openPacket = new ContainerOpenPacket();
|
||||
openPacket.setBlockPosition(packet.getBlockPosition());
|
||||
openPacket.setId((byte) 1);
|
||||
openPacket.setType(ContainerType.STRUCTURE_EDITOR);
|
||||
openPacket.setUniqueEntityId(-1);
|
||||
session.sendUpstreamPacket(openPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.translator.protocol.bedrock;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.inventory.UpdateStructureBlockAction;
|
||||
import com.github.steveice10.mc.protocol.data.game.inventory.UpdateStructureBlockMode;
|
||||
import com.github.steveice10.mc.protocol.data.game.level.block.StructureMirror;
|
||||
import com.github.steveice10.mc.protocol.data.game.level.block.StructureRotation;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundSetStructureBlockPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.data.structure.StructureBlockType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.structure.StructureEditorData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.structure.StructureSettings;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.StructureBlockUpdatePacket;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
|
||||
@Translator(packet = StructureBlockUpdatePacket.class)
|
||||
public class BedrockStructureBlockUpdateTranslator extends PacketTranslator<StructureBlockUpdatePacket> {
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, StructureBlockUpdatePacket packet) {
|
||||
StructureEditorData data = packet.getEditorData();
|
||||
StructureSettings settings = data.getSettings();
|
||||
|
||||
UpdateStructureBlockAction action = UpdateStructureBlockAction.UPDATE_DATA;
|
||||
if (packet.isPowered()) {
|
||||
if (data.getType() == StructureBlockType.LOAD) {
|
||||
action = UpdateStructureBlockAction.LOAD_STRUCTURE;
|
||||
} else if (data.getType() == StructureBlockType.SAVE) {
|
||||
action = UpdateStructureBlockAction.SAVE_STRUCTURE;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateStructureBlockMode mode = switch (data.getType()) {
|
||||
case CORNER -> UpdateStructureBlockMode.CORNER;
|
||||
case DATA -> UpdateStructureBlockMode.DATA;
|
||||
case LOAD -> UpdateStructureBlockMode.LOAD;
|
||||
default -> UpdateStructureBlockMode.SAVE;
|
||||
};
|
||||
|
||||
// Ignore mirror - Java appears to mirror on an axis, while Bedrock mirrors in place
|
||||
StructureMirror mirror = StructureMirror.NONE;
|
||||
|
||||
com.github.steveice10.mc.protocol.data.game.level.block.StructureRotation rotation = switch (settings.getRotation()) {
|
||||
case ROTATE_90 -> StructureRotation.CLOCKWISE_90;
|
||||
case ROTATE_180 -> StructureRotation.CLOCKWISE_180;
|
||||
case ROTATE_270 -> StructureRotation.COUNTERCLOCKWISE_90;
|
||||
default -> StructureRotation.NONE;
|
||||
};
|
||||
|
||||
ServerboundSetStructureBlockPacket structureBlockPacket = new ServerboundSetStructureBlockPacket(
|
||||
packet.getBlockPosition(),
|
||||
action,
|
||||
mode,
|
||||
data.getName(),
|
||||
settings.getOffset(),
|
||||
settings.getSize(),
|
||||
mirror,
|
||||
rotation,
|
||||
"",
|
||||
settings.getIntegrityValue(),
|
||||
settings.getIntegritySeed(),
|
||||
settings.isIgnoringEntities(),
|
||||
false,
|
||||
data.isBoundingBoxVisible()
|
||||
);
|
||||
|
||||
session.sendDownstreamPacket(structureBlockPacket);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.translator.protocol.bedrock;
|
||||
|
||||
import org.cloudburstmc.protocol.bedrock.packet.StructureTemplateDataRequestPacket;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
|
||||
/**
|
||||
* Packet used in Bedrock to load structures into the structure block GUI.
|
||||
* <p>
|
||||
* Java does not have this functionality.
|
||||
*/
|
||||
@Translator(packet = StructureTemplateDataRequestPacket.class)
|
||||
public class BedrockStructureTemplateDataRequestTranslator extends PacketTranslator<StructureTemplateDataRequestPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, StructureTemplateDataRequestPacket packet) {
|
||||
GeyserImpl.getInstance().getLogger().info(packet.toString());
|
||||
|
||||
session.getGeyser().getWorldManager().handleStructureDataRequest(packet, session);
|
||||
}
|
||||
}
|
|
@ -125,7 +125,7 @@ public class InventoryUtils {
|
|||
InventoryTranslator translator = session.getInventoryTranslator();
|
||||
translator.closeInventory(session, inventory);
|
||||
if (confirm && inventory.isDisplayed() && !inventory.isPending()
|
||||
&& !(translator instanceof LecternInventoryTranslator) // TODO: double-check
|
||||
&& !(translator instanceof LecternInventoryTranslator) // Closing lecterns is not followed with a close confirmation
|
||||
) {
|
||||
session.setClosingInventory(true);
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ mixin = "0.8.5"
|
|||
indra = "3.1.3"
|
||||
shadow = "7.1.3-SNAPSHOT"
|
||||
architectury-plugin = "3.4-SNAPSHOT"
|
||||
architectury-loom = "1.4-SNAPSHOT"
|
||||
architectury-loom = "1.5-SNAPSHOT"
|
||||
minotaur = "2.8.7"
|
||||
lombok = "8.4"
|
||||
blossom = "1.2.0"
|
||||
|
|
Loading…
Reference in New Issue