init: send correct structure size/offset/rotation to java, not fully working yet

This commit is contained in:
onebeastchris 2024-03-26 23:54:36 +01:00
parent d1e4b31bce
commit a30d49acba
6 changed files with 289 additions and 85 deletions

View File

@ -90,6 +90,7 @@ import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
import org.cloudburstmc.protocol.bedrock.data.command.SoftEnumUpdateType;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.data.structure.StructureSettings;
import org.cloudburstmc.protocol.bedrock.packet.*;
import org.cloudburstmc.protocol.common.DefinitionRegistry;
import org.cloudburstmc.protocol.common.util.OptionalBoolean;
@ -608,6 +609,13 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@Setter @Getter
private @Nullable Vector3i currentStructureBlock;
/**
* Stores current structure block settings so we know what rotation to undo
* if Bedrock decides to change the rotation
*/
@Setter @Getter
private @Nullable StructureSettings structureSettings;
private final GeyserCameraData cameraData;
private final GeyserEntityData entityData;

View File

@ -27,18 +27,15 @@ 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.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtUtils;
import org.cloudburstmc.protocol.bedrock.data.structure.StructureMirror;
import org.cloudburstmc.protocol.bedrock.data.structure.StructureRotation;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.util.StructureBlockUtils;
@BlockEntity(type = BlockEntityType.STRUCTURE_BLOCK)
public class StructureBlockBlockEntityTranslator extends BlockEntityTranslator {
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
GeyserImpl.getInstance().getLogger().info(NbtUtils.toString(tag));
if (tag.size() < 5) {
return; // These values aren't here
}
@ -81,13 +78,20 @@ public class StructureBlockBlockEntityTranslator extends BlockEntityTranslator {
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);
Vector3i[] sizeAndOffset = StructureBlockUtils.addOffsets(bedrockRotation, bedrockMirror,
xStructureSize, getOrDefault(tag.get("sizeY"), 0), zStructureSize,
posX, getOrDefault(tag.get("posY"), 0), posZ);
/*
int newXStructureSize = xStructureSize;
int newZStructureSize = zStructureSize;
// Modify positions if mirrored - Bedrock doesn't have this
if (bedrockMirror == (byte) StructureMirror.Z.ordinal()) {
posX = posX + xStructureSize;
@ -138,17 +142,20 @@ public class StructureBlockBlockEntityTranslator extends BlockEntityTranslator {
}
}
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);
Vector3i size = sizeAndOffset[1];
builder.putInt("xStructureSize", size.getX());
builder.putInt("yStructureSize", size.getY());
builder.putInt("zStructureSize", size.getZ());
Vector3i offset = sizeAndOffset[0];
builder.putInt("xStructureOffset", offset.getX());
builder.putInt("yStructureOffset", offset.getY());
builder.putInt("zStructureOffset", offset.getZ());
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
GeyserImpl.getInstance().getLogger().error(builder.toString());
}
}

View File

@ -30,19 +30,23 @@ import com.github.steveice10.mc.protocol.data.game.inventory.UpdateStructureBloc
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.math.vector.Vector3i;
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.GeyserImpl;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.StructureBlockUtils;
@Translator(packet = StructureBlockUpdatePacket.class)
public class BedrockStructureBlockUpdateTranslator extends PacketTranslator<StructureBlockUpdatePacket> {
@Override
public void translate(GeyserSession session, StructureBlockUpdatePacket packet) {
GeyserImpl.getInstance().getLogger().info(packet.toString());
StructureEditorData data = packet.getEditorData();
StructureSettings settings = data.getSettings();
@ -63,7 +67,11 @@ public class BedrockStructureBlockUpdateTranslator extends PacketTranslator<Stru
};
// Ignore mirror - Java appears to mirror on an axis, while Bedrock mirrors in place
StructureMirror mirror = StructureMirror.NONE;
StructureMirror mirror = switch (data.getSettings().getMirror()) {
case X -> StructureMirror.FRONT_BACK;
case XZ -> StructureMirror.LEFT_RIGHT;
default -> StructureMirror.NONE;
};
com.github.steveice10.mc.protocol.data.game.level.block.StructureRotation rotation = switch (settings.getRotation()) {
case ROTATE_90 -> StructureRotation.CLOCKWISE_90;
@ -72,13 +80,15 @@ public class BedrockStructureBlockUpdateTranslator extends PacketTranslator<Stru
default -> StructureRotation.NONE;
};
Vector3i[] offsetAndSize = StructureBlockUtils.getStructureOffsetAndRotation(session, settings);
ServerboundSetStructureBlockPacket structureBlockPacket = new ServerboundSetStructureBlockPacket(
packet.getBlockPosition(),
action,
mode,
data.getName(),
settings.getOffset(),
settings.getSize(),
offsetAndSize[0],
offsetAndSize[1],
mirror,
rotation,
"",
@ -89,6 +99,7 @@ public class BedrockStructureBlockUpdateTranslator extends PacketTranslator<Stru
data.isBoundingBoxVisible()
);
session.setStructureSettings(null);
session.sendDownstreamPacket(structureBlockPacket);
}
}

View File

@ -31,18 +31,13 @@ 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.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.StructureTemplateRequestOperation;
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.geyser.GeyserImpl;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.StructureBlockUtils;
/**
* Packet used in Bedrock to load structure size into the structure block GUI.
@ -52,33 +47,25 @@ import org.geysermc.geyser.translator.protocol.Translator;
@Translator(packet = StructureTemplateDataRequestPacket.class)
public class BedrockStructureTemplateDataRequestTranslator extends PacketTranslator<StructureTemplateDataRequestPacket> {
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 void translate(GeyserSession session, StructureTemplateDataRequestPacket packet) {
// This packet is actually rather neat. Each time Bedrock players open the structure block,
// it is sent. Which allows us to cache what rotation operation we might need to (un)do :p
GeyserImpl.getInstance().getLogger().info(packet.toString());
if (packet.getOperation().equals(StructureTemplateRequestOperation.QUERY_SAVED_STRUCTURE)) {
// If we send a load packet to the Java server when the structure size is known, we place the structure.
if (!packet.getSettings().getSize().equals(Vector3i.ZERO)) {
if (session.getStructureSettings() == null) {
// This ensures we don't add more rotation offsetting than needed
session.setStructureSettings(packet.getSettings());
}
// Otherwise, the Bedrock client can't load the structure in
sendEmptyStructureData(session, packet);
StructureBlockUtils.sendEmptyStructureData(session, packet);
return;
}
session.setCurrentStructureBlock(packet.getPosition());
// Request a "load" from Java server so it sends us the structures size :p
// Request a "load" from Java server, so it sends us the structure's size :p
var settings = packet.getSettings();
com.github.steveice10.mc.protocol.data.game.level.block.StructureRotation rotation = switch (settings.getRotation()) {
case ROTATE_90 -> StructureRotation.CLOCKWISE_90;
@ -105,19 +92,7 @@ public class BedrockStructureTemplateDataRequestTranslator extends PacketTransla
);
session.sendDownstreamPacket(structureBlockPacket);
} else {
sendEmptyStructureData(session, packet);
StructureBlockUtils.sendEmptyStructureData(session, packet);
}
}
private 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);
}
}

View File

@ -30,18 +30,16 @@ import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundBlockEntityDataPacket;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import org.cloudburstmc.math.vector.Vector3f;
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.definitions.BlockDefinition;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType;
import org.cloudburstmc.protocol.bedrock.data.structure.StructureTemplateResponseType;
import org.cloudburstmc.protocol.bedrock.data.structure.StructureAnimationMode;
import org.cloudburstmc.protocol.bedrock.data.structure.StructureMirror;
import org.cloudburstmc.protocol.bedrock.data.structure.StructureRotation;
import org.cloudburstmc.protocol.bedrock.data.structure.StructureSettings;
import org.cloudburstmc.protocol.bedrock.packet.ContainerOpenPacket;
import org.cloudburstmc.protocol.bedrock.packet.StructureTemplateDataResponsePacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
@ -50,24 +48,11 @@ import org.geysermc.geyser.translator.level.block.entity.SkullBlockEntityTransla
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.BlockEntityUtils;
import org.geysermc.geyser.util.StructureBlockUtils;
@Translator(packet = ClientboundBlockEntityDataPacket.class)
public class JavaBlockEntityDataTranslator extends PacketTranslator<ClientboundBlockEntityDataPacket> {
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 void translate(GeyserSession session, ClientboundBlockEntityDataPacket packet) {
final BlockEntityType type = packet.getType();
@ -130,20 +115,46 @@ public class JavaBlockEntityDataTranslator extends PacketTranslator<ClientboundB
return;
}
int x = getOrDefault(map.get("sizeX"), 0);
int y = getOrDefault(map.get("sizeY"), 0);
int z = getOrDefault(map.get("sizeZ"), 0);
String mirror = getOrDefault(map.get("mirror"), "");
byte bedrockMirror = switch (mirror) {
case "LEFT_RIGHT" -> 1;
case "FRONT_BACK" -> 2;
default -> 0; // Or NONE
};
StructureTemplateDataResponsePacket responsePacket = new StructureTemplateDataResponsePacket();
responsePacket.setName(getOrDefault(map.get("name"), " "));
responsePacket.setSave(true);
responsePacket.setTag(EMPTY_STRUCTURE_DATA.toBuilder()
.putList("size", NbtType.INT, x, y, z)
.build());
responsePacket.setType(StructureTemplateResponseType.QUERY);
GeyserImpl.getInstance().getLogger().info(responsePacket.toString());
session.sendUpstreamPacket(responsePacket);
String rotation = getOrDefault(map.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
};
// The "positions" are also offsets on Java
int posX = getOrDefault(map.get("posX"), 0);
int posZ = getOrDefault(map.get("posZ"), 0);
Vector3i[] sizeAndOffset = StructureBlockUtils.addOffsets(bedrockRotation, bedrockMirror,
getOrDefault(map.get("sizeX"), 0), getOrDefault(map.get("sizeY"), 0),
getOrDefault(map.get("sizeZ"), 0), posX, getOrDefault(map.get("posY"), 0), posZ);
String name = getOrDefault(map.get("name"), "");
Vector3i size = sizeAndOffset[1];
StructureBlockUtils.sendStructureData(session, size.getX(), size.getY(), size.getZ(), name);
StructureSettings settings = new StructureSettings("",
false,
false,
false,
size,
sizeAndOffset[1],
-1,
StructureRotation.from(bedrockRotation),
StructureMirror.from(bedrockMirror),
StructureAnimationMode.NONE,
0, 0, 0, Vector3f.ZERO);
session.setStructureSettings(settings);
session.setCurrentStructureBlock(null);
}
}

View File

@ -0,0 +1,192 @@
/*
* 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.util;
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.StructureMirror;
import org.cloudburstmc.protocol.bedrock.data.structure.StructureRotation;
import org.cloudburstmc.protocol.bedrock.data.structure.StructureSettings;
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.geyser.session.GeyserSession;
public class StructureBlockUtils {
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 static void sendEmptyStructureData(GeyserSession session, StructureTemplateDataRequestPacket packet) {
sendStructureData(session, packet.getSettings().getSize().getX(),
packet.getSettings().getSize().getY(),
packet.getSettings().getSize().getZ(),
packet.getName());
}
public static void sendStructureData(GeyserSession session, int x, int y, int z, String name) {
StructureTemplateDataResponsePacket responsePacket = new StructureTemplateDataResponsePacket();
responsePacket.setName(name);
responsePacket.setSave(true);
responsePacket.setTag(EMPTY_STRUCTURE_DATA.toBuilder()
.putList("size", NbtType.INT, x, y, z)
.build());
responsePacket.setType(StructureTemplateResponseType.QUERY);
session.sendUpstreamPacket(responsePacket);
}
public static Vector3i[] getStructureOffsetAndRotation(GeyserSession session, StructureSettings newSettings) {
StructureSettings old = session.getStructureSettings();
if (old == null) {
return new Vector3i[] {newSettings.getOffset(), newSettings.getSize()};
}
// Remove offsets for old
return removeOffset(old.getRotation(), old.getMirror(), old.getOffset(), old.getSize());
// Remove offsets for new
//return removeOffset(newSettings.getRotation(), newSettings.getMirror(), temp[0], temp[1]);
}
private static Vector3i[] removeOffset(StructureRotation rotation, StructureMirror mirror,
Vector3i offset, Vector3i size) {
int sizeX = size.getX();
int sizeY = size.getY();
int sizeZ = size.getZ();
int offsetX = offset.getX();
int offsetY = offset.getY();
int offsetZ = offset.getZ();
// Undo mirror/rotation changes
switch (rotation) {
case NONE:
break;
case ROTATE_90:
int tempX = offsetX;
offsetX = offsetZ;
offsetZ = sizeX - 1 - tempX;
int tempSizeX = sizeX;
sizeX = sizeZ;
sizeZ = tempSizeX;
break;
case ROTATE_180:
offsetX = sizeX - 1 - offsetX;
offsetZ = sizeZ - 1 - offsetZ;
break;
case ROTATE_270:
int tempY = offsetY;
offsetY = offsetZ;
offsetZ = sizeZ - 1 - tempY;
int tempSizeZ = sizeZ;
sizeZ = sizeX;
sizeX = tempSizeZ;
break;
}
if (mirror == StructureMirror.Z) {
offsetX = sizeX - 1 - offsetX;
} else if (mirror == StructureMirror.X) {
offsetZ = sizeZ - 1 - offsetZ;
}
return new Vector3i[]{Vector3i.from(offsetX, offsetY, offsetZ), Vector3i.from(sizeX, sizeY, sizeZ)};
}
public static Vector3i[] addOffsets(byte bedrockRotation, byte bedrockMirror,
int sizeX, int sizeY, int sizeZ, int offsetX, int offsetY, int offsetZ) {
int newXStructureSize = sizeX;
int newZStructureSize = sizeZ;
// Modify positions if mirrored - Bedrock doesn't have this
if (bedrockMirror == (byte) StructureMirror.Z.ordinal()) {
offsetX = offsetX + sizeX;
newXStructureSize = sizeX * -1;
} else if (bedrockMirror == (byte) StructureMirror.X.ordinal()) {
offsetZ = offsetZ + sizeZ;
newZStructureSize = sizeZ * -1;
}
// Bedrock rotates with the same origin; Java does not
StructureRotation structureRotation = StructureRotation.values()[bedrockRotation];
switch (structureRotation) {
case ROTATE_90 -> {
if (sizeX >= 0) {
offsetX += 1;
}
if (sizeZ < 0) {
offsetZ += 1;
}
offsetX -= sizeZ;
}
case ROTATE_180 -> {
if (sizeX >= 0) {
offsetX += 1;
}
if (sizeZ >= 0) {
offsetZ += 1;
}
offsetX -= sizeX;
offsetZ -= sizeZ;
}
case ROTATE_270 -> {
if (sizeX < 0) {
offsetX += 1;
}
if (sizeZ >= 0) {
offsetZ += 1;
}
offsetZ -= sizeX;
}
default -> {
if (sizeX < 0) {
offsetX += 1;
}
if (sizeZ < 0) {
offsetZ += 1;
}
}
}
return new Vector3i[]{Vector3i.from(offsetX, offsetY, offsetZ), Vector3i.from(newXStructureSize, sizeY, newZStructureSize)};
}
}