2019-07-08 23:35:32 +00:00
/ *
2022-01-01 19:03:05 +00:00
* Copyright ( c ) 2019 - 2022 GeyserMC . http : //geysermc.org
2019-07-08 23:35:32 +00:00
*
2019-07-11 21:30:35 +00:00
* 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 :
2019-07-08 23:35:32 +00:00
*
2019-07-11 21:30:35 +00:00
* 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 .
2019-07-08 23:35:32 +00:00
*
* @author GeyserMC
* @link https : //github.com/GeyserMC/Geyser
* /
2021-11-20 21:34:30 +00:00
package org.geysermc.geyser.network ;
2019-07-08 23:35:32 +00:00
2020-06-19 10:57:34 +00:00
import com.nukkitx.protocol.bedrock.BedrockPong ;
import com.nukkitx.protocol.bedrock.BedrockServerEventHandler ;
import com.nukkitx.protocol.bedrock.BedrockServerSession ;
2021-08-12 14:42:58 +00:00
import io.netty.buffer.ByteBuf ;
2020-05-23 21:50:04 +00:00
import io.netty.channel.ChannelHandlerContext ;
2021-08-17 00:39:29 +00:00
import io.netty.channel.DefaultEventLoopGroup ;
2020-05-23 21:50:04 +00:00
import io.netty.channel.socket.DatagramPacket ;
2021-08-17 00:39:29 +00:00
import io.netty.util.concurrent.DefaultThreadFactory ;
2021-11-20 21:34:30 +00:00
import org.geysermc.geyser.GeyserImpl ;
2021-11-20 23:29:46 +00:00
import org.geysermc.geyser.ping.GeyserPingInfo ;
2021-11-20 21:34:30 +00:00
import org.geysermc.geyser.configuration.GeyserConfiguration ;
2021-11-22 19:52:26 +00:00
import org.geysermc.geyser.session.GeyserSession ;
2021-11-20 23:29:46 +00:00
import org.geysermc.geyser.translator.text.MessageTranslator ;
2021-11-20 21:34:30 +00:00
import org.geysermc.geyser.ping.IGeyserPingPassthrough ;
2021-11-20 23:29:46 +00:00
import org.geysermc.geyser.text.GeyserLocale ;
2019-07-08 23:35:32 +00:00
2021-08-12 14:42:58 +00:00
import javax.annotation.Nonnull ;
2019-07-08 23:35:32 +00:00
import java.net.InetSocketAddress ;
2021-01-11 20:37:37 +00:00
import java.nio.charset.StandardCharsets ;
2021-02-17 23:25:41 +00:00
import java.util.List ;
2019-07-08 23:35:32 +00:00
2019-07-10 06:34:10 +00:00
public class ConnectorServerEventHandler implements BedrockServerEventHandler {
2022-04-21 01:22:02 +00:00
private static final boolean PRINT_DEBUG_PINGS = Boolean . parseBoolean ( System . getProperty ( " Geyser.PrintPingsInDebugMode " , " true " ) ) ;
2021-02-16 02:41:11 +00:00
/ *
The following constants are all used to ensure the ping does not reach a length where it is unparsable by the Bedrock client
* /
2022-03-20 02:55:29 +00:00
private static final int MINECRAFT_VERSION_BYTES_LENGTH = GameProtocol . DEFAULT_BEDROCK_CODEC . getMinecraftVersion ( ) . getBytes ( StandardCharsets . UTF_8 ) . length ;
2021-11-20 21:34:30 +00:00
private static final int BRAND_BYTES_LENGTH = GeyserImpl . NAME . getBytes ( StandardCharsets . UTF_8 ) . length ;
2021-02-16 02:41:11 +00:00
/ * *
* The MOTD , sub - MOTD and Minecraft version ( { @link # MINECRAFT_VERSION_BYTES_LENGTH } ) combined cannot reach this length .
* /
private static final int MAGIC_RAKNET_LENGTH = 338 ;
2019-07-08 23:35:32 +00:00
2021-11-20 21:34:30 +00:00
private final GeyserImpl geyser ;
2021-08-17 02:33:14 +00:00
// There is a constructor that doesn't require inputting threads, but older Netty versions don't have it
private final DefaultEventLoopGroup eventLoopGroup = new DefaultEventLoopGroup ( 0 , new DefaultThreadFactory ( " Geyser player thread " ) ) ;
2019-07-08 23:35:32 +00:00
2021-11-20 21:34:30 +00:00
public ConnectorServerEventHandler ( GeyserImpl geyser ) {
this . geyser = geyser ;
2019-07-08 23:35:32 +00:00
}
@Override
public boolean onConnectionRequest ( InetSocketAddress inetSocketAddress ) {
2021-11-20 21:34:30 +00:00
List < String > allowedProxyIPs = geyser . getConfig ( ) . getBedrock ( ) . getProxyProtocolWhitelistedIPs ( ) ;
if ( geyser . getConfig ( ) . getBedrock ( ) . isEnableProxyProtocol ( ) & & ! allowedProxyIPs . isEmpty ( ) ) {
2021-02-17 23:25:41 +00:00
boolean isWhitelistedIP = false ;
2021-11-20 21:34:30 +00:00
for ( CIDRMatcher matcher : geyser . getConfig ( ) . getBedrock ( ) . getWhitelistedIPsMatchers ( ) ) {
2021-02-17 23:25:41 +00:00
if ( matcher . matches ( inetSocketAddress . getAddress ( ) ) ) {
isWhitelistedIP = true ;
break ;
}
}
if ( ! isWhitelistedIP ) {
return false ;
}
}
2021-11-20 23:29:46 +00:00
geyser . getLogger ( ) . info ( GeyserLocale . getLocaleStringLog ( " geyser.network.attempt_connect " , inetSocketAddress ) ) ;
2019-07-08 23:35:32 +00:00
return true ;
}
@Override
public BedrockPong onQuery ( InetSocketAddress inetSocketAddress ) {
2022-04-21 01:22:02 +00:00
if ( geyser . getConfig ( ) . isDebugMode ( ) & & PRINT_DEBUG_PINGS ) {
2021-11-20 23:29:46 +00:00
geyser . getLogger ( ) . debug ( GeyserLocale . getLocaleStringLog ( " geyser.network.pinged " , inetSocketAddress ) ) ;
2021-09-10 01:27:38 +00:00
}
2019-12-21 17:35:48 +00:00
2021-11-20 21:34:30 +00:00
GeyserConfiguration config = geyser . getConfig ( ) ;
2020-05-23 21:50:04 +00:00
GeyserPingInfo pingInfo = null ;
if ( config . isPassthroughMotd ( ) | | config . isPassthroughPlayerCounts ( ) ) {
2021-11-20 21:34:30 +00:00
IGeyserPingPassthrough pingPassthrough = geyser . getBootstrap ( ) . getGeyserPingPassthrough ( ) ;
2020-12-07 19:04:50 +00:00
pingInfo = pingPassthrough . getPingInformation ( inetSocketAddress ) ;
2020-05-23 21:50:04 +00:00
}
2019-11-02 20:58:50 +00:00
2019-12-21 17:35:48 +00:00
BedrockPong pong = new BedrockPong ( ) ;
pong . setEdition ( " MCPE " ) ;
2021-02-15 17:57:18 +00:00
pong . setGameType ( " Survival " ) ; // Can only be Survival or Creative as of 1.16.210.59
2019-12-21 17:35:48 +00:00
pong . setNintendoLimited ( false ) ;
2022-03-20 02:55:29 +00:00
pong . setProtocolVersion ( GameProtocol . DEFAULT_BEDROCK_CODEC . getProtocolVersion ( ) ) ;
pong . setVersion ( GameProtocol . DEFAULT_BEDROCK_CODEC . getMinecraftVersion ( ) ) ; // Required to not be empty as of 1.16.210.59. Can only contain . and numbers.
2019-12-21 17:35:48 +00:00
pong . setIpv4Port ( config . getBedrock ( ) . getPort ( ) ) ;
2020-05-23 21:50:04 +00:00
2020-07-04 21:35:48 +00:00
if ( config . isPassthroughMotd ( ) & & pingInfo ! = null & & pingInfo . getDescription ( ) ! = null ) {
2020-11-16 23:57:57 +00:00
String [ ] motd = MessageTranslator . convertMessageLenient ( pingInfo . getDescription ( ) ) . split ( " \ n " ) ;
2020-03-17 22:55:11 +00:00
String mainMotd = motd [ 0 ] ; // First line of the motd.
2021-11-20 21:34:30 +00:00
String subMotd = ( motd . length ! = 1 ) ? motd [ 1 ] : GeyserImpl . NAME ; // Second line of the motd if present, otherwise default.
2020-03-17 22:55:11 +00:00
pong . setMotd ( mainMotd . trim ( ) ) ;
pong . setSubMotd ( subMotd . trim ( ) ) ; // Trimmed to shift it to the left, prevents the universe from collapsing on us just because we went 2 characters over the text box's limit.
2020-05-23 21:50:04 +00:00
} else {
pong . setMotd ( config . getBedrock ( ) . getMotd1 ( ) ) ;
pong . setSubMotd ( config . getBedrock ( ) . getMotd2 ( ) ) ;
}
if ( config . isPassthroughPlayerCounts ( ) & & pingInfo ! = null ) {
2020-07-04 21:35:48 +00:00
pong . setPlayerCount ( pingInfo . getPlayers ( ) . getOnline ( ) ) ;
pong . setMaximumPlayerCount ( pingInfo . getPlayers ( ) . getMax ( ) ) ;
2019-07-21 23:52:30 +00:00
} else {
2021-11-20 21:34:30 +00:00
pong . setPlayerCount ( geyser . getSessionManager ( ) . getSessions ( ) . size ( ) ) ;
2019-12-21 17:35:48 +00:00
pong . setMaximumPlayerCount ( config . getMaxPlayers ( ) ) ;
2019-07-21 23:52:30 +00:00
}
2020-04-15 04:02:57 +00:00
2021-02-16 02:41:11 +00:00
// Fallbacks to prevent errors and allow Bedrock to see the server
2021-09-10 18:10:56 +00:00
if ( pong . getMotd ( ) = = null | | pong . getMotd ( ) . isBlank ( ) ) {
2021-11-20 21:34:30 +00:00
pong . setMotd ( GeyserImpl . NAME ) ;
2021-02-12 19:16:29 +00:00
}
2021-09-10 18:10:56 +00:00
if ( pong . getSubMotd ( ) = = null | | pong . getSubMotd ( ) . isBlank ( ) ) {
2021-02-16 02:41:11 +00:00
// Sub-MOTD cannot be empty as of 1.16.210.59
2021-11-20 21:34:30 +00:00
pong . setSubMotd ( GeyserImpl . NAME ) ;
2021-02-12 19:16:29 +00:00
}
2021-01-11 20:37:37 +00:00
// The ping will not appear if the MOTD + sub-MOTD is of a certain length.
// We don't know why, though
byte [ ] motdArray = pong . getMotd ( ) . getBytes ( StandardCharsets . UTF_8 ) ;
2021-02-16 02:41:11 +00:00
int subMotdLength = pong . getSubMotd ( ) . getBytes ( StandardCharsets . UTF_8 ) . length ;
if ( motdArray . length + subMotdLength > ( MAGIC_RAKNET_LENGTH - MINECRAFT_VERSION_BYTES_LENGTH ) ) {
// Shorten the sub-MOTD first since that only appears locally
if ( subMotdLength > BRAND_BYTES_LENGTH ) {
2021-11-20 21:34:30 +00:00
pong . setSubMotd ( GeyserImpl . NAME ) ;
2021-02-16 02:41:11 +00:00
subMotdLength = BRAND_BYTES_LENGTH ;
}
if ( motdArray . length > ( MAGIC_RAKNET_LENGTH - MINECRAFT_VERSION_BYTES_LENGTH - subMotdLength ) ) {
2021-01-11 20:37:37 +00:00
// If the top MOTD is still too long, we chop it down
2021-02-16 02:41:11 +00:00
byte [ ] newMotdArray = new byte [ MAGIC_RAKNET_LENGTH - MINECRAFT_VERSION_BYTES_LENGTH - subMotdLength ] ;
2021-01-11 20:37:37 +00:00
System . arraycopy ( motdArray , 0 , newMotdArray , 0 , newMotdArray . length ) ;
pong . setMotd ( new String ( newMotdArray , StandardCharsets . UTF_8 ) ) ;
}
}
2020-04-15 04:02:57 +00:00
//Bedrock will not even attempt a connection if the client thinks the server is full
//so we have to fake it not being full
if ( pong . getPlayerCount ( ) > = pong . getMaximumPlayerCount ( ) ) {
pong . setMaximumPlayerCount ( pong . getPlayerCount ( ) + 1 ) ;
}
2019-11-02 20:58:50 +00:00
return pong ;
2019-07-08 23:35:32 +00:00
}
@Override
2021-12-06 00:03:47 +00:00
public void onSessionCreation ( @Nonnull BedrockServerSession bedrockServerSession ) {
try {
2022-03-20 02:55:29 +00:00
bedrockServerSession . setPacketCodec ( GameProtocol . DEFAULT_BEDROCK_CODEC ) ;
2021-12-06 00:03:47 +00:00
bedrockServerSession . setLogging ( true ) ;
bedrockServerSession . setCompressionLevel ( geyser . getConfig ( ) . getBedrock ( ) . getCompressionLevel ( ) ) ;
bedrockServerSession . setPacketHandler ( new UpstreamPacketHandler ( geyser , new GeyserSession ( geyser , bedrockServerSession , eventLoopGroup . next ( ) ) ) ) ;
// Set the packet codec to default just in case we need to send disconnect packets.
} catch ( Throwable e ) {
// Error must be caught or it will be swallowed
geyser . getLogger ( ) . error ( " Error occurred while initializing player! " , e ) ;
bedrockServerSession . disconnect ( e . getMessage ( ) ) ;
}
2019-07-08 23:35:32 +00:00
}
2020-05-23 21:50:04 +00:00
@Override
2021-12-06 00:03:47 +00:00
public void onUnhandledDatagram ( @Nonnull ChannelHandlerContext ctx , @Nonnull DatagramPacket packet ) {
try {
ByteBuf content = packet . content ( ) ;
if ( QueryPacketHandler . isQueryPacket ( content ) ) {
new QueryPacketHandler ( geyser , packet . sender ( ) , content ) ;
}
} catch ( Throwable e ) {
// Error must be caught or it will be swallowed
if ( geyser . getConfig ( ) . isDebugMode ( ) ) {
geyser . getLogger ( ) . error ( " Error occurred during unhandled datagram! " , e ) ;
}
2021-08-12 14:42:58 +00:00
}
2020-05-23 21:50:04 +00:00
}
2019-07-08 23:35:32 +00:00
}