diff --git a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionJoinEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionJoinEvent.java new file mode 100644 index 000000000..ab2088c00 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionJoinEvent.java @@ -0,0 +1,39 @@ +/* + * 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.api.event.bedrock; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.connection.GeyserConnection; +import org.geysermc.geyser.api.event.connection.ConnectionEvent; + +/** + * Called when Geyser session connected to a Java remote server and is in a play-ready state. + */ +public final class SessionJoinEvent extends ConnectionEvent { + public SessionJoinEvent(@NonNull GeyserConnection connection) { + super(connection); + } +} diff --git a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionLoginEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionLoginEvent.java new file mode 100644 index 000000000..c3c8198c1 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionLoginEvent.java @@ -0,0 +1,109 @@ +/* + * 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.api.event.bedrock; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.event.Cancellable; +import org.geysermc.geyser.api.connection.GeyserConnection; +import org.geysermc.geyser.api.event.connection.ConnectionEvent; +import org.geysermc.geyser.api.network.RemoteServer; + +/** + * Called when a session has logged in, and is about to connect to a remote java server. + * This event is cancellable, and can be used to prevent the player from connecting to the remote server. + */ +public final class SessionLoginEvent extends ConnectionEvent implements Cancellable { + private RemoteServer remoteServer; + private boolean cancelled; + private String disconnectReason; + + public SessionLoginEvent(@NonNull GeyserConnection connection, @NonNull RemoteServer remoteServer) { + super(connection); + this.remoteServer = remoteServer; + } + + /** + * Returns whether the event is cancelled. + * + * @return The cancel status of the event. + */ + @Override + public boolean isCancelled() { + return this.cancelled; + } + + /** + * Cancels the login event, and disconnects the player. + * If cancelled, the player disconnects without connecting to the remote server. + * This method will use a default disconnect reason. To specify one, use {@link #setCancelled(boolean, String)}. + * + * @param cancelled If the login event should be cancelled. + */ + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + /** + * Cancels the login event, and disconnects the player with the specified reason. + * If cancelled, the player disconnects without connecting to the remote server. + * + * @param cancelled If the login event should be cancelled. + * @param disconnectReason The reason for the cancellation. + */ + public void setCancelled(boolean cancelled, @NonNull String disconnectReason) { + this.cancelled = cancelled; + this.disconnectReason = disconnectReason; + } + + /** + * Returns the reason for the cancellation, or null if there is no reason given. + * + * @return The reason for the cancellation. + */ + public @Nullable String disconnectReason() { + return this.disconnectReason; + } + + /** + * Gets the {@link RemoteServer} the section will attempt to connect to. + * + * @return the {@link RemoteServer} the section will attempt to connect to. + */ + public @NonNull RemoteServer remoteServer() { + return this.remoteServer; + } + + /** + * Sets the {@link RemoteServer} to connect the session to. + * + * @param remoteServer Sets the {@link RemoteServer} to connect to. + */ + public void remoteServer(@NonNull RemoteServer remoteServer) { + this.remoteServer = remoteServer; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 641473e22..deb5b90cc 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -56,7 +56,11 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.Server import com.github.steveice10.mc.protocol.packet.login.serverbound.ServerboundCustomQueryPacket; import com.github.steveice10.packetlib.BuiltinFlags; import com.github.steveice10.packetlib.Session; -import com.github.steveice10.packetlib.event.session.*; +import com.github.steveice10.packetlib.event.session.ConnectedEvent; +import com.github.steveice10.packetlib.event.session.DisconnectedEvent; +import com.github.steveice10.packetlib.event.session.PacketErrorEvent; +import com.github.steveice10.packetlib.event.session.PacketSendingEvent; +import com.github.steveice10.packetlib.event.session.SessionAdapter; import com.github.steveice10.packetlib.packet.Packet; import com.github.steveice10.packetlib.tcp.TcpClientSession; import com.github.steveice10.packetlib.tcp.TcpSession; @@ -82,6 +86,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.common.value.qual.IntRange; import org.cloudburstmc.math.vector.*; import org.cloudburstmc.nbt.NbtMap; +import org.cloudburstmc.protocol.bedrock.BedrockDisconnectReasons; import org.cloudburstmc.protocol.bedrock.BedrockServerSession; import org.cloudburstmc.protocol.bedrock.data.*; import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumData; @@ -104,6 +109,7 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.entity.type.GeyserEntity; import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; +import org.geysermc.geyser.api.event.bedrock.SessionLoginEvent; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.command.GeyserCommandSource; @@ -879,6 +885,16 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { * After getting whatever credentials needed, we attempt to join the Java server. */ private void connectDownstream() { + SessionLoginEvent loginEvent = new SessionLoginEvent(this, remoteServer); + GeyserImpl.getInstance().eventBus().fire(loginEvent); + if (loginEvent.isCancelled()) { + String disconnectReason = loginEvent.disconnectReason() == null ? + BedrockDisconnectReasons.DISCONNECTED : loginEvent.disconnectReason(); + disconnect(disconnectReason); + return; + } + + this.remoteServer = loginEvent.remoteServer(); boolean floodgate = this.remoteServer.authType() == AuthType.FLOODGATE; // Start ticking diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java index defe58a8e..de2df0cb7 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java @@ -26,6 +26,8 @@ package org.geysermc.geyser.translator.protocol.bedrock; import org.cloudburstmc.protocol.bedrock.packet.SetLocalPlayerAsInitializedPacket; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.event.bedrock.SessionJoinEvent; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; @@ -68,6 +70,8 @@ public class BedrockSetLocalPlayerAsInitializedTranslator extends PacketTranslat // What am I to expect - as of Bedrock 1.18 session.getFormCache().resendAllForms(); + + GeyserImpl.getInstance().eventBus().fire(new SessionJoinEvent(session)); } } }