Support Java 1.20.2 (#4112)

* Initial pass for 1.20.2, compiling
* Remove unused level events
* handle null GameProfile in ClientboundPlayerInfoUpdatePacket
* Handle level events BRUSH_BLOCK_COMPLETE and EGG_CRACK
* Account for null tag in DecoratedPotBlockEntityTranslator
* Explicitly show that 1.20.31 is supported
This commit is contained in:
Konicai 2023-09-29 17:14:49 -04:00 committed by GitHub
parent 5276a1e2ba
commit c2cd99c829
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 243 additions and 167 deletions

View file

@ -14,7 +14,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 joined us here!
### Currently supporting Minecraft Bedrock 1.20.0 - 1.20.30 and Minecraft Java 1.20/1.20.1.
### Currently supporting Minecraft Bedrock 1.20.0 - 1.20.31 and Minecraft Java 1.20/1.20.1.
## Setting Up
Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.

View file

@ -71,7 +71,6 @@ import org.geysermc.geyser.level.WorldManager;
import org.geysermc.geyser.network.netty.GeyserServer;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.loader.RegistryLoaders;
import org.geysermc.geyser.scoreboard.ScoreboardUpdater;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.PendingMicrosoftAuthentication;

View file

@ -52,11 +52,6 @@ public class GeyserAdvancement {
return this.advancement.getId();
}
@NonNull
public List<String> getCriteria() {
return this.advancement.getCriteria();
}
@NonNull
public List<List<String>> getRequirements() {
return this.advancement.getRequirements();

View file

@ -46,7 +46,9 @@ public final class GameProtocol {
* Default Bedrock codec that should act as a fallback. Should represent the latest available
* release of the game that Geyser supports.
*/
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = Bedrock_v618.CODEC;
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = Bedrock_v618.CODEC.toBuilder()
.minecraftVersion("1.20.31")
.build();
/**
* A list of all supported Bedrock versions that can join Geyser
@ -66,7 +68,9 @@ public final class GameProtocol {
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v594.CODEC.toBuilder()
.minecraftVersion("1.20.10/1.20.15")
.build());
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC);
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder()
.minecraftVersion("1.20.30/1.20.31")
.build());
}
/**

View file

@ -27,6 +27,7 @@ package org.geysermc.geyser.registry;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundDelimiterPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundTabListPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundChunkBatchStartPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundLightUpdatePacket;
import io.netty.channel.EventLoop;
import org.geysermc.geyser.GeyserImpl;
@ -43,9 +44,10 @@ public class PacketTranslatorRegistry<T> extends AbstractMappedRegistry<Class<?
private static final Set<Class<?>> IGNORED_PACKETS = Collections.newSetFromMap(new IdentityHashMap<>());
static {
IGNORED_PACKETS.add(ClientboundChunkBatchStartPacket.class); // we don't track chunk batch sizes/periods
IGNORED_PACKETS.add(ClientboundDelimiterPacket.class); // Not implemented, spams logs
IGNORED_PACKETS.add(ClientboundLightUpdatePacket.class); // Light is handled on Bedrock for us
IGNORED_PACKETS.add(ClientboundTabListPacket.class); // Cant be implemented in Bedrock
IGNORED_PACKETS.add(ClientboundDelimiterPacket.class); // Not implemented, spams logs
}
protected PacketTranslatorRegistry() {

View file

@ -45,15 +45,15 @@ import com.github.steveice10.mc.protocol.data.game.setting.ChatVisibility;
import com.github.steveice10.mc.protocol.data.game.setting.SkinPart;
import com.github.steveice10.mc.protocol.data.game.statistic.CustomStatistic;
import com.github.steveice10.mc.protocol.data.game.statistic.Statistic;
import com.github.steveice10.mc.protocol.packet.common.serverbound.ServerboundClientInformationPacket;
import com.github.steveice10.mc.protocol.packet.handshake.serverbound.ClientIntentionPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundChatCommandPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundChatPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundClientInformationPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerAbilitiesPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundUseItemPacket;
import com.github.steveice10.mc.protocol.packet.login.serverbound.ServerboundCustomQueryPacket;
import com.github.steveice10.mc.protocol.packet.login.serverbound.ServerboundCustomQueryAnswerPacket;
import com.github.steveice10.packetlib.BuiltinFlags;
import com.github.steveice10.packetlib.Session;
import com.github.steveice10.packetlib.event.session.ConnectedEvent;
@ -292,7 +292,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
private Vector2i lastChunkPosition = null;
@Setter
private int clientRenderDistance = -1;
private int serverRenderDistance;
private int serverRenderDistance = -1;
// Exposed for GeyserConnect usage
protected boolean sentSpawnPacket;
@ -1665,7 +1665,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
}
private void sendDownstreamPacket0(Packet packet) {
if (protocol.getState().equals(ProtocolState.GAME) || packet.getClass() == ServerboundCustomQueryPacket.class) {
ProtocolState state = protocol.getState();
if (state == ProtocolState.GAME || state == ProtocolState.CONFIGURATION || packet.getClass() == ServerboundCustomQueryAnswerPacket.class) {
downstream.sendPacket(packet);
} else {
geyser.getLogger().debug("Tried to send downstream packet " + packet.getClass().getSimpleName() + " before connected to the server");
@ -1824,9 +1825,12 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
if (clientRenderDistance != -1) {
// The client has sent a render distance
return clientRenderDistance;
}
} else if (serverRenderDistance != -1) {
// only known once ClientboundLoginPacket is received
return serverRenderDistance;
}
return 2; // unfortunate default until we got more info
}
// We need to send our skin parts to the server otherwise java sees us with no hat, jacket etc
private static final List<SkinPart> SKIN_PARTS = Arrays.asList(SkinPart.values());

View file

@ -25,9 +25,10 @@
package org.geysermc.geyser.session.cache;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundUpdateTagsPacket;
import com.github.steveice10.mc.protocol.packet.common.clientbound.ClientboundUpdateTagsPacket;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntLists;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.type.Item;
@ -112,7 +113,7 @@ public class TagCache {
}
}
private IntList load(int[] tags) {
private IntList load(int @Nullable[] tags) {
if (tags == null) {
return IntLists.EMPTY_LIST;
}

View file

@ -41,6 +41,10 @@ public class DecoratedPotBlockEntityTranslator extends BlockEntityTranslator {
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
if (tag == null) {
return;
}
// exact same format
if (tag.get("sherds") instanceof ListTag sherds) {
List<String> translated = new ArrayList<>(4);

View file

@ -25,7 +25,7 @@
package org.geysermc.geyser.translator.protocol.bedrock;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundKeepAlivePacket;
import com.github.steveice10.mc.protocol.packet.common.serverbound.ServerboundKeepAlivePacket;
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
import org.cloudburstmc.protocol.bedrock.packet.NetworkStackLatencyPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;

View file

@ -25,8 +25,8 @@
package org.geysermc.geyser.translator.protocol.java;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundCustomPayloadPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundCustomPayloadPacket;
import com.github.steveice10.mc.protocol.packet.common.clientbound.ClientboundCustomPayloadPacket;
import com.github.steveice10.mc.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket;
import com.google.common.base.Charsets;
import org.cloudburstmc.protocol.bedrock.packet.TransferPacket;
import org.cloudburstmc.protocol.bedrock.packet.UnknownPacket;

View file

@ -26,7 +26,7 @@
package org.geysermc.geyser.translator.protocol.java;
import com.github.steveice10.mc.protocol.packet.login.clientbound.ClientboundCustomQueryPacket;
import com.github.steveice10.mc.protocol.packet.login.serverbound.ServerboundCustomQueryPacket;
import com.github.steveice10.mc.protocol.packet.login.serverbound.ServerboundCustomQueryAnswerPacket;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
@ -41,7 +41,7 @@ public class JavaCustomQueryTranslator extends PacketTranslator<ClientboundCusto
// 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 ServerboundCustomQueryPacket(packet.getMessageId(), null)
new ServerboundCustomQueryAnswerPacket(packet.getMessageId(), null)
);
}
}

View file

@ -25,7 +25,7 @@
package org.geysermc.geyser.translator.protocol.java;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundDisconnectPacket;
import com.github.steveice10.mc.protocol.packet.common.clientbound.ClientboundDisconnectPacket;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;

View file

@ -26,6 +26,7 @@
package org.geysermc.geyser.translator.protocol.java;
import com.github.steveice10.mc.auth.data.GameProfile;
import com.github.steveice10.mc.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket;
import com.github.steveice10.mc.protocol.packet.login.clientbound.ClientboundGameProfilePacket;
import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.entity.type.player.PlayerEntity;
@ -33,7 +34,11 @@ import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.skin.SkinManager;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.PluginMessageUtils;
/**
* ClientboundGameProfilePacket triggers protocol change LOGIN -> CONFIGURATION
*/
@Translator(packet = ClientboundGameProfilePacket.class)
public class JavaGameProfileTranslator extends PacketTranslator<ClientboundGameProfilePacket> {
@ -65,5 +70,9 @@ public class JavaGameProfileTranslator extends PacketTranslator<ClientboundGameP
// We no longer need these variables; they're just taking up space in memory now
session.setCertChainData(null);
session.getClientData().setOriginalString(null);
// configuration phase stuff that the vanilla client replies with after receiving the GameProfilePacket
session.sendDownstreamPacket(new ServerboundCustomPayloadPacket("minecraft:brand", PluginMessageUtils.getGeyserBrandData()));
session.sendJavaClientSettings();
}
}

View file

@ -25,7 +25,7 @@
package org.geysermc.geyser.translator.protocol.java;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundKeepAlivePacket;
import com.github.steveice10.mc.protocol.packet.common.clientbound.ClientboundKeepAlivePacket;
import org.cloudburstmc.protocol.bedrock.packet.NetworkStackLatencyPacket;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;

View file

@ -25,31 +25,22 @@
package org.geysermc.geyser.translator.protocol.java;
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerSpawnInfo;
import com.github.steveice10.mc.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundLoginPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundCustomPayloadPacket;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntTag;
import org.cloudburstmc.protocol.bedrock.data.GameRuleData;
import org.cloudburstmc.protocol.bedrock.packet.GameRulesChangedPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetPlayerGameTypePacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import org.geysermc.floodgate.pluginmessage.PluginMessageChannels;
import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.erosion.GeyserboundHandshakePacketHandler;
import org.geysermc.geyser.level.JavaDimension;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.TextDecoration;
import org.geysermc.geyser.translator.level.BiomeTranslator;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.ChunkUtils;
import org.geysermc.geyser.util.DimensionUtils;
import org.geysermc.geyser.util.EntityUtils;
import org.geysermc.geyser.util.JavaCodecUtil;
import org.geysermc.geyser.util.PluginMessageUtils;
import java.util.Map;
@Translator(packet = ClientboundLoginPacket.class)
public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket> {
@ -64,42 +55,22 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
session.setErosionHandler(new GeyserboundHandshakePacketHandler(session));
}
Map<String, JavaDimension> dimensions = session.getDimensions();
dimensions.clear();
JavaDimension.load(packet.getRegistry(), dimensions);
Int2ObjectMap<TextDecoration> chatTypes = session.getChatTypes();
chatTypes.clear();
for (CompoundTag tag : JavaCodecUtil.iterateAsTag(packet.getRegistry().get("minecraft:chat_type"))) {
// The ID is NOT ALWAYS THE SAME! ViaVersion as of 1.19 adds two registry entries that do NOT match vanilla.
int id = ((IntTag) tag.get("id")).getValue();
CompoundTag element = tag.get("element");
CompoundTag chat = element.get("chat");
TextDecoration textDecoration = null;
if (chat != null) {
textDecoration = new TextDecoration(chat);
}
chatTypes.put(id, textDecoration);
}
PlayerSpawnInfo spawnInfo = packet.getCommonPlayerSpawnInfo();
// If the player is already initialized and a join game packet is sent, they
// are swapping servers
if (session.isSpawned()) {
String fakeDim = DimensionUtils.getTemporaryDimension(session.getDimension(), packet.getDimension());
String fakeDim = DimensionUtils.getTemporaryDimension(session.getDimension(), spawnInfo.getDimension());
DimensionUtils.switchDimension(session, fakeDim);
session.getWorldCache().removeScoreboard();
}
session.setWorldName(packet.getWorldName());
session.setWorldName(spawnInfo.getWorldName());
session.setLevels(packet.getWorldNames());
BiomeTranslator.loadServerBiomes(session, packet.getRegistry());
session.getTagCache().clear();
session.setGameMode(spawnInfo.getGameMode());
session.setGameMode(packet.getGameMode());
String newDimension = packet.getDimension();
String newDimension = spawnInfo.getDimension();
boolean needsSpawnPacket = !session.isSentSpawnPacket();
if (needsSpawnPacket) {
@ -114,11 +85,11 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
if (!needsSpawnPacket) {
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
playerGameTypePacket.setGamemode(EntityUtils.toBedrockGamemode(packet.getGameMode()).ordinal());
playerGameTypePacket.setGamemode(EntityUtils.toBedrockGamemode(spawnInfo.getGameMode()).ordinal());
session.sendUpstreamPacket(playerGameTypePacket);
}
entity.setLastDeathPosition(packet.getLastDeathPos());
entity.setLastDeathPosition(spawnInfo.getLastDeathPos());
entity.updateBedrockMetadata();
@ -131,16 +102,10 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
session.setServerRenderDistance(packet.getViewDistance());
// TODO customize
// send this again now that we know the server render distance
// as the bedrock client isn't required to send a render distance
session.sendJavaClientSettings();
session.sendDownstreamPacket(new ServerboundCustomPayloadPacket("minecraft:brand", PluginMessageUtils.getGeyserBrandData()));
// TODO don't send two packets
// if (true) {
// session.sendDownstreamPacket(new ServerboundCustomPayloadPacket("minecraft:register", Constants.PLUGIN_MESSAGE.getBytes(StandardCharsets.UTF_8)));
// }
// register the plugin messaging channels used in Floodgate
if (session.remoteServer().authType() == AuthType.FLOODGATE) {
session.sendDownstreamPacket(new ServerboundCustomPayloadPacket("minecraft:register", PluginMessageChannels.getFloodgateRegisterData()));
}

View file

@ -25,8 +25,8 @@
package org.geysermc.geyser.translator.protocol.java;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundPingPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundPongPacket;
import com.github.steveice10.mc.protocol.packet.common.clientbound.ClientboundPingPacket;
import com.github.steveice10.mc.protocol.packet.common.serverbound.ServerboundPongPacket;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.translator.protocol.java;
import com.github.steveice10.mc.protocol.packet.configuration.clientbound.ClientboundRegistryDataPacket;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntTag;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import org.geysermc.geyser.level.JavaDimension;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.TextDecoration;
import org.geysermc.geyser.translator.level.BiomeTranslator;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.JavaCodecUtil;
import java.util.Map;
@Translator(packet = ClientboundRegistryDataPacket.class)
public class JavaRegistryDataTranslator extends PacketTranslator<ClientboundRegistryDataPacket> {
@Override
public void translate(GeyserSession session, ClientboundRegistryDataPacket packet) {
Map<String, JavaDimension> dimensions = session.getDimensions();
dimensions.clear();
JavaDimension.load(packet.getRegistry(), dimensions);
Int2ObjectMap<TextDecoration> chatTypes = session.getChatTypes();
chatTypes.clear();
for (CompoundTag tag : JavaCodecUtil.iterateAsTag(packet.getRegistry().get("minecraft:chat_type"))) {
// The ID is NOT ALWAYS THE SAME! ViaVersion as of 1.19 adds two registry entries that do NOT match vanilla.
int id = ((IntTag) tag.get("id")).getValue();
CompoundTag element = tag.get("element");
CompoundTag chat = element.get("chat");
TextDecoration textDecoration = null;
if (chat != null) {
textDecoration = new TextDecoration(chat);
}
chatTypes.put(id, textDecoration);
}
BiomeTranslator.loadServerBiomes(session, packet.getRegistry());
}
}

View file

@ -25,6 +25,7 @@
package org.geysermc.geyser.translator.protocol.java;
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerSpawnInfo;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundRespawnPacket;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
@ -46,6 +47,7 @@ public class JavaRespawnTranslator extends PacketTranslator<ClientboundRespawnPa
@Override
public void translate(GeyserSession session, ClientboundRespawnPacket packet) {
SessionPlayerEntity entity = session.getPlayerEntity();
PlayerSpawnInfo spawnInfo = packet.getCommonPlayerSpawnInfo();
session.setSpawned(false);
@ -56,13 +58,13 @@ public class JavaRespawnTranslator extends PacketTranslator<ClientboundRespawnPa
session.setOpenInventory(null);
session.setClosingInventory(false);
entity.setLastDeathPosition(packet.getLastDeathPos());
entity.setLastDeathPosition(spawnInfo.getLastDeathPos());
entity.updateBedrockMetadata();
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
playerGameTypePacket.setGamemode(EntityUtils.toBedrockGamemode(packet.getGamemode()).ordinal());
playerGameTypePacket.setGamemode(EntityUtils.toBedrockGamemode(spawnInfo.getGameMode()).ordinal());
session.sendUpstreamPacket(playerGameTypePacket);
session.setGameMode(packet.getGamemode());
session.setGameMode(spawnInfo.getGameMode());
if (session.isRaining()) {
LevelEventPacket stopRainPacket = new LevelEventPacket();
@ -82,14 +84,14 @@ public class JavaRespawnTranslator extends PacketTranslator<ClientboundRespawnPa
session.setThunder(false);
}
String newDimension = packet.getDimension();
if (!session.getDimension().equals(newDimension) || !packet.getWorldName().equals(session.getWorldName())) {
String newDimension = spawnInfo.getDimension();
if (!session.getDimension().equals(newDimension) || !spawnInfo.getWorldName().equals(session.getWorldName())) {
// Switching to a new world (based off the world name change or new dimension); send a fake dimension change
if (DimensionUtils.javaToBedrock(session.getDimension()) == DimensionUtils.javaToBedrock(newDimension)) {
String fakeDim = DimensionUtils.getTemporaryDimension(session.getDimension(), newDimension);
DimensionUtils.switchDimension(session, fakeDim);
}
session.setWorldName(packet.getWorldName());
session.setWorldName(spawnInfo.getWorldName());
DimensionUtils.switchDimension(session, newDimension);
ChunkUtils.loadDimension(session);

View file

@ -25,7 +25,7 @@
package org.geysermc.geyser.translator.protocol.java;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundUpdateTagsPacket;
import com.github.steveice10.mc.protocol.packet.common.clientbound.ClientboundUpdateTagsPacket;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;

View file

@ -42,8 +42,9 @@ public class JavaUpdateMobEffectTranslator extends PacketTranslator<ClientboundU
if (entity == session.getPlayerEntity()) {
session.getEffectCache().setEffect(packet.getEffect(), packet.getAmplifier());
}
if (entity == null)
if (entity == null) {
return;
}
int duration = packet.getDuration();
if (duration < 0) {

View file

@ -29,6 +29,7 @@ import com.github.steveice10.mc.auth.data.GameProfile;
import com.github.steveice10.mc.protocol.data.game.PlayerListEntry;
import com.github.steveice10.mc.protocol.data.game.PlayerListEntryAction;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundPlayerInfoUpdatePacket;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.packet.PlayerListPacket;
import org.geysermc.geyser.GeyserImpl;
@ -41,6 +42,7 @@ import org.geysermc.geyser.translator.protocol.Translator;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@Translator(packet = ClientboundPlayerInfoUpdatePacket.class)
public class JavaPlayerInfoUpdateTranslator extends PacketTranslator<ClientboundPlayerInfoUpdatePacket> {
@ -50,13 +52,24 @@ public class JavaPlayerInfoUpdateTranslator extends PacketTranslator<Clientbound
if (actions.contains(PlayerListEntryAction.ADD_PLAYER)) {
for (PlayerListEntry entry : packet.getEntries()) {
GameProfile profile = entry.getProfile();
PlayerEntity playerEntity;
boolean self = profile.getId().equals(session.getPlayerEntity().getUuid());
@Nullable GameProfile profile = entry.getProfile();
UUID id = entry.getProfileId();
String name = null;
String texturesProperty = null;
if (profile != null) {
name = profile.getName();
GameProfile.Property textures = profile.getProperty("textures");
String texturesProperty = textures == null ? null : textures.getValue();
if (textures != null) {
texturesProperty = textures.getValue();
}
}
boolean self = id.equals(session.getPlayerEntity().getUuid());
PlayerEntity playerEntity;
if (self) {
// Entity is ourself
playerEntity = session.getPlayerEntity();
@ -66,17 +79,17 @@ public class JavaPlayerInfoUpdateTranslator extends PacketTranslator<Clientbound
session,
-1,
session.getEntityCache().getNextEntityId().incrementAndGet(),
profile.getId(),
id,
Vector3f.ZERO,
Vector3f.ZERO,
0, 0, 0,
profile.getName(),
name,
texturesProperty
);
session.getEntityCache().addPlayerEntity(playerEntity);
}
playerEntity.setUsername(profile.getName());
playerEntity.setUsername(name);
playerEntity.setTexturesProperty(texturesProperty);
if (self) {

View file

@ -31,11 +31,14 @@ import com.github.steveice10.mc.protocol.data.game.entity.object.ProjectileData;
import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.spawn.ClientboundAddEntityPacket;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.type.*;
import org.geysermc.geyser.entity.type.player.PlayerEntity;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.skin.SkinManager;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
@ -44,15 +47,44 @@ public class JavaAddEntityTranslator extends PacketTranslator<ClientboundAddEnti
@Override
public void translate(GeyserSession session, ClientboundAddEntityPacket packet) {
EntityDefinition<?> definition = Registries.ENTITY_DEFINITIONS.get(packet.getType());
if (definition == null) {
session.getGeyser().getLogger().warning("Could not find an entity definition with type " + packet.getType());
return;
}
Vector3f position = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
Vector3f motion = Vector3f.from(packet.getMotionX(), packet.getMotionY(), packet.getMotionZ());
float yaw = packet.getYaw();
float pitch = packet.getPitch();
float headYaw = packet.getHeadYaw();
EntityDefinition<?> definition = Registries.ENTITY_DEFINITIONS.get(packet.getType());
if (definition == null) {
session.getGeyser().getLogger().warning("Could not find an entity definition with type " + packet.getType());
if (packet.getType() == EntityType.PLAYER) {
PlayerEntity entity;
if (packet.getUuid().equals(session.getPlayerEntity().getUuid())) {
// Server is sending a fake version of the current player
entity = new PlayerEntity(session, packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
session.getPlayerEntity().getUuid(), position, motion, yaw, pitch, headYaw, session.getPlayerEntity().getUsername(),
session.getPlayerEntity().getTexturesProperty());
} else {
entity = session.getEntityCache().getPlayerEntity(packet.getUuid());
if (entity == null) {
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.entity.player.failed_list", packet.getUuid()));
return;
}
entity.setEntityId(packet.getEntityId());
entity.setPosition(position);
entity.setYaw(yaw);
entity.setPitch(pitch);
entity.setHeadYaw(headYaw);
entity.setMotion(motion);
}
session.getEntityCache().cacheEntity(entity);
entity.sendPlayer();
SkinManager.requestAndHandleSkinAndCape(entity, session, null);
return;
}

View file

@ -1,72 +0,0 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.translator.protocol.java.entity.spawn;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.spawn.ClientboundAddPlayerPacket;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.entity.type.player.PlayerEntity;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.skin.SkinManager;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
@Translator(packet = ClientboundAddPlayerPacket.class)
public class JavaAddPlayerTranslator extends PacketTranslator<ClientboundAddPlayerPacket> {
@Override
public void translate(GeyserSession session, ClientboundAddPlayerPacket packet) {
Vector3f position = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
float yaw = packet.getYaw();
float pitch = packet.getPitch();
float headYaw = packet.getYaw();
PlayerEntity entity;
if (packet.getUuid().equals(session.getPlayerEntity().getUuid())) {
// Server is sending a fake version of the current player
entity = new PlayerEntity(session, packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
session.getPlayerEntity().getUuid(), position, Vector3f.ZERO, yaw, pitch, headYaw, session.getPlayerEntity().getUsername(),
session.getPlayerEntity().getTexturesProperty());
} else {
entity = session.getEntityCache().getPlayerEntity(packet.getUuid());
if (entity == null) {
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.entity.player.failed_list", packet.getUuid()));
return;
}
entity.setEntityId(packet.getEntityId());
entity.setPosition(position);
entity.setYaw(yaw);
entity.setPitch(pitch);
entity.setHeadYaw(headYaw);
}
session.getEntityCache().cacheEntity(entity);
entity.sendPlayer();
SkinManager.requestAndHandleSkinAndCape(entity, session, null);
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.translator.protocol.java.level;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundChunkBatchFinishedPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.level.ServerboundChunkBatchReceivedPacket;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
@Translator(packet = ClientboundChunkBatchFinishedPacket.class)
public class JavaChunkBatchFinishedTranslator extends PacketTranslator<ClientboundChunkBatchFinishedPacket> {
@Override
public void translate(GeyserSession session, ClientboundChunkBatchFinishedPacket packet) {
// server just sent a batch of LevelChunkWithLightPackets
// the vanilla client uses a ChunkBatchSizeCalculator to calculate the desiredChunksPerTick,
// but currently we just send an arbitrary value. server clamps the value between 0.01 and 64.
session.sendDownstreamPacket(new ServerboundChunkBatchReceivedPacket(20));
}
}

View file

@ -31,6 +31,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.Clientb
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.protocol.bedrock.data.ParticleType;
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
import org.cloudburstmc.protocol.bedrock.packet.LevelEventGenericPacket;
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
@ -103,6 +104,10 @@ public class JavaLevelEventTranslator extends PacketTranslator<ClientboundLevelE
effectPacket.setPosition(pos);
effectPacket.setData(0);
switch (levelEvent) {
case BRUSH_BLOCK_COMPLETE -> {
effectPacket.setType(ParticleType.BRUSH_DUST);
session.playSoundEvent(SoundEvent.BRUSH_COMPLETED, pos); // todo 1.20.2 verify this
}
case COMPOSTER -> {
effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.PARTICLE_CROP_GROWTH);
@ -224,6 +229,7 @@ public class JavaLevelEventTranslator extends PacketTranslator<ClientboundLevelE
BonemealGrowEventData growEventData = (BonemealGrowEventData) packet.getData();
effectPacket.setData(growEventData.getParticleCount());
}
case EGG_CRACK -> effectPacket.setType(ParticleType.VILLAGER_HAPPY); // both the lil green sparkle
case ENDERDRAGON_FIREBALL_EXPLODE -> {
effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.PARTICLE_EYE_OF_ENDER_DEATH); // TODO

View file

@ -25,7 +25,7 @@
package org.geysermc.geyser.util;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundCustomPayloadPacket;
import com.github.steveice10.mc.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket;
import com.google.common.base.Charsets;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.session.GeyserSession;

@ -1 +1 @@
Subproject commit 587220aafb55e80f2a70d6eac2d4b89dc0a005bd
Subproject commit 31ce17e12e991bd841270b99f461641093f42564

View file

@ -14,7 +14,7 @@ protocol-connection = "3.0.0.Beta1-20230908.171156-105"
raknet = "1.0.0.CR1-20230703.195238-9"
blockstateupdater="1.20.30-20230918.203831-4"
mcauthlib = "d9d773e"
mcprotocollib = "1.20-2-20230827.192136-1"
mcprotocollib = "1.20.2-1-20230926.224810-3"
adventure = "4.14.0"
adventure-platform = "4.3.0"
junit = "5.9.2"