2019-07-08 23:35:32 +00:00
/ *
2021-01-01 15:10:36 +00:00
* Copyright ( c ) 2019 - 2021 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
* /
2019-07-10 06:34:10 +00:00
package org.geysermc.connector.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 ;
2019-07-08 23:35:32 +00:00
import org.geysermc.connector.GeyserConnector ;
2021-01-11 20:37:37 +00:00
import org.geysermc.connector.common.ping.GeyserPingInfo ;
2020-06-19 10:57:34 +00:00
import org.geysermc.connector.configuration.GeyserConfiguration ;
2019-07-10 06:34:10 +00:00
import org.geysermc.connector.network.session.GeyserSession ;
2020-11-16 23:57:57 +00:00
import org.geysermc.connector.network.translators.chat.MessageTranslator ;
2021-01-11 20:37:37 +00:00
import org.geysermc.connector.ping.IGeyserPingPassthrough ;
2020-07-05 23:35:51 +00:00
import org.geysermc.connector.utils.LanguageUtils ;
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 {
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
* /
private static final int MINECRAFT_VERSION_BYTES_LENGTH = BedrockProtocol . DEFAULT_BEDROCK_CODEC . getMinecraftVersion ( ) . getBytes ( StandardCharsets . UTF_8 ) . length ;
private static final int BRAND_BYTES_LENGTH = GeyserConnector . NAME . getBytes ( StandardCharsets . UTF_8 ) . length ;
/ * *
* 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
2020-05-23 21:50:04 +00:00
private final GeyserConnector connector ;
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
2019-07-10 06:34:10 +00:00
public ConnectorServerEventHandler ( GeyserConnector connector ) {
2019-07-08 23:35:32 +00:00
this . connector = connector ;
}
@Override
public boolean onConnectionRequest ( InetSocketAddress inetSocketAddress ) {
2021-02-17 23:25:41 +00:00
List < String > allowedProxyIPs = connector . getConfig ( ) . getBedrock ( ) . getProxyProtocolWhitelistedIPs ( ) ;
if ( connector . getConfig ( ) . getBedrock ( ) . isEnableProxyProtocol ( ) & & ! allowedProxyIPs . isEmpty ( ) ) {
boolean isWhitelistedIP = false ;
for ( CIDRMatcher matcher : connector . getConfig ( ) . getBedrock ( ) . getWhitelistedIPsMatchers ( ) ) {
if ( matcher . matches ( inetSocketAddress . getAddress ( ) ) ) {
isWhitelistedIP = true ;
break ;
}
}
if ( ! isWhitelistedIP ) {
return false ;
}
}
2020-07-05 23:35:51 +00:00
connector . getLogger ( ) . info ( LanguageUtils . getLocaleStringLog ( " geyser.network.attempt_connect " , inetSocketAddress ) ) ;
2019-07-08 23:35:32 +00:00
return true ;
}
@Override
public BedrockPong onQuery ( InetSocketAddress inetSocketAddress ) {
2021-09-10 01:27:38 +00:00
if ( connector . getConfig ( ) . isDebugMode ( ) ) {
connector . getLogger ( ) . debug ( LanguageUtils . getLocaleStringLog ( " geyser.network.pinged " , inetSocketAddress ) ) ;
}
2019-12-21 17:35:48 +00:00
2020-04-29 20:01:53 +00:00
GeyserConfiguration config = connector . getConfig ( ) ;
2020-05-23 21:50:04 +00:00
GeyserPingInfo pingInfo = null ;
if ( config . isPassthroughMotd ( ) | | config . isPassthroughPlayerCounts ( ) ) {
IGeyserPingPassthrough pingPassthrough = connector . 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 ) ;
2020-08-11 14:00:14 +00:00
pong . setProtocolVersion ( BedrockProtocol . DEFAULT_BEDROCK_CODEC . getProtocolVersion ( ) ) ;
2021-02-16 02:41:11 +00:00
pong . setVersion ( BedrockProtocol . 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-02-16 02:41:11 +00:00
String subMotd = ( motd . length ! = 1 ) ? motd [ 1 ] : GeyserConnector . 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 {
2019-12-21 17:35:48 +00:00
pong . setPlayerCount ( connector . getPlayers ( ) . size ( ) ) ;
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
if ( pong . getMotd ( ) = = null | | pong . getMotd ( ) . trim ( ) . isEmpty ( ) ) {
pong . setMotd ( GeyserConnector . NAME ) ;
2021-02-12 19:16:29 +00:00
}
2021-02-16 02:41:11 +00:00
if ( pong . getSubMotd ( ) = = null | | pong . getSubMotd ( ) . trim ( ) . isEmpty ( ) ) {
// Sub-MOTD cannot be empty as of 1.16.210.59
pong . setSubMotd ( GeyserConnector . 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 ) {
pong . setSubMotd ( GeyserConnector . NAME ) ;
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
public void onSessionCreation ( BedrockServerSession bedrockServerSession ) {
2019-07-10 06:34:10 +00:00
bedrockServerSession . setLogging ( true ) ;
2021-04-20 02:18:18 +00:00
bedrockServerSession . setCompressionLevel ( connector . getConfig ( ) . getBedrock ( ) . getCompressionLevel ( ) ) ;
2021-08-17 00:39:29 +00:00
bedrockServerSession . setPacketHandler ( new UpstreamPacketHandler ( connector , new GeyserSession ( connector , bedrockServerSession , eventLoopGroup . next ( ) ) ) ) ;
2020-08-13 14:56:03 +00:00
// Set the packet codec to default just in case we need to send disconnect packets.
bedrockServerSession . setPacketCodec ( BedrockProtocol . DEFAULT_BEDROCK_CODEC ) ;
2019-07-08 23:35:32 +00:00
}
2020-05-23 21:50:04 +00:00
@Override
2021-08-12 14:42:58 +00:00
public void onUnhandledDatagram ( @Nonnull ChannelHandlerContext ctx , DatagramPacket packet ) {
ByteBuf content = packet . content ( ) ;
if ( QueryPacketHandler . isQueryPacket ( content ) ) {
new QueryPacketHandler ( connector , packet . sender ( ) , content ) ;
}
2020-05-23 21:50:04 +00:00
}
2019-07-08 23:35:32 +00:00
}