Merge master

This commit is contained in:
ImDaBigBoss 2022-06-12 10:33:32 +02:00
commit db003293e9
10 changed files with 232 additions and 31 deletions

View file

@ -0,0 +1,105 @@
/*
* 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.platform.spigot;
import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
import com.destroystokyo.paper.network.StatusClient;
import com.destroystokyo.paper.profile.PlayerProfile;
import org.bukkit.Bukkit;
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.ping.GeyserPingInfo;
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.net.InetSocketAddress;
/**
* This class is used if possible, so listeners listening for PaperServerListPingEvent exclusively have their changes
* applied.
*/
public final class GeyserPaperPingPassthrough implements IGeyserPingPassthrough {
private final GeyserSpigotLogger logger;
public GeyserPaperPingPassthrough(GeyserSpigotLogger logger) {
this.logger = logger;
}
@Nullable
@Override
public GeyserPingInfo getPingInformation(InetSocketAddress inetSocketAddress) {
try {
// We'd rather *not* use deprecations here, but unfortunately any Adventure class would be relocated at
// runtime because we still have to shade in our own Adventure class. For now.
PaperServerListPingEvent event = new PaperServerListPingEvent(new GeyserStatusClient(inetSocketAddress),
Bukkit.getMotd(), Bukkit.getOnlinePlayers().size(), Bukkit.getMaxPlayers(), Bukkit.getVersion(),
GameProtocol.getJavaProtocolVersion(), null);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) {
// We have to send a ping, so not really sure what else to do here.
return null;
}
GeyserPingInfo.Players players;
if (event.shouldHidePlayers()) {
players = new GeyserPingInfo.Players(1, 0);
} else {
players = new GeyserPingInfo.Players(event.getMaxPlayers(), event.getNumPlayers());
}
GeyserPingInfo geyserPingInfo = new GeyserPingInfo(event.getMotd(), players,
new GeyserPingInfo.Version(Bukkit.getVersion(), GameProtocol.getJavaProtocolVersion()));
if (!event.shouldHidePlayers()) {
for (PlayerProfile profile : event.getPlayerSample()) {
geyserPingInfo.getPlayerList().add(profile.getName());
}
}
return geyserPingInfo;
} catch (Exception e) {
logger.debug("Error while getting Paper ping passthrough: " + e);
return null;
}
}
private record GeyserStatusClient(InetSocketAddress address) implements StatusClient {
@Override
public @NotNull InetSocketAddress getAddress() {
return address;
}
@Override
public int getProtocolVersion() {
return GameProtocol.getJavaProtocolVersion();
}
@Override
public @Nullable InetSocketAddress getVirtualHost() {
return null;
}
}
}

View file

@ -168,8 +168,14 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
if (geyserConfig.isLegacyPingPassthrough()) {
this.geyserSpigotPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
} else {
try {
Class.forName("com.destroystokyo.paper.event.server.PaperServerListPingEvent");
this.geyserSpigotPingPassthrough = new GeyserPaperPingPassthrough(geyserLogger);
} catch (ClassNotFoundException e) {
this.geyserSpigotPingPassthrough = new GeyserSpigotPingPassthrough(geyserLogger);
}
}
geyserLogger.debug("Spigot ping passthrough type: " + (this.geyserSpigotPingPassthrough == null ? null : this.geyserSpigotPingPassthrough.getClass()));
this.geyserCommandManager = new GeyserSpigotCommandManager(geyser);
this.geyserCommandManager.init();

View file

@ -33,7 +33,7 @@ object Versions {
const val protocolVersion = "977a9a1"
const val raknetVersion = "1.6.28-SNAPSHOT"
const val mcauthlibVersion = "d9d773e"
const val mcprotocollibversion = "bf3919a"
const val mcprotocollibversion = "bb2b414"
const val packetlibVersion = "3.0"
const val adventureVersion = "4.9.3"
const val eventVersion = "3.0.0"

View file

@ -37,7 +37,6 @@ import com.github.steveice10.mc.protocol.MinecraftProtocol;
import com.github.steveice10.mc.protocol.codec.MinecraftCodecHelper;
import com.github.steveice10.mc.protocol.data.ProtocolState;
import com.github.steveice10.mc.protocol.data.UnexpectedEncryptionException;
import com.github.steveice10.mc.protocol.data.game.MessageType;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
import com.github.steveice10.mc.protocol.data.game.entity.object.Direction;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
@ -123,9 +122,9 @@ import org.geysermc.geyser.session.auth.AuthData;
import org.geysermc.geyser.session.auth.BedrockClientData;
import org.geysermc.geyser.session.cache.*;
import org.geysermc.geyser.skin.FloodgateSkinUploader;
import org.geysermc.geyser.text.ChatTypeEntry;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.text.MinecraftLocale;
import org.geysermc.geyser.text.TextDecoration;
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.ChunkUtils;
@ -336,7 +335,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
*/
private final Map<String, JavaDimension> dimensions = new Object2ObjectOpenHashMap<>(3);
private final Map<MessageType, TextDecoration> chatTypes = new EnumMap<>(MessageType.class);
private final Int2ObjectMap<ChatTypeEntry> chatTypes = new Int2ObjectOpenHashMap<>(8);
@Setter
private int breakingBlock;
@ -547,6 +546,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
this.playerEntity = new SessionPlayerEntity(this);
collisionManager.updatePlayerBoundingBox(this.playerEntity.getPosition());
ChatTypeEntry.applyDefaults(chatTypes);
this.playerInventory = new PlayerInventory();
this.openInventory = null;
this.craftingRecipes = new Int2ObjectOpenHashMap<>();

View file

@ -33,12 +33,15 @@ import lombok.RequiredArgsConstructor;
import lombok.Setter;
import java.net.InetSocketAddress;
import java.util.ArrayDeque;
import java.util.Queue;
@RequiredArgsConstructor
public class UpstreamSession {
@Getter private final BedrockServerSession session;
@Getter @Setter
private boolean initialized = false;
private Queue<BedrockPacket> postStartGamePackets = new ArrayDeque<>();
public void sendPacket(@NonNull BedrockPacket packet) {
if (!isClosed()) {
@ -56,6 +59,25 @@ public class UpstreamSession {
session.disconnect(reason);
}
/**
* Queue a packet that must be delayed until after login.
*/
public void queuePostStartGamePacket(BedrockPacket packet) {
postStartGamePackets.add(packet);
}
public void sendPostStartGamePackets() {
if (isClosed()) {
return;
}
BedrockPacket packet;
while ((packet = postStartGamePackets.poll()) != null) {
session.sendPacket(packet);
}
postStartGamePackets = null;
}
public boolean isClosed() {
return session.isClosed();
}

View file

@ -0,0 +1,57 @@
/*
* 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.text;
import com.github.steveice10.mc.protocol.data.game.MessageType;
import com.nukkitx.protocol.bedrock.packet.TextPacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public record ChatTypeEntry(@Nonnull TextPacket.Type bedrockChatType, @Nullable TextDecoration textDecoration) {
private static final ChatTypeEntry CHAT = new ChatTypeEntry(TextPacket.Type.CHAT, null);
private static final ChatTypeEntry SYSTEM = new ChatTypeEntry(TextPacket.Type.CHAT, null);
private static final ChatTypeEntry TIP = new ChatTypeEntry(TextPacket.Type.CHAT, null);
private static final ChatTypeEntry RAW = new ChatTypeEntry(TextPacket.Type.CHAT, null);
/**
* Apply defaults to a map so it isn't empty in the event a chat message is sent before the login packet.
*/
public static void applyDefaults(Int2ObjectMap<ChatTypeEntry> chatTypes) {
// So the proper way to do this, probably, would be to dump the NBT data from vanilla and load it.
// But, the only way this happens is if a chat message is sent to us before the login packet, which is rare.
// So we'll just make sure chat ends up in the right place.
chatTypes.put(MessageType.CHAT.ordinal(), CHAT);
chatTypes.put(MessageType.SYSTEM.ordinal(), SYSTEM);
chatTypes.put(MessageType.GAME_INFO.ordinal(), TIP);
chatTypes.put(MessageType.SAY_COMMAND.ordinal(), RAW);
chatTypes.put(MessageType.MSG_COMMAND.ordinal(), RAW);
chatTypes.put(MessageType.TEAM_MSG_COMMAND.ordinal(), RAW);
chatTypes.put(MessageType.EMOTE_COMMAND.ordinal(), RAW);
chatTypes.put(MessageType.TELLRAW_COMMAND.ordinal(), RAW);
}
}

View file

@ -30,16 +30,20 @@ import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundLo
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 com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.nukkitx.protocol.bedrock.data.GameRuleData;
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
import com.nukkitx.protocol.bedrock.packet.AdventureSettingsPacket;
import com.nukkitx.protocol.bedrock.packet.GameRulesChangedPacket;
import com.nukkitx.protocol.bedrock.packet.SetPlayerGameTypePacket;
import com.nukkitx.protocol.bedrock.packet.TextPacket;
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.level.JavaDimension;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.ChatTypeEntry;
import org.geysermc.geyser.text.TextDecoration;
import org.geysermc.geyser.translator.level.BiomeTranslator;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
@ -64,21 +68,29 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
JavaDimension.load(packet.getRegistry(), dimensions);
Map<MessageType, TextDecoration> chatTypes = session.getChatTypes();
Int2ObjectMap<ChatTypeEntry> chatTypes = session.getChatTypes();
chatTypes.clear();
for (CompoundTag tag : JavaCodecEntry.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");
if (chat == null) {
continue;
TextDecoration textDecoration = null;
if (chat != null) {
CompoundTag decorationTag = chat.get("decoration");
if (decorationTag != null) {
textDecoration = new TextDecoration(decorationTag);
}
CompoundTag decoration = chat.get("decoration");
if (decoration == null) {
continue;
}
MessageType type = MessageType.from(id);
chatTypes.put(type, new TextDecoration(decoration));
MessageType type = MessageType.from(((StringTag) tag.get("name")).getValue());
// TODO new types?
TextPacket.Type bedrockType = switch (type) {
case CHAT -> TextPacket.Type.CHAT;
case SYSTEM -> TextPacket.Type.SYSTEM;
case GAME_INFO -> TextPacket.Type.TIP;
default -> TextPacket.Type.RAW;
};
chatTypes.put(id, new ChatTypeEntry(bedrockType, textDecoration));
}
// If the player is already initialized and a join game packet is sent, they
@ -103,6 +115,9 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
// The player has yet to spawn so let's do that using some of the information in this Java packet
session.setDimension(newDimension);
session.connect();
// It is now safe to send these packets
session.getUpstream().sendPostStartGamePackets();
}
AdventureSettingsPacket bedrockPacket = new AdventureSettingsPacket();

View file

@ -30,6 +30,7 @@ import com.nukkitx.protocol.bedrock.packet.TextPacket;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslatableComponent;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.ChatTypeEntry;
import org.geysermc.geyser.text.TextDecoration;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
@ -44,22 +45,18 @@ public class JavaPlayerChatTranslator extends PacketTranslator<ClientboundPlayer
@Override
public void translate(GeyserSession session, ClientboundPlayerChatPacket packet) {
ChatTypeEntry entry = session.getChatTypes().get(packet.getTypeId());
TextPacket textPacket = new TextPacket();
textPacket.setPlatformChatId("");
textPacket.setSourceName("");
textPacket.setXuid(session.getAuthData().xuid());
// TODO new types
textPacket.setType(switch (packet.getType()) {
case CHAT -> TextPacket.Type.CHAT;
case SYSTEM -> TextPacket.Type.SYSTEM;
case GAME_INFO -> TextPacket.Type.TIP;
default -> TextPacket.Type.RAW;
});
textPacket.setType(entry.bedrockChatType());
textPacket.setNeedsTranslation(false);
Component message = packet.getUnsignedContent() == null ? packet.getSignedContent() : packet.getUnsignedContent();
TextDecoration decoration = session.getChatTypes().get(packet.getType());
TextDecoration decoration = entry.textDecoration();
if (decoration != null) {
// As of 1.19 - do this to apply all the styling for signed messages
// Though, Bedrock cannot care about the signed stuff.

View file

@ -41,17 +41,15 @@ public class JavaSystemChatTranslator extends PacketTranslator<ClientboundSystem
textPacket.setPlatformChatId("");
textPacket.setSourceName("");
textPacket.setXuid(session.getAuthData().xuid());
// TODO new types
textPacket.setType(switch (packet.getType()) {
case CHAT -> TextPacket.Type.CHAT;
case SYSTEM -> TextPacket.Type.SYSTEM;
case GAME_INFO -> TextPacket.Type.TIP;
default -> TextPacket.Type.RAW;
});
textPacket.setType(session.getChatTypes().get(packet.getTypeId()).bedrockChatType());
textPacket.setNeedsTranslation(false);
textPacket.setMessage(MessageTranslator.convertMessage(packet.getContent(), session.locale()));
if (session.isSentSpawnPacket()) {
session.sendUpstreamPacket(textPacket);
} else {
session.getUpstream().queuePostStartGamePacket(textPacket);
}
}
}

@ -1 +1 @@
Subproject commit e13611fd97b1801d4c4b914cd409351a49d19537
Subproject commit 99a1f8070e844d059454dacbb6e8b203521eed23