diff --git a/Jenkinsfile b/Jenkinsfile
index 6564bd1f9..09e88e86e 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -35,8 +35,8 @@ pipeline {
rtMavenResolver(
id: "maven-resolver",
serverId: "opencollab-artifactory",
- releaseRepo: "release",
- snapshotRepo: "snapshot"
+ releaseRepo: "maven-deploy-release",
+ snapshotRepo: "maven-deploy-snapshot"
)
rtMavenRun(
pom: 'pom.xml',
diff --git a/README.md b/README.md
index 343fca94a..b17eede96 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have now joined us here!
-### Currently supporting Minecraft Bedrock v1.16.100 - v1.16.201 and Minecraft Java v1.16.4 - v1.16.5.
+### Currently supporting Minecraft Bedrock v1.16.100 - v1.16.210 and Minecraft Java v1.16.4 - v1.16.5.
## Setting Up
Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set up Geyser.
diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml
index a6a2a86de..4b61a65c3 100644
--- a/bootstrap/pom.xml
+++ b/bootstrap/pom.xml
@@ -16,10 +16,6 @@
spigot-public
https://hub.spigotmc.org/nexus/content/repositories/public/
-
- bukkit-public
- https://repo.md-5.net/content/repositories/public/
-
sponge-repo
https://repo.spongepowered.org/repository/maven-public/
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java
index 56fa7581b..51e68a263 100644
--- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java
@@ -53,11 +53,11 @@ public class GeyserSpigotBlockPlaceListener implements Listener {
placeBlockSoundPacket.setPosition(Vector3f.from(event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ()));
placeBlockSoundPacket.setBabySound(false);
if (worldManager.isLegacy()) {
- placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(worldManager.getBlockAt(session,
+ placeBlockSoundPacket.setExtraData(session.getBlockTranslator().getBedrockBlockId(worldManager.getBlockAt(session,
event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ())));
} else {
String javaBlockId = event.getBlockPlaced().getBlockData().getAsString();
- placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaIdBlockMap().getOrDefault(javaBlockId, BlockTranslator.JAVA_AIR_ID)));
+ placeBlockSoundPacket.setExtraData(session.getBlockTranslator().getBedrockBlockId(BlockTranslator.getJavaIdBlockMap().getOrDefault(javaBlockId, BlockTranslator.JAVA_AIR_ID)));
}
placeBlockSoundPacket.setIdentifier(":");
session.sendUpstreamPacket(placeBlockSoundPacket);
diff --git a/connector/pom.xml b/connector/pom.xml
index 107b4144b..5a462c5c5 100644
--- a/connector/pom.xml
+++ b/connector/pom.xml
@@ -12,6 +12,8 @@
4.1.59.Final
+ 8.5.2
+ 4.5.0
@@ -40,14 +42,10 @@
com.github.CloudburstMC.Protocol
- bedrock-v422
- 294e7e5
+ bedrock-v428
+ 42da92f
compile
-
- net.sf.trove4j
- trove
-
com.nukkitx.network
raknet
@@ -55,9 +53,9 @@
- com.github.CloudburstMC.Network
+ com.nukkitx.network
raknet
- a94d2dd
+ 1.6.26-20210217.205834-2
compile
@@ -69,61 +67,61 @@
com.nukkitx.fastutil
fastutil-int-int-maps
- 8.3.1
+ ${fastutil.version}
compile
com.nukkitx.fastutil
fastutil-int-float-maps
- 8.3.1
+ ${fastutil.version}
compile
com.nukkitx.fastutil
fastutil-long-long-maps
- 8.3.1
+ ${fastutil.version}
compile
com.nukkitx.fastutil
fastutil-object-long-maps
- 8.3.1
+ ${fastutil.version}
compile
com.nukkitx.fastutil
fastutil-int-byte-maps
- 8.3.1
+ ${fastutil.version}
compile
com.nukkitx.fastutil
fastutil-int-double-maps
- 8.3.1
+ ${fastutil.version}
compile
com.nukkitx.fastutil
fastutil-int-boolean-maps
- 8.3.1
+ ${fastutil.version}
compile
com.nukkitx.fastutil
fastutil-object-int-maps
- 8.3.1
+ ${fastutil.version}
compile
com.nukkitx.fastutil
fastutil-object-byte-maps
- 8.3.1
+ ${fastutil.version}
compile
com.nukkitx.fastutil
fastutil-object-object-maps
- 8.3.1
+ ${fastutil.version}
compile
@@ -225,25 +223,25 @@
net.kyori
adventure-api
- 4.5.0
+ ${adventure.version}
compile
net.kyori
adventure-text-serializer-gson
- 4.5.0
+ ${adventure.version}
compile
net.kyori
adventure-text-serializer-legacy
- 4.5.0
+ ${adventure.version}
compile
net.kyori
adventure-text-serializer-gson-legacy-impl
- 4.5.0
+ ${adventure.version}
compile
diff --git a/connector/src/main/java/org/geysermc/connector/entity/BoatEntity.java b/connector/src/main/java/org/geysermc/connector/entity/BoatEntity.java
index ed5272b89..d07ecc965 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/BoatEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/BoatEntity.java
@@ -28,6 +28,7 @@ package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
+import com.nukkitx.protocol.bedrock.packet.AnimatePacket;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
@@ -105,27 +106,38 @@ public class BoatEntity extends Entity {
metadata.put(EntityData.VARIANT, entityMetadata.getValue());
} else if (entityMetadata.getId() == 11) {
isPaddlingLeft = (boolean) entityMetadata.getValue();
- if (!isPaddlingLeft) {
- metadata.put(EntityData.ROW_TIME_LEFT, 0f);
- }
- else {
+ if (isPaddlingLeft) {
// Java sends simply "true" and "false" (is_paddling_left), Bedrock keeps sending packets as you're rowing
// This is an asynchronous method that emulates Bedrock rowing until "false" is sent.
paddleTimeLeft = 0f;
- session.getConnector().getGeneralThreadPool().execute(() ->
- updateLeftPaddle(session, entityMetadata)
- );
+ if (!this.passengers.isEmpty()) {
+ // Get the entity by the first stored passenger and convey motion in this manner
+ Entity entity = session.getEntityCache().getEntityByJavaId(this.passengers.iterator().nextLong());
+ if (entity != null) {
+ session.getConnector().getGeneralThreadPool().execute(() ->
+ updateLeftPaddle(session, entity)
+ );
+ }
+ }
+ } else {
+ // Indicate that the row position should be reset
+ metadata.put(EntityData.ROW_TIME_LEFT, 0.0f);
}
}
else if (entityMetadata.getId() == 12) {
isPaddlingRight = (boolean) entityMetadata.getValue();
- if (!isPaddlingRight) {
- metadata.put(EntityData.ROW_TIME_RIGHT, 0f);
- } else {
+ if (isPaddlingRight) {
paddleTimeRight = 0f;
- session.getConnector().getGeneralThreadPool().execute(() ->
- updateRightPaddle(session, entityMetadata)
- );
+ if (!this.passengers.isEmpty()) {
+ Entity entity = session.getEntityCache().getEntityByJavaId(this.passengers.iterator().nextLong());
+ if (entity != null) {
+ session.getConnector().getGeneralThreadPool().execute(() ->
+ updateRightPaddle(session, entity)
+ );
+ }
+ }
+ } else {
+ metadata.put(EntityData.ROW_TIME_RIGHT, 0.0f);
}
} else if (entityMetadata.getId() == 13) {
// Possibly - I don't think this does anything?
@@ -135,27 +147,46 @@ public class BoatEntity extends Entity {
super.updateBedrockMetadata(entityMetadata, session);
}
- public void updateLeftPaddle(GeyserSession session, EntityMetadata entityMetadata) {
+ @Override
+ public void updateBedrockMetadata(GeyserSession session) {
+ super.updateBedrockMetadata(session);
+
+ // As these indicate to reset rowing, remove them until it is time to send them out again.
+ metadata.remove(EntityData.ROW_TIME_LEFT);
+ metadata.remove(EntityData.ROW_TIME_RIGHT);
+ }
+
+ private void updateLeftPaddle(GeyserSession session, Entity rower) {
if (isPaddlingLeft) {
paddleTimeLeft += ROWING_SPEED;
- metadata.put(EntityData.ROW_TIME_LEFT, paddleTimeLeft);
- super.updateBedrockMetadata(entityMetadata, session);
+ sendAnimationPacket(session, rower, AnimatePacket.Action.ROW_LEFT, paddleTimeLeft);
+
session.getConnector().getGeneralThreadPool().schedule(() ->
- updateLeftPaddle(session, entityMetadata),
+ updateLeftPaddle(session, rower),
100,
TimeUnit.MILLISECONDS
);
- }}
+ }
+ }
- public void updateRightPaddle(GeyserSession session, EntityMetadata entityMetadata) {
+ private void updateRightPaddle(GeyserSession session, Entity rower) {
if (isPaddlingRight) {
paddleTimeRight += ROWING_SPEED;
- metadata.put(EntityData.ROW_TIME_RIGHT, paddleTimeRight);
- super.updateBedrockMetadata(entityMetadata, session);
+ sendAnimationPacket(session, rower, AnimatePacket.Action.ROW_RIGHT, paddleTimeRight);
+
session.getConnector().getGeneralThreadPool().schedule(() ->
- updateRightPaddle(session, entityMetadata),
+ updateRightPaddle(session, rower),
100,
TimeUnit.MILLISECONDS
);
- }}
+ }
+ }
+
+ private void sendAnimationPacket(GeyserSession session, Entity rower, AnimatePacket.Action action, float rowTime) {
+ AnimatePacket packet = new AnimatePacket();
+ packet.setRuntimeEntityId(rower.getGeyserId());
+ packet.setAction(action);
+ packet.setRowingTime(rowTime);
+ session.sendUpstreamPacket(packet);
+ }
}
diff --git a/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java
index 6ae65643c..52183c431 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java
@@ -31,7 +31,6 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import net.kyori.adventure.text.Component;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
-import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity {
@@ -60,8 +59,8 @@ public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity {
* By default, the command block shown is purple on Bedrock, which does not match Java Edition's orange.
*/
@Override
- public void updateDefaultBlockMetadata() {
- metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.BEDROCK_RUNTIME_COMMAND_BLOCK_ID);
+ public void updateDefaultBlockMetadata(GeyserSession session) {
+ metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockRuntimeCommandBlockId());
metadata.put(EntityData.DISPLAY_OFFSET, 6);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java
index 8ab368e70..805105c64 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java
@@ -30,7 +30,6 @@ import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
-import org.geysermc.connector.network.translators.world.block.BlockTranslator;
/**
* This class is used as a base for minecarts with a default block to display like furnaces and spawners
@@ -44,10 +43,15 @@ public class DefaultBlockMinecartEntity extends MinecartEntity {
public DefaultBlockMinecartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
- updateDefaultBlockMetadata();
metadata.put(EntityData.CUSTOM_DISPLAY, (byte) 1);
}
+ @Override
+ public void spawnEntity(GeyserSession session) {
+ updateDefaultBlockMetadata(session);
+ super.spawnEntity(session);
+ }
+
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
@@ -56,7 +60,7 @@ public class DefaultBlockMinecartEntity extends MinecartEntity {
customBlock = (int) entityMetadata.getValue();
if (showCustomBlock) {
- metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(customBlock));
+ metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId(customBlock));
}
}
@@ -73,16 +77,16 @@ public class DefaultBlockMinecartEntity extends MinecartEntity {
if (entityMetadata.getId() == 12) {
if ((boolean) entityMetadata.getValue()) {
showCustomBlock = true;
- metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(customBlock));
+ metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId(customBlock));
metadata.put(EntityData.DISPLAY_OFFSET, customBlockOffset);
} else {
showCustomBlock = false;
- updateDefaultBlockMetadata();
+ updateDefaultBlockMetadata(session);
}
}
super.updateBedrockMetadata(entityMetadata, session);
}
- public void updateDefaultBlockMetadata() { }
+ public void updateDefaultBlockMetadata(GeyserSession session) { }
}
diff --git a/connector/src/main/java/org/geysermc/connector/entity/Entity.java b/connector/src/main/java/org/geysermc/connector/entity/Entity.java
index d41578db4..3ba3c9730 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/Entity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/Entity.java
@@ -273,13 +273,9 @@ public class Entity {
metadata.getFlags().setFlag(EntityFlag.SWIMMING, ((xd & 0x10) == 0x10) && metadata.getFlags().getFlag(EntityFlag.SPRINTING)); // Otherwise swimming is enabled on older servers
metadata.getFlags().setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80);
- if ((xd & 0x20) == 0x20) {
- // Armour stands are handled in their own class
- if (!this.is(ArmorStandEntity.class)) {
- metadata.getFlags().setFlag(EntityFlag.INVISIBLE, true);
- }
- } else {
- metadata.getFlags().setFlag(EntityFlag.INVISIBLE, false);
+ // Armour stands are handled in their own class
+ if (!this.is(ArmorStandEntity.class)) {
+ metadata.getFlags().setFlag(EntityFlag.INVISIBLE, (xd & 0x20) == 0x20);
}
// Shield code
diff --git a/connector/src/main/java/org/geysermc/connector/entity/FallingBlockEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FallingBlockEntity.java
index 76ca0567e..bd0fe9b80 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/FallingBlockEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/FallingBlockEntity.java
@@ -31,14 +31,19 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
-import org.geysermc.connector.network.translators.world.block.BlockTranslator;
public class FallingBlockEntity extends Entity {
+ private final int javaId;
public FallingBlockEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, int javaId) {
super(entityId, geyserId, entityType, position, motion, rotation);
+ this.javaId = javaId;
+ }
- this.metadata.put(EntityData.VARIANT, BlockTranslator.getBedrockBlockId(javaId));
+ @Override
+ public void spawnEntity(GeyserSession session) {
+ this.metadata.put(EntityData.VARIANT, session.getBlockTranslator().getBedrockBlockId(javaId));
+ super.spawnEntity(session);
}
@Override
diff --git a/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java
index e3af51be6..fdf24f176 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java
@@ -44,15 +44,15 @@ public class FurnaceMinecartEntity extends DefaultBlockMinecartEntity {
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 13 && !showCustomBlock) {
hasFuel = (boolean) entityMetadata.getValue();
- updateDefaultBlockMetadata();
+ updateDefaultBlockMetadata(session);
}
super.updateBedrockMetadata(entityMetadata, session);
}
@Override
- public void updateDefaultBlockMetadata() {
- metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(hasFuel ? BlockTranslator.JAVA_RUNTIME_FURNACE_LIT_ID : BlockTranslator.JAVA_RUNTIME_FURNACE_ID));
+ public void updateDefaultBlockMetadata(GeyserSession session) {
+ metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId(hasFuel ? BlockTranslator.JAVA_RUNTIME_FURNACE_LIT_ID : BlockTranslator.JAVA_RUNTIME_FURNACE_ID));
metadata.put(EntityData.DISPLAY_OFFSET, 6);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java
index 4f0a224e2..a898ea389 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java
@@ -40,7 +40,6 @@ import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.network.translators.item.ItemTranslator;
-import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import java.util.concurrent.TimeUnit;
@@ -49,15 +48,19 @@ import java.util.concurrent.TimeUnit;
*/
public class ItemFrameEntity extends Entity {
+ /**
+ * Used to construct the block entity tag on spawning.
+ */
+ private final HangingDirection direction;
/**
* Used for getting the Bedrock block position.
* Blocks deal with integers whereas entities deal with floats.
*/
- private final Vector3i bedrockPosition;
+ private Vector3i bedrockPosition;
/**
* Specific block 'state' we are emulating in Bedrock.
*/
- private final int bedrockRuntimeId;
+ private int bedrockRuntimeId;
/**
* Rotation of item in frame.
*/
@@ -69,19 +72,21 @@ public class ItemFrameEntity extends Entity {
public ItemFrameEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, HangingDirection direction) {
super(entityId, geyserId, entityType, position, motion, rotation);
- NbtMapBuilder blockBuilder = NbtMap.builder()
- .putString("name", "minecraft:frame")
- .putInt("version", BlockTranslator.getBlockStateVersion());
- blockBuilder.put("states", NbtMap.builder()
- .putInt("facing_direction", direction.ordinal())
- .putByte("item_frame_map_bit", (byte) 0)
- .build());
- bedrockRuntimeId = BlockTranslator.getItemFrame(blockBuilder.build());
- bedrockPosition = Vector3i.from(position.getFloorX(), position.getFloorY(), position.getFloorZ());
+ this.direction = direction;
}
@Override
public void spawnEntity(GeyserSession session) {
+ NbtMapBuilder blockBuilder = NbtMap.builder()
+ .putString("name", "minecraft:frame")
+ .putInt("version", session.getBlockTranslator().getBlockStateVersion());
+ blockBuilder.put("states", NbtMap.builder()
+ .putInt("facing_direction", direction.ordinal())
+ .putByte("item_frame_map_bit", (byte) 0)
+ .build());
+ bedrockRuntimeId = session.getBlockTranslator().getItemFrame(blockBuilder.build());
+ bedrockPosition = Vector3i.from(position.getFloorX(), position.getFloorY(), position.getFloorZ());
+
session.getItemFrameCache().put(bedrockPosition, entityId);
// Delay is required, or else loading in frames on chunk load is sketchy at best
session.getConnector().getGeneralThreadPool().schedule(() -> {
@@ -136,7 +141,7 @@ public class ItemFrameEntity extends Entity {
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setDataLayer(0);
updateBlockPacket.setBlockPosition(bedrockPosition);
- updateBlockPacket.setRuntimeId(BlockTranslator.BEDROCK_AIR_ID);
+ updateBlockPacket.setRuntimeId(session.getBlockTranslator().getBedrockAirId());
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
diff --git a/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java
index ed5f28d17..49b12a3e1 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java
@@ -30,7 +30,6 @@ import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
-import org.geysermc.connector.network.translators.world.block.BlockTranslator;
public class MinecartEntity extends Entity {
@@ -58,7 +57,7 @@ public class MinecartEntity extends Entity {
if (!(this instanceof DefaultBlockMinecartEntity)) { // Handled in the DefaultBlockMinecartEntity class
// Custom block
if (entityMetadata.getId() == 10) {
- metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId((int) entityMetadata.getValue()));
+ metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId((int) entityMetadata.getValue()));
}
// Custom block offset
diff --git a/connector/src/main/java/org/geysermc/connector/entity/SpawnerMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/SpawnerMinecartEntity.java
index 143e36373..2f7af73eb 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/SpawnerMinecartEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/SpawnerMinecartEntity.java
@@ -28,6 +28,7 @@ package org.geysermc.connector.entity;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import org.geysermc.connector.entity.type.EntityType;
+import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
public class SpawnerMinecartEntity extends DefaultBlockMinecartEntity {
@@ -37,8 +38,8 @@ public class SpawnerMinecartEntity extends DefaultBlockMinecartEntity {
}
@Override
- public void updateDefaultBlockMetadata() {
- metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(BlockTranslator.JAVA_RUNTIME_SPAWNER_ID));
+ public void updateDefaultBlockMetadata(GeyserSession session) {
+ metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId(BlockTranslator.JAVA_RUNTIME_SPAWNER_ID));
metadata.put(EntityData.DISPLAY_OFFSET, 6);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/ArmorStandEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/ArmorStandEntity.java
index e0f4d9a11..3d1005510 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/living/ArmorStandEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/living/ArmorStandEntity.java
@@ -29,6 +29,9 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadat
import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
+import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
+import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
+import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
import lombok.Getter;
import org.geysermc.connector.entity.LivingEntity;
import org.geysermc.connector.entity.type.EntityType;
@@ -42,15 +45,68 @@ public class ArmorStandEntity extends LivingEntity {
private boolean isInvisible = false;
private boolean isSmall = false;
+ /**
+ * On Java Edition, armor stands always show their name. Invisibility hides the name on Bedrock.
+ * By having a second entity, we can allow an invisible entity with the name tag.
+ * (This lets armor on armor stands still show)
+ */
+ private ArmorStandEntity secondEntity = null;
+ /**
+ * Whether this is the primary armor stand that holds the armor and not the name tag.
+ */
+ private boolean primaryEntity = true;
+ /**
+ * Whether the entity's position must be updated to included the offset.
+ *
+ * This should be true when the Java server marks the armor stand as invisible, but we shrink the entity
+ * to allow the nametag to appear. Basically:
+ * - Is visible: this is irrelevant (false)
+ * - Has armor, no name: false
+ * - Has armor, has name: false, with a second entity
+ * - No armor, no name: false
+ * - No armor, yes name: true
+ */
+ private boolean positionRequiresOffset = false;
+ /**
+ * Whether we should update the position of this armor stand after metadata updates.
+ */
+ private boolean positionUpdateRequired = false;
+ private GeyserSession session;
+
public ArmorStandEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
+ @Override
+ public void spawnEntity(GeyserSession session) {
+ this.session = session;
+ this.rotation = Vector3f.from(rotation.getX(), rotation.getX(), rotation.getX());
+ super.spawnEntity(session);
+ }
+
+ @Override
+ public boolean despawnEntity(GeyserSession session) {
+ if (secondEntity != null) {
+ secondEntity.despawnEntity(session);
+ }
+ return super.despawnEntity(session);
+ }
+
+ @Override
+ public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) {
+ if (secondEntity != null) {
+ secondEntity.moveRelative(session, relX, relY, relZ, rotation, isOnGround);
+ }
+ super.moveRelative(session, relX, relY, relZ, rotation, isOnGround);
+ }
+
@Override
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
- // Fake the height to be above where it is so the nametag appears in the right location for invisible non-marker armour stands
- if (!isMarker && isInvisible && passengers.isEmpty()) {
- position = position.add(0d, entityType.getHeight() * (isSmall ? 0.55d : 1d), 0d);
+ if (secondEntity != null) {
+ secondEntity.moveAbsolute(session, applyOffsetToPosition(position), rotation, isOnGround, teleported);
+ } else if (positionRequiresOffset) {
+ // Fake the height to be above where it is so the nametag appears in the right location for invisible non-marker armour stands
+ position = applyOffsetToPosition(position);
}
super.moveAbsolute(session, position, Vector3f.from(rotation.getX(), rotation.getX(), rotation.getX()), isOnGround, teleported);
@@ -58,47 +114,250 @@ public class ArmorStandEntity extends LivingEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
+ super.updateBedrockMetadata(entityMetadata, session);
if (entityMetadata.getId() == 0 && entityMetadata.getType() == MetadataType.BYTE) {
byte xd = (byte) entityMetadata.getValue();
// Check if the armour stand is invisible and store accordingly
- if ((xd & 0x20) == 0x20) {
- metadata.put(EntityData.SCALE, 0.0f);
- isInvisible = true;
+ if (primaryEntity) {
+ isInvisible = (xd & 0x20) == 0x20;
+ updateSecondEntityStatus(false);
}
+ } else if (entityMetadata.getId() == 2) {
+ updateSecondEntityStatus(false);
} else if (entityMetadata.getId() == 14 && entityMetadata.getType() == MetadataType.BYTE) {
byte xd = (byte) entityMetadata.getValue();
// isSmall
- if ((xd & 0x01) == 0x01) {
- isSmall = true;
-
- if (metadata.getFloat(EntityData.SCALE) != 0.55f && metadata.getFloat(EntityData.SCALE) != 0.0f) {
- metadata.put(EntityData.SCALE, 0.55f);
+ boolean newIsSmall = (xd & 0x01) == 0x01;
+ if (newIsSmall != isSmall) {
+ if (positionRequiresOffset) {
+ // Fix new inconsistency with offset
+ this.position = fixOffsetForSize(position, newIsSmall);
+ positionUpdateRequired = true;
}
- if (metadata.get(EntityData.BOUNDING_BOX_WIDTH) != null && metadata.get(EntityData.BOUNDING_BOX_WIDTH).equals(0.5f)) {
- metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.25f);
- metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.9875f);
+ isSmall = newIsSmall;
+ if (!isMarker) {
+ toggleSmallStatus();
}
- } else if (metadata.get(EntityData.BOUNDING_BOX_WIDTH) != null && metadata.get(EntityData.BOUNDING_BOX_WIDTH).equals(0.25f)) {
- metadata.put(EntityData.BOUNDING_BOX_WIDTH, entityType.getWidth());
- metadata.put(EntityData.BOUNDING_BOX_HEIGHT, entityType.getHeight());
}
// setMarker
- if ((xd & 0x10) == 0x10 && (metadata.get(EntityData.BOUNDING_BOX_WIDTH) == null || !metadata.get(EntityData.BOUNDING_BOX_WIDTH).equals(0.0f))) {
- metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.0f);
- metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.0f);
- isMarker = true;
+ boolean oldIsMarker = isMarker;
+ isMarker = (xd & 0x10) == 0x10;
+ if (oldIsMarker != isMarker) {
+ if (isMarker) {
+ metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.0f);
+ metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.0f);
+ metadata.put(EntityData.SCALE, 0f);
+ } else {
+ toggleSmallStatus();
+ }
+
+ updateSecondEntityStatus(false);
}
}
- super.updateBedrockMetadata(entityMetadata, session);
+ if (secondEntity != null) {
+ secondEntity.updateBedrockMetadata(entityMetadata, session);
+ }
}
@Override
- public void spawnEntity(GeyserSession session) {
- this.rotation = Vector3f.from(rotation.getX(), rotation.getX(), rotation.getX());
- super.spawnEntity(session);
+ public void updateBedrockMetadata(GeyserSession session) {
+ if (secondEntity != null) {
+ secondEntity.updateBedrockMetadata(session);
+ }
+ super.updateBedrockMetadata(session);
+ if (positionUpdateRequired) {
+ positionUpdateRequired = false;
+ updatePosition();
+ }
+ }
+
+ @Override
+ public void setHelmet(ItemData helmet) {
+ super.setHelmet(helmet);
+ updateSecondEntityStatus(true);
+ }
+
+ @Override
+ public void setChestplate(ItemData chestplate) {
+ super.setChestplate(chestplate);
+ updateSecondEntityStatus(true);
+ }
+
+ @Override
+ public void setLeggings(ItemData leggings) {
+ super.setLeggings(leggings);
+ updateSecondEntityStatus(true);
+ }
+
+ @Override
+ public void setBoots(ItemData boots) {
+ super.setBoots(boots);
+ updateSecondEntityStatus(true);
+ }
+
+ @Override
+ public void setHand(ItemData hand) {
+ super.setHand(hand);
+ updateSecondEntityStatus(true);
+ }
+
+ @Override
+ public void setOffHand(ItemData offHand) {
+ super.setOffHand(offHand);
+ updateSecondEntityStatus(true);
+ }
+
+ /**
+ * Determine if we need to load or unload the second entity.
+ *
+ * @param sendMetadata whether to send a metadata update after a change.
+ */
+ private void updateSecondEntityStatus(boolean sendMetadata) {
+ // A secondary entity always has to have the offset applied, so it remains invisible and the nametag shows.
+ if (!primaryEntity) return;
+ if (!isInvisible || isMarker) {
+ // It is either impossible to show armor, or the armor stand isn't invisible. We good.
+ metadata.getFlags().setFlag(EntityFlag.INVISIBLE, false);
+ updateOffsetRequirement(false);
+ if (positionUpdateRequired) {
+ positionUpdateRequired = false;
+ updatePosition();
+ }
+
+ if (secondEntity != null) {
+ secondEntity.despawnEntity(session);
+ secondEntity = null;
+ }
+ return;
+ }
+ //boolean isNametagEmpty = metadata.getString(EntityData.NAMETAG).isEmpty() || metadata.getByte(EntityData.NAMETAG_ALWAYS_SHOW, (byte) -1) == (byte) 0; - may not be necessary?
+ boolean isNametagEmpty = metadata.getString(EntityData.NAMETAG).isEmpty();
+ if (!isNametagEmpty && (!helmet.equals(ItemData.AIR) || !chestplate.equals(ItemData.AIR) || !leggings.equals(ItemData.AIR)
+ || !boots.equals(ItemData.AIR) || !hand.equals(ItemData.AIR) || !offHand.equals(ItemData.AIR))) {
+ // If the second entity exists, no need to recreate it.
+ // We can't stuff this check above or else it'll fall into another else case and delete the second entity
+ if (secondEntity != null) return;
+
+ // Create the second entity. It doesn't need to worry about the items, but it does need to worry about
+ // the metadata as it will hold the name tag.
+ secondEntity = new ArmorStandEntity(0, session.getEntityCache().getNextEntityId().incrementAndGet(),
+ EntityType.ARMOR_STAND, position, motion, rotation);
+ secondEntity.primaryEntity = false;
+ if (!this.positionRequiresOffset) {
+ // Ensure the offset is applied for the 0 scale
+ secondEntity.position = secondEntity.applyOffsetToPosition(secondEntity.position);
+ }
+ // Copy metadata
+ secondEntity.isSmall = isSmall;
+ secondEntity.getMetadata().putAll(metadata);
+ // Copy the flags so they aren't the same object in memory
+ secondEntity.getMetadata().putFlags(metadata.getFlags().copy());
+ // Guarantee this copy is NOT invisible
+ secondEntity.getMetadata().getFlags().setFlag(EntityFlag.INVISIBLE, false);
+ // Scale to 0 to show nametag
+ secondEntity.getMetadata().put(EntityData.SCALE, 0.0f);
+ // No bounding box as we don't want to interact with this entity
+ secondEntity.getMetadata().put(EntityData.BOUNDING_BOX_WIDTH, 0.0f);
+ secondEntity.getMetadata().put(EntityData.BOUNDING_BOX_HEIGHT, 0.0f);
+ secondEntity.spawnEntity(session);
+
+ // Reset scale of the proper armor stand
+ this.metadata.put(EntityData.SCALE, isSmall ? 0.55f : 1f);
+ // Set the proper armor stand to invisible to show armor
+ this.metadata.getFlags().setFlag(EntityFlag.INVISIBLE, true);
+ // Update the position of the armor stand
+ updateOffsetRequirement(false);
+ } else if (isNametagEmpty) {
+ // We can just make an invisible entity
+ // Reset scale of the proper armor stand
+ metadata.put(EntityData.SCALE, isSmall ? 0.55f : 1f);
+ // Set the proper armor stand to invisible to show armor
+ metadata.getFlags().setFlag(EntityFlag.INVISIBLE, true);
+ // Update offset
+ updateOffsetRequirement(false);
+
+ if (secondEntity != null) {
+ secondEntity.despawnEntity(session);
+ secondEntity = null;
+ }
+ } else {
+ // Nametag is not empty and there is no armor
+ // We don't need to make a new entity
+ metadata.getFlags().setFlag(EntityFlag.INVISIBLE, false);
+ metadata.put(EntityData.SCALE, 0.0f);
+ // As the above is applied, we need an offset
+ updateOffsetRequirement(true);
+
+ if (secondEntity != null) {
+ secondEntity.despawnEntity(session);
+ secondEntity = null;
+ }
+ }
+ if (sendMetadata) {
+ this.updateBedrockMetadata(session);
+ }
+ }
+
+ /**
+ * If this armor stand is not a marker, set its bounding box size and scale.
+ */
+ private void toggleSmallStatus() {
+ metadata.put(EntityData.BOUNDING_BOX_WIDTH, isSmall ? 0.25f : entityType.getWidth());
+ metadata.put(EntityData.BOUNDING_BOX_HEIGHT, isSmall ? 0.9875f : entityType.getHeight());
+ metadata.put(EntityData.SCALE, isSmall ? 0.55f : 1f);
+ }
+
+ /**
+ * @return the selected position with the position offset applied.
+ */
+ private Vector3f applyOffsetToPosition(Vector3f position) {
+ return position.add(0d, entityType.getHeight() * (isSmall ? 0.55d : 1d), 0d);
+ }
+
+ /**
+ * @return an adjusted offset for the new small status.
+ */
+ private Vector3f fixOffsetForSize(Vector3f position, boolean isNowSmall) {
+ position = removeOffsetFromPosition(position);
+ return position.add(0d, entityType.getHeight() * (isNowSmall ? 0.55d : 1d), 0d);
+ }
+
+ /**
+ * @return the selected position with the position offset removed.
+ */
+ private Vector3f removeOffsetFromPosition(Vector3f position) {
+ return position.sub(0d, entityType.getHeight() * (isSmall ? 0.55d : 1d), 0d);
+ }
+
+ /**
+ * Set the offset to a new value; if it changed, update the position, too.
+ */
+ private void updateOffsetRequirement(boolean newValue) {
+ if (newValue != positionRequiresOffset) {
+ this.positionRequiresOffset = newValue;
+ if (positionRequiresOffset) {
+ this.position = applyOffsetToPosition(position);
+ } else {
+ this.position = removeOffsetFromPosition(position);
+ }
+ positionUpdateRequired = true;
+ }
+ }
+
+ /**
+ * Updates position without calling movement code.
+ */
+ private void updatePosition() {
+ MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket();
+ moveEntityPacket.setRuntimeEntityId(geyserId);
+ moveEntityPacket.setPosition(position);
+ moveEntityPacket.setRotation(Vector3f.from(rotation.getX(), rotation.getX(), rotation.getX()));
+ moveEntityPacket.setOnGround(onGround);
+ moveEntityPacket.setTeleported(false);
+ session.sendUpstreamPacket(moveEntityPacket);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java
index 3151ae474..0d265b56e 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java
@@ -33,7 +33,6 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.packet.LevelSoundEvent2Packet;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
-import org.geysermc.connector.network.translators.world.block.BlockTranslator;
public class EndermanEntity extends MonsterEntity {
@@ -45,7 +44,7 @@ public class EndermanEntity extends MonsterEntity {
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
// Held block
if (entityMetadata.getId() == 15) {
- metadata.put(EntityData.CARRIED_BLOCK, BlockTranslator.getBedrockBlockId((int) entityMetadata.getValue()));
+ metadata.put(EntityData.CARRIED_BLOCK, session.getBlockTranslator().getBedrockBlockId((int) entityMetadata.getValue()));
}
// "Is screaming" - controls sound
if (entityMetadata.getId() == 16) {
diff --git a/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java b/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java
index d24cea328..3b5af7f99 100644
--- a/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java
+++ b/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java
@@ -28,6 +28,7 @@ package org.geysermc.connector.network;
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
import com.nukkitx.protocol.bedrock.v419.Bedrock_v419;
import com.nukkitx.protocol.bedrock.v422.Bedrock_v422;
+import com.nukkitx.protocol.bedrock.v428.Bedrock_v428;
import java.util.ArrayList;
import java.util.List;
@@ -40,9 +41,7 @@ public class BedrockProtocol {
* Default Bedrock codec that should act as a fallback. Should represent the latest available
* release of the game that Geyser supports.
*/
- public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v422.V422_CODEC.toBuilder()
- .minecraftVersion("1.16.201")
- .build();
+ public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v428.V428_CODEC;
/**
* A list of all supported Bedrock versions that can join Geyser
*/
@@ -52,9 +51,10 @@ public class BedrockProtocol {
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v419.V419_CODEC.toBuilder()
.minecraftVersion("1.16.100/1.16.101") // We change this as 1.16.100.60 is a beta
.build());
- SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder()
+ SUPPORTED_BEDROCK_CODECS.add(Bedrock_v422.V422_CODEC.toBuilder()
.minecraftVersion("1.16.200/1.16.201")
.build());
+ SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC);
}
/**
diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java
index d22645467..fa2670be1 100644
--- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java
+++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java
@@ -26,15 +26,18 @@
package org.geysermc.connector.network;
import com.nukkitx.protocol.bedrock.BedrockPacket;
-import com.nukkitx.protocol.bedrock.data.ResourcePackType;
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
+import com.nukkitx.protocol.bedrock.data.ResourcePackType;
import com.nukkitx.protocol.bedrock.packet.*;
+import com.nukkitx.protocol.bedrock.v428.Bedrock_v428;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.common.AuthType;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.session.cache.AdvancementsCache;
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator1_16_100;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator1_16_210;
import org.geysermc.connector.utils.LanguageUtils;
import org.geysermc.connector.utils.LoginEncryptionUtils;
import org.geysermc.connector.utils.MathUtils;
@@ -72,6 +75,10 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
session.getUpstream().getSession().setPacketCodec(packetCodec);
+ // Set the block translation based off of version
+ session.setBlockTranslator(packetCodec.getProtocolVersion() >= Bedrock_v428.V428_CODEC.getProtocolVersion()
+ ? BlockTranslator1_16_210.INSTANCE : BlockTranslator1_16_100.INSTANCE);
+
LoginEncryptionUtils.encryptPlayerConnection(connector, session, loginPacket);
PlayStatusPacket playStatus = new PlayStatusPacket();
diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java
index 01b9452be..589214652 100644
--- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java
+++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java
@@ -42,6 +42,7 @@ import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionRotationPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket;
+import com.github.steveice10.mc.protocol.packet.login.client.LoginPluginResponsePacket;
import com.github.steveice10.mc.protocol.packet.login.server.LoginSuccessPacket;
import com.github.steveice10.packetlib.BuiltinFlags;
import com.github.steveice10.packetlib.Client;
@@ -85,6 +86,7 @@ import org.geysermc.connector.network.translators.chat.MessageTranslator;
import org.geysermc.connector.network.translators.collision.CollisionManager;
import org.geysermc.connector.network.translators.inventory.EnchantmentInventoryTranslator;
import org.geysermc.connector.network.translators.item.ItemRegistry;
+import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.skin.FloodgateSkinUploader;
import org.geysermc.connector.skin.SkinManager;
import org.geysermc.connector.utils.*;
@@ -136,6 +138,12 @@ public class GeyserSession implements CommandSender {
*/
private final CollisionManager collisionManager;
+ /**
+ * Stores the block translations for this specific version.
+ */
+ @Setter
+ private BlockTranslator blockTranslator;
+
private final Map skullCache = new ConcurrentHashMap<>();
private final Long2ObjectMap storedMaps = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
@@ -949,7 +957,7 @@ public class GeyserSession implements CommandSender {
* @param packet the java edition packet from MCProtocolLib
*/
public void sendDownstreamPacket(Packet packet) {
- if (downstream != null && downstream.getSession() != null && protocol.getSubProtocol().equals(SubProtocol.GAME)) {
+ if (downstream != null && downstream.getSession() != null && (protocol.getSubProtocol().equals(SubProtocol.GAME) || packet.getClass() == LoginPluginResponsePacket.class)) {
downstream.getSession().send(packet);
} else {
connector.getLogger().debug("Tried to send downstream packet " + packet.getClass().getSimpleName() + " before connected to the server");
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java
index 8263507b2..9b8b5b668 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java
@@ -108,7 +108,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator= 2 && session.getGameMode() == GameMode.CREATIVE) {
// Otherwise insufficient permissions
- int blockState = BlockTranslator.getJavaBlockState(packet.getBlockRuntimeId());
+ int blockState = session.getBlockTranslator().getJavaBlockState(packet.getBlockRuntimeId());
String blockName = BlockTranslator.getJavaIdBlockMap().inverse().getOrDefault(blockState, "");
// In the future this can be used for structure blocks too, however not all elements
// are available in each GUI
@@ -253,7 +253,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator {
@Override
@@ -40,9 +42,12 @@ public class BedrockServerSettingsRequestTranslator extends PacketTranslator {
+ ServerSettingsResponsePacket serverSettingsResponsePacket = new ServerSettingsResponsePacket();
+ serverSettingsResponsePacket.setFormData(window.getJsonData());
+ serverSettingsResponsePacket.setFormId(windowId);
+ session.sendUpstreamPacket(serverSettingsResponsePacket);
+ }, 1, TimeUnit.SECONDS);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java
index c248b57a5..e837a036a 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java
@@ -180,7 +180,7 @@ public class BedrockActionTranslator extends PacketTranslator 0) {
canModifyBedrock = new String[canModifyJava.size()];
for (int i = 0; i < canModifyBedrock.length; i++) {
@@ -185,7 +185,7 @@ public abstract class ItemTranslator {
if (!block.startsWith("minecraft:")) block = "minecraft:" + block;
// Get the Bedrock identifier of the item and replace it.
// This will unfortunately be limited - for example, beds and banners will be translated weirdly
- canModifyBedrock[i] = BlockTranslator.getBedrockBlockIdentifier(block).replace("minecraft:", "");
+ canModifyBedrock[i] = session.getBlockTranslator().getBedrockBlockIdentifier(block).replace("minecraft:", "");
}
}
return canModifyBedrock;
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkBaseTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkBaseTranslator.java
new file mode 100644
index 000000000..dff40ea75
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkBaseTranslator.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2019-2021 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.connector.network.translators.item.translators.nbt;
+
+import com.github.steveice10.opennbt.tag.builtin.ByteArrayTag;
+import com.github.steveice10.opennbt.tag.builtin.ByteTag;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.github.steveice10.opennbt.tag.builtin.IntArrayTag;
+import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
+import org.geysermc.connector.utils.FireworkColor;
+import org.geysermc.connector.utils.MathUtils;
+
+/**
+ * Stores common code for firework rockets and firework stars.
+ */
+public abstract class FireworkBaseTranslator extends NbtItemStackTranslator {
+
+ protected CompoundTag translateExplosionToBedrock(CompoundTag explosion, String newName) {
+ CompoundTag newExplosionData = new CompoundTag(newName);
+
+ if (explosion.get("Type") != null) {
+ newExplosionData.put(new ByteTag("FireworkType", MathUtils.convertByte(explosion.get("Type").getValue())));
+ }
+
+ if (explosion.get("Colors") != null) {
+ int[] oldColors = (int[]) explosion.get("Colors").getValue();
+ byte[] colors = new byte[oldColors.length];
+
+ int i = 0;
+ for (int color : oldColors) {
+ colors[i++] = FireworkColor.fromJavaID(color).getBedrockID();
+ }
+
+ newExplosionData.put(new ByteArrayTag("FireworkColor", colors));
+ }
+
+ if (explosion.get("FadeColors") != null) {
+ int[] oldColors = (int[]) explosion.get("FadeColors").getValue();
+ byte[] colors = new byte[oldColors.length];
+
+ int i = 0;
+ for (int color : oldColors) {
+ colors[i++] = FireworkColor.fromJavaID(color).getBedrockID();
+ }
+
+ newExplosionData.put(new ByteArrayTag("FireworkFade", colors));
+ }
+
+ if (explosion.get("Trail") != null) {
+ newExplosionData.put(new ByteTag("FireworkTrail", MathUtils.convertByte(explosion.get("Trail").getValue())));
+ }
+
+ if (explosion.get("Flicker") != null) {
+ newExplosionData.put(new ByteTag("FireworkFlicker", MathUtils.convertByte(explosion.get("Flicker").getValue())));
+ }
+
+ return newExplosionData;
+ }
+
+ protected CompoundTag translateExplosionToJava(CompoundTag explosion, String newName) {
+ CompoundTag newExplosionData = new CompoundTag(newName);
+
+ if (explosion.get("FireworkType") != null) {
+ newExplosionData.put(new ByteTag("Type", MathUtils.convertByte(explosion.get("FireworkType").getValue())));
+ }
+
+ if (explosion.get("FireworkColor") != null) {
+ byte[] oldColors = (byte[]) explosion.get("FireworkColor").getValue();
+ int[] colors = new int[oldColors.length];
+
+ int i = 0;
+ for (byte color : oldColors) {
+ colors[i++] = FireworkColor.fromBedrockID(color).getJavaID();
+ }
+
+ newExplosionData.put(new IntArrayTag("Colors", colors));
+ }
+
+ if (explosion.get("FireworkFade") != null) {
+ byte[] oldColors = (byte[]) explosion.get("FireworkFade").getValue();
+ int[] colors = new int[oldColors.length];
+
+ int i = 0;
+ for (byte color : oldColors) {
+ colors[i++] = FireworkColor.fromBedrockID(color).getJavaID();
+ }
+
+ newExplosionData.put(new IntArrayTag("FadeColors", colors));
+ }
+
+ if (explosion.get("FireworkTrail") != null) {
+ newExplosionData.put(new ByteTag("Trail", MathUtils.convertByte(explosion.get("FireworkTrail").getValue())));
+ }
+
+ if (explosion.get("FireworkFlicker") != null) {
+ newExplosionData.put(new ByteTag("Flicker", MathUtils.convertByte(explosion.get("FireworkFlicker").getValue())));
+ }
+
+ return newExplosionData;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkRocketTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkRocketTranslator.java
new file mode 100644
index 000000000..f294315c8
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkRocketTranslator.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2019-2021 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.connector.network.translators.item.translators.nbt;
+
+import com.github.steveice10.opennbt.tag.builtin.ByteTag;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.github.steveice10.opennbt.tag.builtin.ListTag;
+import com.github.steveice10.opennbt.tag.builtin.Tag;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.ItemRemapper;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+import org.geysermc.connector.utils.MathUtils;
+
+@ItemRemapper
+public class FireworkRocketTranslator extends FireworkBaseTranslator {
+
+ @Override
+ public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) {
+ CompoundTag fireworks = itemTag.get("Fireworks");
+ if (fireworks == null) {
+ return;
+ }
+
+ if (fireworks.get("Flight") != null) {
+ fireworks.put(new ByteTag("Flight", MathUtils.convertByte(fireworks.get("Flight").getValue())));
+ }
+
+ ListTag explosions = fireworks.get("Explosions");
+ if (explosions == null) {
+ return;
+ }
+ for (Tag effect : explosions.getValue()) {
+ CompoundTag effectData = (CompoundTag) effect;
+ CompoundTag newEffectData = translateExplosionToBedrock(effectData, "");
+
+ explosions.remove(effectData);
+ explosions.add(newEffectData);
+ }
+ }
+
+ @Override
+ public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
+ CompoundTag fireworks = itemTag.get("Fireworks");
+ if (fireworks == null) {
+ return;
+ }
+
+ if (fireworks.contains("Flight")) {
+ fireworks.put(new ByteTag("Flight", MathUtils.convertByte(fireworks.get("Flight").getValue())));
+ }
+
+ ListTag explosions = fireworks.get("Explosions");
+ if (explosions == null) {
+ return;
+ }
+ for (Tag effect : explosions.getValue()) {
+ CompoundTag effectData = (CompoundTag) effect;
+ CompoundTag newEffectData = translateExplosionToJava(effectData, "");
+
+ explosions.remove(effect);
+ explosions.add(newEffectData);
+ }
+ }
+
+ @Override
+ public boolean acceptItem(ItemEntry itemEntry) {
+ return "minecraft:firework_rocket".equals(itemEntry.getJavaIdentifier());
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkStarTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkStarTranslator.java
new file mode 100644
index 000000000..686887b45
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkStarTranslator.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2019-2021 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.connector.network.translators.item.translators.nbt;
+
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import com.github.steveice10.opennbt.tag.builtin.IntArrayTag;
+import com.github.steveice10.opennbt.tag.builtin.IntTag;
+import com.github.steveice10.opennbt.tag.builtin.Tag;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.ItemRemapper;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+
+@ItemRemapper
+public class FireworkStarTranslator extends FireworkBaseTranslator {
+
+ @Override
+ public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) {
+ Tag explosion = itemTag.get("Explosion");
+ if (explosion instanceof CompoundTag) {
+ CompoundTag newExplosion = translateExplosionToBedrock((CompoundTag) explosion, "FireworksItem");
+ itemTag.remove("Explosion");
+ itemTag.put(newExplosion);
+ Tag color = ((CompoundTag) explosion).get("Colors");
+ if (color instanceof IntArrayTag) {
+ // Determine the custom color, if any.
+ // Mostly replicates Java's own rendering code, as Java determines the final firework star color client-side
+ // while Bedrock determines it server-side.
+ int[] colors = ((IntArrayTag) color).getValue();
+ if (colors.length == 0) {
+ return;
+ }
+ int finalColor;
+ if (colors.length == 1) {
+ finalColor = colors[0];
+ } else {
+ int r = 0;
+ int g = 0;
+ int b = 0;
+
+ for (int fireworkColor : colors) {
+ r += (fireworkColor & (255 << 16)) >> 16;
+ g += (fireworkColor & (255 << 8)) >> 8;
+ b += fireworkColor & 255;
+ }
+
+ r /= colors.length;
+ g /= colors.length;
+ b /= colors.length;
+ finalColor = r << 16 | g << 8 | b;
+ }
+
+ itemTag.put(new IntTag("customColor", finalColor));
+ }
+ }
+ }
+
+ @Override
+ public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
+ Tag explosion = itemTag.get("FireworksItem");
+ if (explosion instanceof CompoundTag) {
+ CompoundTag newExplosion = translateExplosionToJava((CompoundTag) explosion, "Explosion");
+ itemTag.remove("FireworksItem");
+ itemTag.put(newExplosion);
+ }
+ // Remove custom color, if any, since this only exists on Bedrock
+ itemTag.remove("customColor");
+ }
+
+ @Override
+ public boolean acceptItem(ItemEntry itemEntry) {
+ return "minecraft:firework_star".equals(itemEntry.getJavaIdentifier());
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkTranslator.java
deleted file mode 100644
index 8c5b74f13..000000000
--- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkTranslator.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (c) 2019-2021 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.connector.network.translators.item.translators.nbt;
-
-import com.github.steveice10.opennbt.tag.builtin.*;
-import org.geysermc.connector.network.session.GeyserSession;
-import org.geysermc.connector.network.translators.ItemRemapper;
-import org.geysermc.connector.network.translators.item.ItemEntry;
-import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
-import org.geysermc.connector.utils.FireworkColor;
-import org.geysermc.connector.utils.MathUtils;
-
-@ItemRemapper
-public class FireworkTranslator extends NbtItemStackTranslator {
-
- @Override
- public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) {
- if (!itemTag.contains("Fireworks")) {
- return;
- }
-
- CompoundTag fireworks = itemTag.get("Fireworks");
- if (fireworks.get("Flight") != null) {
- fireworks.put(new ByteTag("Flight", MathUtils.convertByte(fireworks.get("Flight").getValue())));
- }
-
- ListTag explosions = fireworks.get("Explosions");
- if (explosions == null) {
- return;
- }
- for (Tag effect : explosions.getValue()) {
- CompoundTag effectData = (CompoundTag) effect;
-
- CompoundTag newEffectData = new CompoundTag("");
-
- if (effectData.get("Type") != null) {
- newEffectData.put(new ByteTag("FireworkType", MathUtils.convertByte(effectData.get("Type").getValue())));
- }
-
- if (effectData.get("Colors") != null) {
- int[] oldColors = (int[]) effectData.get("Colors").getValue();
- byte[] colors = new byte[oldColors.length];
-
- int i = 0;
- for (int color : oldColors) {
- colors[i++] = FireworkColor.fromJavaID(color).getBedrockID();
- }
-
- newEffectData.put(new ByteArrayTag("FireworkColor", colors));
- }
-
- if (effectData.get("FadeColors") != null) {
- int[] oldColors = (int[]) effectData.get("FadeColors").getValue();
- byte[] colors = new byte[oldColors.length];
-
- int i = 0;
- for (int color : oldColors) {
- colors[i++] = FireworkColor.fromJavaID(color).getBedrockID();
- }
-
- newEffectData.put(new ByteArrayTag("FireworkFade", colors));
- }
-
- if (effectData.get("Trail") != null) {
- newEffectData.put(new ByteTag("FireworkTrail", MathUtils.convertByte(effectData.get("Trail").getValue())));
- }
-
- if (effectData.get("Flicker") != null) {
- newEffectData.put(new ByteTag("FireworkFlicker", MathUtils.convertByte(effectData.get("Flicker").getValue())));
- }
-
- explosions.remove(effect);
- explosions.add(newEffectData);
- }
- }
-
- @Override
- public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
- if (!itemTag.contains("Fireworks")) {
- return;
- }
- CompoundTag fireworks = itemTag.get("Fireworks");
- if (fireworks.contains("Flight")) {
- fireworks.put(new ByteTag("Flight", MathUtils.convertByte(fireworks.get("Flight").getValue())));
- }
-
- if (!itemTag.contains("Explosions")) {
- return;
- }
- ListTag explosions = fireworks.get("Explosions");
- for (Tag effect : explosions.getValue()) {
- CompoundTag effectData = (CompoundTag) effect;
-
- CompoundTag newEffectData = new CompoundTag("");
-
- if (effectData.get("FireworkType") != null) {
- newEffectData.put(new ByteTag("Type", MathUtils.convertByte(effectData.get("FireworkType").getValue())));
- }
-
- if (effectData.get("FireworkColor") != null) {
- byte[] oldColors = (byte[]) effectData.get("FireworkColor").getValue();
- int[] colors = new int[oldColors.length];
-
- int i = 0;
- for (byte color : oldColors) {
- colors[i++] = FireworkColor.fromBedrockID(color).getJavaID();
- }
-
- newEffectData.put(new IntArrayTag("Colors", colors));
- }
-
- if (effectData.get("FireworkFade") != null) {
- byte[] oldColors = (byte[]) effectData.get("FireworkFade").getValue();
- int[] colors = new int[oldColors.length];
-
- int i = 0;
- for (byte color : oldColors) {
- colors[i++] = FireworkColor.fromBedrockID(color).getJavaID();
- }
-
- newEffectData.put(new IntArrayTag("FadeColors", colors));
- }
-
- if (effectData.get("FireworkTrail") != null) {
- newEffectData.put(new ByteTag("Trail", MathUtils.convertByte(effectData.get("FireworkTrail").getValue())));
- }
-
- if (effectData.get("FireworkFlicker") != null) {
- newEffectData.put(new ByteTag("Flicker", MathUtils.convertByte(effectData.get("FireworkFlicker").getValue())));
- }
-
- explosions.remove(effect);
- explosions.add(newEffectData);
- }
- }
-
- @Override
- public boolean acceptItem(ItemEntry itemEntry) {
- return "minecraft:firework_rocket".equals(itemEntry.getJavaIdentifier());
- }
-}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaLoginPluginMessageTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaLoginPluginRequestTranslator.java
similarity index 92%
rename from connector/src/main/java/org/geysermc/connector/network/translators/java/JavaLoginPluginMessageTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/java/JavaLoginPluginRequestTranslator.java
index 74583b797..9680188be 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaLoginPluginMessageTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaLoginPluginRequestTranslator.java
@@ -33,10 +33,11 @@ import com.github.steveice10.mc.protocol.packet.login.client.LoginPluginResponse
import com.github.steveice10.mc.protocol.packet.login.server.LoginPluginRequestPacket;
@Translator(packet = LoginPluginRequestPacket.class)
-public class JavaLoginPluginMessageTranslator extends PacketTranslator {
+public class JavaLoginPluginRequestTranslator extends PacketTranslator {
@Override
public void translate(LoginPluginRequestPacket packet, GeyserSession session) {
// A vanilla client doesn't know any PluginMessage in the Login state, so we don't know any either.
+ // Note: Fabric Networking API v1 will not let the client log in without sending this
session.sendDownstreamPacket(
new LoginPluginResponsePacket(packet.getMessageId(), null)
);
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java
index d74165b14..371446b7e 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java
@@ -84,7 +84,7 @@ public class JavaBlockChangeTranslator extends PacketTranslator DOUBLE_CHEST_VALUES = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap FLOWER_POT_VALUES = new Int2ObjectOpenHashMap<>();
- private static final Map FLOWER_POT_BLOCKS = new HashMap<>();
private static final Int2IntMap NOTEBLOCK_PITCHES = new Int2IntOpenHashMap();
private static final Int2BooleanMap IS_STICKY_PISTON = new Int2BooleanOpenHashMap();
private static final Int2BooleanMap PISTON_VALUES = new Int2BooleanOpenHashMap();
@@ -196,15 +193,6 @@ public class BlockStateValues {
return FLOWER_POT_VALUES;
}
- /**
- * Get the map of contained flower pot plants to Bedrock CompoundTag
- *
- * @return Map of flower pot blocks.
- */
- public static Map getFlowerPotBlocks() {
- return FLOWER_POT_BLOCKS;
- }
-
/**
* The note that noteblocks output when hit is part of the block state in Java but sent as a BlockEventPacket in Bedrock.
* This gives an integer pitch that Bedrock can use.
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java
index 1d7c86a53..64bbae210 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java
@@ -34,16 +34,20 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import lombok.Getter;
import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.network.translators.world.chunk.ChunkSection;
+import org.geysermc.connector.network.translators.world.chunk.EmptyChunkProvider;
import org.geysermc.connector.utils.FileUtils;
-import org.reflections.Reflections;
import java.io.DataInputStream;
import java.io.InputStream;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import java.util.zip.GZIPInputStream;
-public class BlockTranslator {
+public abstract class BlockTranslator {
/**
* The Java block runtime ID of air
*/
@@ -51,11 +55,11 @@ public class BlockTranslator {
/**
* The Bedrock block runtime ID of air
*/
- public static final int BEDROCK_AIR_ID;
- public static final int BEDROCK_WATER_ID;
+ private final int bedrockAirId;
+ private final int bedrockWaterId;
- private static final Int2IntMap JAVA_TO_BEDROCK_BLOCK_MAP = new Int2IntOpenHashMap();
- private static final Int2IntMap BEDROCK_TO_JAVA_BLOCK_MAP = new Int2IntOpenHashMap();
+ private final Int2IntMap javaToBedrockBlockMap = new Int2IntOpenHashMap();
+ private final Int2IntMap bedrockToJavaBlockMap = new Int2IntOpenHashMap();
/**
* Stores a list of differences in block identifiers.
* Items will not be added to this list if the key and value is the same.
@@ -63,7 +67,8 @@ public class BlockTranslator {
private static final Object2ObjectMap JAVA_TO_BEDROCK_IDENTIFIERS = new Object2ObjectOpenHashMap<>();
private static final BiMap JAVA_ID_BLOCK_MAP = HashBiMap.create();
private static final IntSet WATERLOGGED = new IntOpenHashSet();
- private static final Object2IntMap ITEM_FRAMES = new Object2IntOpenHashMap<>();
+ private final Object2IntMap itemFrames = new Object2IntOpenHashMap<>();
+ private final Map flowerPotBlocks = new HashMap<>();
// Bedrock carpet ID, used in LlamaEntity.java for decoration
public static final int CARPET = 171;
@@ -85,7 +90,10 @@ public class BlockTranslator {
/**
* Runtime command block ID, used for fixing command block minecart appearances
*/
- public static final int BEDROCK_RUNTIME_COMMAND_BLOCK_ID;
+ @Getter
+ private final int bedrockRuntimeCommandBlockId;
+
+ private final EmptyChunkProvider emptyChunkProvider;
/**
* A list of all Java runtime wool IDs, for use with block breaking math and shears
@@ -98,63 +106,30 @@ public class BlockTranslator {
public static final int JAVA_RUNTIME_SPAWNER_ID;
- private static final int BLOCK_STATE_VERSION = 17825808;
+ /**
+ * Stores the raw blocks JSON until it is no longer needed.
+ */
+ public static JsonNode BLOCKS_JSON;
static {
- /* Load block palette */
- InputStream stream = FileUtils.getResource("bedrock/blockpalette.nbt");
-
- NbtList blocksTag;
- try (NBTInputStream nbtInputStream = new NBTInputStream(new DataInputStream(stream))) {
- NbtMap blockPalette = (NbtMap) nbtInputStream.readTag();
- blocksTag = (NbtList) blockPalette.getList("blocks", NbtType.COMPOUND);
- } catch (Exception e) {
- throw new AssertionError("Unable to get blocks from runtime block states", e);
- }
-
- // New since 1.16.100 - find the block runtime ID by the order given to us in the block palette,
- // as we no longer send a block palette
- Object2IntMap blockStateOrderedMap = new Object2IntOpenHashMap<>(blocksTag.size());
-
- for (int i = 0; i < blocksTag.size(); i++) {
- NbtMap tag = blocksTag.get(i);
- NbtMap blockTag = tag.getCompound("block");
- if (blockStateOrderedMap.containsKey(blockTag)) {
- throw new AssertionError("Duplicate block states in Bedrock palette");
- }
- blockStateOrderedMap.put(blockTag, i);
- }
-
- stream = FileUtils.getResource("mappings/blocks.json");
- JsonNode blocks;
+ InputStream stream = FileUtils.getResource("mappings/blocks.json");
try {
- blocks = GeyserConnector.JSON_MAPPER.readTree(stream);
+ BLOCKS_JSON = GeyserConnector.JSON_MAPPER.readTree(stream);
} catch (Exception e) {
throw new AssertionError("Unable to load Java block mappings", e);
}
- Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections("org.geysermc.connector.network.translators.world.block.entity")
- : new Reflections("org.geysermc.connector.network.translators.world.block.entity");
-
- int waterRuntimeId = -1;
int javaRuntimeId = -1;
- int airRuntimeId = -1;
int cobwebRuntimeId = -1;
- int commandBlockRuntimeId = -1;
int furnaceRuntimeId = -1;
int furnaceLitRuntimeId = -1;
int spawnerRuntimeId = -1;
int uniqueJavaId = -1;
- Iterator> blocksIterator = blocks.fields();
+ Iterator> blocksIterator = BLOCKS_JSON.fields();
while (blocksIterator.hasNext()) {
javaRuntimeId++;
Map.Entry entry = blocksIterator.next();
String javaId = entry.getKey();
- NbtMap blockTag = buildBedrockState(entry.getValue());
- int bedrockRuntimeId = blockStateOrderedMap.getOrDefault(blockTag, -1);
- if (bedrockRuntimeId == -1) {
- throw new RuntimeException("Unable to find " + javaId + " Bedrock runtime ID!");
- }
// TODO fix this, (no block should have a null hardness)
JsonNode hardnessNode = entry.getValue().get("block_hardness");
@@ -196,42 +171,17 @@ public class BlockTranslator {
String bedrockIdentifier = entry.getValue().get("bedrock_identifier").asText();
+ // Keeping this here since this is currently unchanged between versions
if (!cleanJavaIdentifier.equals(bedrockIdentifier)) {
JAVA_TO_BEDROCK_IDENTIFIERS.put(cleanJavaIdentifier, bedrockIdentifier);
}
- // Get the tag needed for non-empty flower pots
- if (entry.getValue().get("pottable") != null) {
- BlockStateValues.getFlowerPotBlocks().put(cleanJavaIdentifier, buildBedrockState(entry.getValue()));
- }
-
- if ("minecraft:water[level=0]".equals(javaId)) {
- waterRuntimeId = bedrockRuntimeId;
- }
- boolean waterlogged = entry.getKey().contains("waterlogged=true")
- || javaId.contains("minecraft:bubble_column") || javaId.contains("minecraft:kelp") || javaId.contains("seagrass");
-
- if (waterlogged) {
- BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId | 1 << 31, javaRuntimeId);
- WATERLOGGED.add(javaRuntimeId);
- } else {
- BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId, javaRuntimeId);
- }
-
- JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, bedrockRuntimeId);
-
- if (bedrockIdentifier.equals("minecraft:air")) {
- airRuntimeId = bedrockRuntimeId;
-
- } else if (javaId.contains("wool")) {
+ if (javaId.contains("wool")) {
JAVA_RUNTIME_WOOL_IDS.add(javaRuntimeId);
} else if (javaId.contains("cobweb")) {
cobwebRuntimeId = javaRuntimeId;
- } else if (javaId.equals("minecraft:command_block[conditional=false,facing=north]")) {
- commandBlockRuntimeId = bedrockRuntimeId;
-
} else if (javaId.startsWith("minecraft:furnace[facing=north")) {
if (javaId.contains("lit=true")) {
furnaceLitRuntimeId = javaRuntimeId;
@@ -249,11 +199,6 @@ public class BlockTranslator {
}
JAVA_RUNTIME_COBWEB_ID = cobwebRuntimeId;
- if (commandBlockRuntimeId == -1) {
- throw new AssertionError("Unable to find command block in palette");
- }
- BEDROCK_RUNTIME_COMMAND_BLOCK_ID = commandBlockRuntimeId;
-
if (furnaceRuntimeId == -1) {
throw new AssertionError("Unable to find furnace in palette");
}
@@ -269,35 +214,117 @@ public class BlockTranslator {
}
JAVA_RUNTIME_SPAWNER_ID = spawnerRuntimeId;
+ BlockTranslator1_16_100.init();
+ BlockTranslator1_16_210.init();
+ BLOCKS_JSON = null; // We no longer require this so let it garbage collect away
+ }
+
+ public BlockTranslator(String paletteFile) {
+ /* Load block palette */
+ InputStream stream = FileUtils.getResource(paletteFile);
+
+ NbtList blocksTag;
+ try (NBTInputStream nbtInputStream = new NBTInputStream(new DataInputStream(new GZIPInputStream(stream)))) {
+ NbtMap blockPalette = (NbtMap) nbtInputStream.readTag();
+ blocksTag = (NbtList) blockPalette.getList("blocks", NbtType.COMPOUND);
+ } catch (Exception e) {
+ throw new AssertionError("Unable to get blocks from runtime block states", e);
+ }
+
+ // New since 1.16.100 - find the block runtime ID by the order given to us in the block palette,
+ // as we no longer send a block palette
+ Object2IntMap blockStateOrderedMap = new Object2IntOpenHashMap<>(blocksTag.size());
+
+ for (int i = 0; i < blocksTag.size(); i++) {
+ NbtMap tag = blocksTag.get(i);
+ if (blockStateOrderedMap.containsKey(tag)) {
+ throw new AssertionError("Duplicate block states in Bedrock palette: " + tag);
+ }
+ blockStateOrderedMap.put(tag, i);
+ }
+
+ int airRuntimeId = -1;
+ int commandBlockRuntimeId = -1;
+ int javaRuntimeId = -1;
+ int waterRuntimeId = -1;
+ Iterator> blocksIterator = BLOCKS_JSON.fields();
+ while (blocksIterator.hasNext()) {
+ javaRuntimeId++;
+ Map.Entry entry = blocksIterator.next();
+ String javaId = entry.getKey();
+
+ NbtMap blockTag = buildBedrockState(entry.getValue());
+ int bedrockRuntimeId = blockStateOrderedMap.getOrDefault(blockTag, -1);
+ if (bedrockRuntimeId == -1) {
+ throw new RuntimeException("Unable to find " + javaId + " Bedrock runtime ID! Built compound tag: \n" + blockTag);
+ }
+
+ switch (javaId) {
+ case "minecraft:air":
+ airRuntimeId = bedrockRuntimeId;
+ break;
+ case "minecraft:water[level=0]":
+ waterRuntimeId = bedrockRuntimeId;
+ break;
+ case "minecraft:command_block[conditional=false,facing=north]":
+ commandBlockRuntimeId = bedrockRuntimeId;
+ break;
+ }
+
+ boolean waterlogged = entry.getKey().contains("waterlogged=true")
+ || javaId.contains("minecraft:bubble_column") || javaId.contains("minecraft:kelp") || javaId.contains("seagrass");
+
+ if (waterlogged) {
+ bedrockToJavaBlockMap.putIfAbsent(bedrockRuntimeId | 1 << 31, javaRuntimeId);
+ WATERLOGGED.add(javaRuntimeId);
+ } else {
+ bedrockToJavaBlockMap.putIfAbsent(bedrockRuntimeId, javaRuntimeId);
+ }
+
+ String cleanJavaIdentifier = entry.getKey().split("\\[")[0];
+
+ // Get the tag needed for non-empty flower pots
+ if (entry.getValue().get("pottable") != null) {
+ flowerPotBlocks.put(cleanJavaIdentifier, buildBedrockState(entry.getValue()));
+ }
+
+ javaToBedrockBlockMap.put(javaRuntimeId, bedrockRuntimeId);
+ }
+
+ if (commandBlockRuntimeId == -1) {
+ throw new AssertionError("Unable to find command block in palette");
+ }
+ bedrockRuntimeCommandBlockId = commandBlockRuntimeId;
+
if (waterRuntimeId == -1) {
throw new AssertionError("Unable to find water in palette");
}
- BEDROCK_WATER_ID = waterRuntimeId;
+ bedrockWaterId = waterRuntimeId;
if (airRuntimeId == -1) {
throw new AssertionError("Unable to find air in palette");
}
- BEDROCK_AIR_ID = airRuntimeId;
+ bedrockAirId = airRuntimeId;
// Loop around again to find all item frame runtime IDs
for (Object2IntMap.Entry entry : blockStateOrderedMap.object2IntEntrySet()) {
if (entry.getKey().getString("name").equals("minecraft:frame")) {
- ITEM_FRAMES.put(entry.getKey(), entry.getIntValue());
+ itemFrames.put(entry.getKey(), entry.getIntValue());
}
}
- }
- private BlockTranslator() {
+ this.emptyChunkProvider = new EmptyChunkProvider(bedrockAirId);
}
public static void init() {
// no-op
}
- private static NbtMap buildBedrockState(JsonNode node) {
+ private NbtMap buildBedrockState(JsonNode node) {
NbtMapBuilder tagBuilder = NbtMap.builder();
- tagBuilder.putString("name", node.get("bedrock_identifier").textValue())
- .putInt("version", BlockTranslator.BLOCK_STATE_VERSION);
+ String bedrockIdentifier = node.get("bedrock_identifier").textValue();
+ tagBuilder.putString("name", bedrockIdentifier)
+ .putInt("version", getBlockStateVersion());
NbtMapBuilder statesBuilder = NbtMap.builder();
@@ -320,36 +347,67 @@ public class BlockTranslator {
}
}
}
- tagBuilder.put("states", statesBuilder.build());
+ tagBuilder.put("states", adjustBlockStateForVersion(bedrockIdentifier, statesBuilder).build());
return tagBuilder.build();
}
- public static int getBedrockBlockId(int state) {
- return JAVA_TO_BEDROCK_BLOCK_MAP.get(state);
+ /**
+ * @return an adjusted state list, if necessary, that converts Geyser's new mapping to Bedrock's older version
+ * of the mapping.
+ */
+ protected NbtMapBuilder adjustBlockStateForVersion(String bedrockIdentifier, NbtMapBuilder statesBuilder) {
+ return statesBuilder;
}
- public static int getJavaBlockState(int bedrockId) {
- return BEDROCK_TO_JAVA_BLOCK_MAP.get(bedrockId);
+ public int getBedrockBlockId(int state) {
+ return javaToBedrockBlockMap.get(state);
+ }
+
+ public int getJavaBlockState(int bedrockId) {
+ return bedrockToJavaBlockMap.get(bedrockId);
}
/**
* @param javaIdentifier the Java identifier of the block to search for
* @return the Bedrock identifier if different, or else the Java identifier
*/
- public static String getBedrockBlockIdentifier(String javaIdentifier) {
+ public String getBedrockBlockIdentifier(String javaIdentifier) {
return JAVA_TO_BEDROCK_IDENTIFIERS.getOrDefault(javaIdentifier, javaIdentifier);
}
- public static int getItemFrame(NbtMap tag) {
- return ITEM_FRAMES.getOrDefault(tag, -1);
+ public int getItemFrame(NbtMap tag) {
+ return itemFrames.getOrDefault(tag, -1);
}
- public static boolean isItemFrame(int bedrockBlockRuntimeId) {
- return ITEM_FRAMES.values().contains(bedrockBlockRuntimeId);
+ public boolean isItemFrame(int bedrockBlockRuntimeId) {
+ return itemFrames.values().contains(bedrockBlockRuntimeId);
}
- public static int getBlockStateVersion() {
- return BLOCK_STATE_VERSION;
+ /**
+ * Get the map of contained flower pot plants to Bedrock CompoundTag
+ *
+ * @return Map of flower pot blocks.
+ */
+ public Map getFlowerPotBlocks() {
+ return flowerPotBlocks;
+ }
+
+ public int getBedrockAirId() {
+ return bedrockAirId;
+ }
+
+ public int getBedrockWaterId() {
+ return bedrockWaterId;
+ }
+
+ public abstract int getBlockStateVersion();
+
+ public byte[] getEmptyChunkData() {
+ return emptyChunkProvider.getEmptyLevelChunkData();
+ }
+
+ public ChunkSection getEmptyChunkSection() {
+ return emptyChunkProvider.getEmptySection();
}
/**
@@ -368,10 +426,6 @@ public class BlockTranslator {
return JAVA_ID_BLOCK_MAP;
}
- public static int getJavaWaterloggedState(int bedrockId) {
- return BEDROCK_TO_JAVA_BLOCK_MAP.get(1 << 31 | bedrockId);
- }
-
/**
* Get the item a Java client would receive when pressing
* the Pick Block key on a specific Java block state.
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_16_100.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_16_100.java
new file mode 100644
index 000000000..e10a503ea
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_16_100.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019-2021 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.connector.network.translators.world.block;
+
+import com.google.common.collect.ImmutableSet;
+import com.nukkitx.nbt.NbtMapBuilder;
+
+import java.util.Set;
+
+public class BlockTranslator1_16_100 extends BlockTranslator {
+ private static final Set CORRECTED_STATES = ImmutableSet.of("minecraft:stripped_warped_stem",
+ "minecraft:stripped_warped_hyphae", "minecraft:stripped_crimson_stem", "minecraft:stripped_crimson_hyphae");
+
+ public static final BlockTranslator1_16_100 INSTANCE = new BlockTranslator1_16_100();
+
+ public BlockTranslator1_16_100() {
+ super("bedrock/blockpalette.1_16_100.nbt");
+ }
+
+ @Override
+ public int getBlockStateVersion() {
+ return 17825808;
+ }
+
+ @Override
+ protected NbtMapBuilder adjustBlockStateForVersion(String bedrockIdentifier, NbtMapBuilder statesBuilder) {
+ if (CORRECTED_STATES.contains(bedrockIdentifier)) {
+ statesBuilder.putInt("deprecated", 0);
+ }
+ return super.adjustBlockStateForVersion(bedrockIdentifier, statesBuilder);
+ }
+
+ public static void init() {
+ // no-op
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_16_210.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_16_210.java
new file mode 100644
index 000000000..58861cb9c
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_16_210.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019-2021 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.connector.network.translators.world.block;
+
+public class BlockTranslator1_16_210 extends BlockTranslator {
+ public static final BlockTranslator1_16_210 INSTANCE = new BlockTranslator1_16_210();
+
+ public BlockTranslator1_16_210() {
+ super("bedrock/blockpalette.1_16_210.nbt");
+ }
+
+ @Override
+ public int getBlockStateVersion() {
+ return 17879555;
+ }
+
+ public static void init() {
+ // no-op
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedrockOnlyBlockEntity.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedrockOnlyBlockEntity.java
index e05fcc67b..2417f1e6e 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedrockOnlyBlockEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedrockOnlyBlockEntity.java
@@ -47,9 +47,9 @@ public interface BedrockOnlyBlockEntity {
* @param blockState Java BlockState of block.
* @return Bedrock tag, or null if not a Bedrock-only Block Entity
*/
- static NbtMap getTag(Vector3i position, int blockState) {
+ static NbtMap getTag(GeyserSession session, Vector3i position, int blockState) {
if (FlowerPotBlockEntityTranslator.isFlowerBlock(blockState)) {
- return FlowerPotBlockEntityTranslator.getTag(blockState, position);
+ return FlowerPotBlockEntityTranslator.getTag(session, blockState, position);
} else if (PistonBlockEntityTranslator.isBlock(blockState)) {
return PistonBlockEntityTranslator.getTag(blockState, position);
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/FlowerPotBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/FlowerPotBlockEntityTranslator.java
index 9eebe37d7..062fd4922 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/FlowerPotBlockEntityTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/FlowerPotBlockEntityTranslator.java
@@ -31,7 +31,6 @@ import com.nukkitx.nbt.NbtMapBuilder;
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
-import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.BlockEntityUtils;
public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity, RequiresBlockState {
@@ -50,7 +49,7 @@ public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity, R
* @param position Bedrock position of flower pot.
* @return Bedrock tag of flower pot.
*/
- public static NbtMap getTag(int blockState, Vector3i position) {
+ public static NbtMap getTag(GeyserSession session, int blockState, Vector3i position) {
NbtMapBuilder tagBuilder = NbtMap.builder()
.putInt("x", position.getX())
.putInt("y", position.getY())
@@ -62,7 +61,7 @@ public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity, R
if (name != null) {
// Get the Bedrock CompoundTag of the block.
// This is where we need to store the *Java* name because Bedrock has six minecraft:sapling blocks with different block states.
- NbtMap plant = BlockStateValues.getFlowerPotBlocks().get(name);
+ NbtMap plant = session.getBlockTranslator().getFlowerPotBlocks().get(name);
if (plant != null) {
tagBuilder.put("PlantBlock", plant.toBuilder().build());
}
@@ -77,15 +76,16 @@ public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity, R
@Override
public void updateBlock(GeyserSession session, int blockState, Vector3i position) {
- BlockEntityUtils.updateBlockEntity(session, getTag(blockState, position), position);
+ NbtMap tag = getTag(session, blockState, position);
+ BlockEntityUtils.updateBlockEntity(session, tag, position);
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setDataLayer(0);
- updateBlockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(blockState));
+ updateBlockPacket.setRuntimeId(session.getBlockTranslator().getBedrockBlockId(blockState));
updateBlockPacket.setBlockPosition(position);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY);
session.sendUpstreamPacket(updateBlockPacket);
- BlockEntityUtils.updateBlockEntity(session, getTag(blockState, position), position);
+ BlockEntityUtils.updateBlockEntity(session, tag, position);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java
index 7a5086241..672fa1a35 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java
@@ -30,7 +30,6 @@ import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import lombok.Getter;
-import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.network.translators.world.chunk.bitarray.BitArray;
import org.geysermc.connector.network.translators.world.chunk.bitarray.BitArrayVersion;
@@ -44,14 +43,14 @@ public class BlockStorage {
private final IntList palette;
private BitArray bitArray;
- public BlockStorage() {
- this(BitArrayVersion.V2);
+ public BlockStorage(int airBlockId) {
+ this(airBlockId, BitArrayVersion.V2);
}
- public BlockStorage(BitArrayVersion version) {
+ public BlockStorage(int airBlockId, BitArrayVersion version) {
this.bitArray = version.createArray(SIZE);
this.palette = new IntArrayList(16);
- this.palette.add(BlockTranslator.BEDROCK_AIR_ID); // Air is at the start of every palette and controls what the default block is in second-layer non-air block spaces.
+ this.palette.add(airBlockId); // Air is at the start of every palette and controls what the default block is in second-layer non-air block spaces.
}
public BlockStorage(BitArray bitArray, IntList palette) {
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkSection.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkSection.java
index 2709e3e23..53528d654 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkSection.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkSection.java
@@ -34,8 +34,8 @@ public class ChunkSection {
private final BlockStorage[] storage;
- public ChunkSection() {
- this(new BlockStorage[]{new BlockStorage(), new BlockStorage()});
+ public ChunkSection(int airBlockId) {
+ this(new BlockStorage[]{new BlockStorage(airBlockId), new BlockStorage(airBlockId)});
}
public ChunkSection(BlockStorage[] storage) {
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/EmptyChunkProvider.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/EmptyChunkProvider.java
new file mode 100644
index 000000000..1bc7d684f
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/EmptyChunkProvider.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019-2021 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.connector.network.translators.world.chunk;
+
+import com.nukkitx.nbt.NBTOutputStream;
+import com.nukkitx.nbt.NbtMap;
+import com.nukkitx.nbt.NbtUtils;
+import lombok.Getter;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+public class EmptyChunkProvider {
+ @Getter
+ private final byte[] emptyLevelChunkData;
+ @Getter
+ private final ChunkSection emptySection;
+
+ public EmptyChunkProvider(int airId) {
+ BlockStorage emptyStorage = new BlockStorage(airId);
+ emptySection = new ChunkSection(new BlockStorage[]{emptyStorage});
+
+ try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+ outputStream.write(new byte[258]); // Biomes + Border Size + Extra Data Size
+
+ try (NBTOutputStream stream = NbtUtils.createNetworkWriter(outputStream)) {
+ stream.writeTag(NbtMap.EMPTY);
+ }
+
+ emptyLevelChunkData = outputStream.toByteArray();
+ } catch (IOException e) {
+ throw new AssertionError("Unable to generate empty level chunk data");
+ }
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/skin/SkinProvider.java b/connector/src/main/java/org/geysermc/connector/skin/SkinProvider.java
index 96e390e75..6574c9350 100644
--- a/connector/src/main/java/org/geysermc/connector/skin/SkinProvider.java
+++ b/connector/src/main/java/org/geysermc/connector/skin/SkinProvider.java
@@ -412,15 +412,32 @@ public class SkinProvider {
// if the requested image is a cape
if (provider != null) {
- if (image.getWidth() > 64) {
- image = scale(image, 64, 32);
+ if (image.getWidth() > 64 || image.getHeight() > 32) {
+ // Prevent weirdly-scaled capes from being cut off
+ BufferedImage newImage = new BufferedImage(128, 64, BufferedImage.TYPE_INT_ARGB);
+ Graphics g = newImage.createGraphics();
+ g.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);
+ g.dispose();
+ image.flush();
+ image = scale(newImage, 64, 32);
+ } else if (image.getWidth() < 64 || image.getHeight() < 32) {
+ // Bedrock doesn't like smaller-sized capes, either.
+ BufferedImage newImage = new BufferedImage(64, 32, BufferedImage.TYPE_INT_ARGB);
+ Graphics g = newImage.createGraphics();
+ g.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);
+ g.dispose();
+ image.flush();
+ image = newImage;
}
} else {
// Very rarely, skins can be larger than Minecraft's default.
// Bedrock will not render anything above a width of 128.
if (image.getWidth() > 128) {
- image = scale(image, 128, image.getHeight() / (image.getWidth() / 128));
+ // On Height: Scale by the amount we divided width by, or simply cut down to 128
+ image = scale(image, 128, image.getHeight() >= 256 ? (image.getHeight() / (image.getWidth() / 128)) : 128);
}
+
+ // TODO remove alpha channel
}
byte[] data = bufferedImageToImageData(image);
diff --git a/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java
index c859b9f65..07fe9744d 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java
@@ -132,7 +132,7 @@ public class BlockUtils {
miningFatigueLevel = session.getEffectCache().getEffectLevel(Effect.SLOWER_DIG);
boolean isInWater = session.getConnector().getConfig().isCacheChunks()
- && BlockTranslator.getBedrockBlockId(session.getConnector().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt())) == BlockTranslator.BEDROCK_WATER_ID;
+ && session.getBlockTranslator().getBedrockBlockId(session.getConnector().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt())) == session.getBlockTranslator().getBedrockWaterId();
boolean insideOfWaterWithoutAquaAffinity = isInWater &&
ItemUtils.getEnchantmentLevel(Optional.ofNullable(session.getInventory().getItem(5)).map(ItemStack::getNbt).orElse(null), "minecraft:aqua_affinity") < 1;
diff --git a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java
index e5e3f36d7..65c15eb21 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java
@@ -36,9 +36,7 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.nukkitx.math.vector.Vector2i;
import com.nukkitx.math.vector.Vector3i;
-import com.nukkitx.nbt.NBTOutputStream;
import com.nukkitx.nbt.NbtMap;
-import com.nukkitx.nbt.NbtUtils;
import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket;
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
@@ -64,13 +62,11 @@ import org.geysermc.connector.network.translators.world.chunk.ChunkSection;
import org.geysermc.connector.network.translators.world.chunk.bitarray.BitArray;
import org.geysermc.connector.network.translators.world.chunk.bitarray.BitArrayVersion;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
-import static org.geysermc.connector.network.translators.world.block.BlockTranslator.*;
+import static org.geysermc.connector.network.translators.world.block.BlockTranslator.JAVA_AIR_ID;
@UtilityClass
public class ChunkUtils {
@@ -80,26 +76,6 @@ public class ChunkUtils {
*/
public static final Object2IntMap CACHED_BLOCK_ENTITIES = new Object2IntOpenHashMap<>();
- private static final NbtMap EMPTY_TAG = NbtMap.builder().build();
- public static final byte[] EMPTY_LEVEL_CHUNK_DATA;
-
- public static final BlockStorage EMPTY_STORAGE = new BlockStorage();
- public static final ChunkSection EMPTY_SECTION = new ChunkSection(new BlockStorage[]{ EMPTY_STORAGE });
-
- static {
- try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
- outputStream.write(new byte[258]); // Biomes + Border Size + Extra Data Size
-
- try (NBTOutputStream stream = NbtUtils.createNetworkWriter(outputStream)) {
- stream.writeTag(EMPTY_TAG);
- }
-
- EMPTY_LEVEL_CHUNK_DATA = outputStream.toByteArray();
- } catch (IOException e) {
- throw new AssertionError("Unable to generate empty level chunk data");
- }
- }
-
private static int indexYZXtoXZY(int yzx) {
return (yzx >> 8) | (yzx & 0x0F0) | ((yzx & 0x00F) << 8);
}
@@ -161,20 +137,20 @@ public class ChunkUtils {
if (javaPalette instanceof GlobalPalette) {
// As this is the global palette, simply iterate through the whole chunk section once
- ChunkSection section = new ChunkSection();
+ ChunkSection section = new ChunkSection(session.getBlockTranslator().getBedrockAirId());
for (int yzx = 0; yzx < BlockStorage.SIZE; yzx++) {
int javaId = javaData.get(yzx);
- int bedrockId = BlockTranslator.getBedrockBlockId(javaId);
+ int bedrockId = session.getBlockTranslator().getBedrockBlockId(javaId);
int xzy = indexYZXtoXZY(yzx);
section.getBlockStorageArray()[0].setFullBlock(xzy, bedrockId);
if (BlockTranslator.isWaterlogged(javaId)) {
- section.getBlockStorageArray()[1].setFullBlock(xzy, BEDROCK_WATER_ID);
+ section.getBlockStorageArray()[1].setFullBlock(xzy, session.getBlockTranslator().getBedrockWaterId());
}
// Check if block is piston or flower to see if we'll need to create additional block entities, as they're only block entities in Bedrock
if (BlockStateValues.getFlowerPotValues().containsKey(javaId) || BlockStateValues.getPistonValues().containsKey(javaId)) {
- bedrockOnlyBlockEntities.add(BedrockOnlyBlockEntity.getTag(
+ bedrockOnlyBlockEntities.add(BedrockOnlyBlockEntity.getTag(session,
Vector3i.from((column.getX() << 4) + (yzx & 0xF), (sectionY << 4) + ((yzx >> 8) & 0xF), (column.getZ() << 4) + ((yzx >> 4) & 0xF)),
javaId
));
@@ -191,7 +167,7 @@ public class ChunkUtils {
// Iterate through palette and convert state IDs to Bedrock, doing some additional checks as we go
for (int i = 0; i < javaPalette.size(); i++) {
int javaId = javaPalette.idToState(i);
- bedrockPalette.add(BlockTranslator.getBedrockBlockId(javaId));
+ bedrockPalette.add(session.getBlockTranslator().getBedrockBlockId(javaId));
if (BlockTranslator.isWaterlogged(javaId)) {
waterloggedPaletteIds.set(i);
@@ -210,7 +186,7 @@ public class ChunkUtils {
for (int yzx = 0; yzx < BlockStorage.SIZE; yzx++) {
int paletteId = javaData.get(yzx);
if (pistonOrFlowerPaletteIds.get(paletteId)) {
- bedrockOnlyBlockEntities.add(BedrockOnlyBlockEntity.getTag(
+ bedrockOnlyBlockEntities.add(BedrockOnlyBlockEntity.getTag(session,
Vector3i.from((column.getX() << 4) + (yzx & 0xF), (sectionY << 4) + ((yzx >> 8) & 0xF), (column.getZ() << 4) + ((yzx >> 4) & 0xF)),
javaPalette.idToState(paletteId)
));
@@ -247,8 +223,8 @@ public class ChunkUtils {
// V1 palette
IntList layer1Palette = new IntArrayList(2);
- layer1Palette.add(BEDROCK_AIR_ID); // Air - see BlockStorage's constructor for more information
- layer1Palette.add(BEDROCK_WATER_ID);
+ layer1Palette.add(session.getBlockTranslator().getBedrockAirId()); // Air - see BlockStorage's constructor for more information
+ layer1Palette.add(session.getBlockTranslator().getBedrockWaterId());
layers = new BlockStorage[]{ layer0, new BlockStorage(BitArrayVersion.V1.createArray(BlockStorage.SIZE, layer1Data), layer1Palette) };
}
@@ -368,7 +344,7 @@ public class ChunkUtils {
skull.despawnEntity(session, position);
}
- int blockId = BlockTranslator.getBedrockBlockId(blockState);
+ int blockId = session.getBlockTranslator().getBedrockBlockId(blockState);
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setDataLayer(0);
@@ -382,9 +358,9 @@ public class ChunkUtils {
waterPacket.setDataLayer(1);
waterPacket.setBlockPosition(position);
if (BlockTranslator.isWaterlogged(blockState)) {
- waterPacket.setRuntimeId(BEDROCK_WATER_ID);
+ waterPacket.setRuntimeId(session.getBlockTranslator().getBedrockWaterId());
} else {
- waterPacket.setRuntimeId(BEDROCK_AIR_ID);
+ waterPacket.setRuntimeId(session.getBlockTranslator().getBedrockAirId());
}
session.sendUpstreamPacket(waterPacket);
@@ -417,7 +393,7 @@ public class ChunkUtils {
data.setChunkX(chunkX + x);
data.setChunkZ(chunkZ + z);
data.setSubChunksLength(0);
- data.setData(EMPTY_LEVEL_CHUNK_DATA);
+ data.setData(session.getBlockTranslator().getEmptyChunkData());
data.setCachingEnabled(false);
session.sendUpstreamPacket(data);
diff --git a/connector/src/main/resources/bedrock/blockpalette.1_16_100.nbt b/connector/src/main/resources/bedrock/blockpalette.1_16_100.nbt
new file mode 100644
index 000000000..4513be031
Binary files /dev/null and b/connector/src/main/resources/bedrock/blockpalette.1_16_100.nbt differ
diff --git a/connector/src/main/resources/bedrock/blockpalette.1_16_210.nbt b/connector/src/main/resources/bedrock/blockpalette.1_16_210.nbt
new file mode 100644
index 000000000..178a370ed
Binary files /dev/null and b/connector/src/main/resources/bedrock/blockpalette.1_16_210.nbt differ
diff --git a/connector/src/main/resources/bedrock/blockpalette.nbt b/connector/src/main/resources/bedrock/blockpalette.nbt
deleted file mode 100644
index b92a06260..000000000
Binary files a/connector/src/main/resources/bedrock/blockpalette.nbt and /dev/null differ
diff --git a/connector/src/main/resources/languages b/connector/src/main/resources/languages
index bffb5617c..3d3b60de7 160000
--- a/connector/src/main/resources/languages
+++ b/connector/src/main/resources/languages
@@ -1 +1 @@
-Subproject commit bffb5617c1ecdacc10031c6ec36988a5f04cb5c6
+Subproject commit 3d3b60de724f3f552f351c5f400269fde7598b67
diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings
index bf0610450..ef994d8fe 160000
--- a/connector/src/main/resources/mappings
+++ b/connector/src/main/resources/mappings
@@ -1 +1 @@
-Subproject commit bf0610450ce94507a18286e94af2965550ff9eaa
+Subproject commit ef994d8fea421524cbc11f565a3f5ec59fc05741