mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Merge master
This commit is contained in:
commit
db003293e9
10 changed files with 232 additions and 31 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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<>();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
Loading…
Reference in a new issue