BungeeCord-在离线模式下检测到经过身份验证的播放器

时间:2018-10-27 16:57:36

标签: java minecraft bukkit bungeecord

几天来,我一直在尝试如何检测玩家的帐户在离线模式下是否通过了Mojang身份验证。

我为什么要这样做?

当前,我有一个基本的管理系统,该系统包括检查玩家的昵称是否存在于mojang数据库中,是否将setOnlineMode设置为true,否则将其设置为false。   系统允许显示玩家的皮肤和他的UUID,但是问题是,如果认为脱机的玩家购买了具有相同假名的Premium帐户,则他没有皮肤或真实的UUID,因为setOnlineMode设置为false防止损失进度。   我的目标是创建一个系统,该系统可检测到离线用户刚刚使用经过身份验证的Minecraft帐户登录,以便服务器可以向其提供自动将其进度转移到新的真实UUID的信息。

我做了一些研究,例如尝试here,我删除了在线模式条件以允许检查播放器是否已通过身份验证,然后here如果播放器无效,则删除了断开连接。这给了我一个奇妙的错误。

  

13:13:31 [GRAVE] [Arbi13_]-> UpstreamBridge-遇到异常   io.netty.handler.codec.EncoderException:java.lang.IllegalArgumentException:在阶段GAME中,方向为TO_CLIENT的数据包类net.md_5.bungee.protocol.packet.SetCompression无法获取ID       在io.netty.handler.codec.MessageToByteEncoder.write(MessageToByteEncoder.java:125)       在io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:738)       在io.netty.channel.AbstractChannelHandlerContext.invokeWriteAndFlush(AbstractChannelHandlerContext.java:801)       在io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:814)       在io.netty.channel.AbstractChannelHandlerContext.writeAndFlush(AbstractChannelHandlerContext.java:794)       在io.netty.channel.DefaultChannelPipeline.writeAndFlush(DefaultChannelPipeline.java:1066)       在io.netty.channel.AbstractChannel.writeAndFlush(AbstractChannel.java:305)       在net.md_5.bungee.netty.ChannelWrapper.write(ChannelWrapper.java:60)       在net.md_5.bungee.UserConnection $ 1.sendPacket(UserConnection.java:148)       位于net.md_5.bungee.UserConnection.setCompressionThreshold(UserConnection.java:697)       在net.md_5.bungee.connection.InitialHandler $ 6 $ 1.run(InitialHandler.java:523)       在io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)       在io.netty.util.concurrent.SingleThreadEventExecutor.runAllTask​​s(SingleThreadEventExecutor.java:404)       在io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:326)       在io.netty.util.concurrent.SingleThreadEventExecutor $ 5.run(SingleThreadEventExecutor.java:897)       在java.lang.Thread.run(Thread.java:748)   由以下原因引起:java.lang.IllegalArgumentException:在阶段GAME中,方向为TO_CLIENT的数据包类net.md_5.bungee.protocol.packet.SetCompression无法获取ID。       在com.google.common.base.Preconditions.checkArgument(Preconditions.java:399)       位于net.md_5.bungee.protocol.Protocol $ DirectionData.getId(Protocol.java:462)       在net.md_5.bungee.protocol.MinecraftEncoder.encode(MinecraftEncoder.java:23)       位于net.md_5.bungee.protocol.MinecraftEncoder.encode(MinecraftEncoder.java:9)       在io.netty.handler.codec.MessageToByteEncoder.write(MessageToByteEncoder.java:107)       ...另外15个

@Override
public void handle(LoginRequest loginRequest) throws Exception
{
    Preconditions.checkState( thisState == State.USERNAME, "Not expecting USERNAME" );
    this.loginRequest = loginRequest;

    if ( getName().contains( "." ) )
    {
        disconnect( bungee.getTranslation( "name_invalid" ) );
        return;
    }

    if ( getName().length() > 16 )
    {
        disconnect( bungee.getTranslation( "name_too_long" ) );
        return;
    }

    int limit = BungeeCord.getInstance().config.getPlayerLimit();
    if ( limit > 0 && bungee.getOnlineCount() > limit )
    {
        disconnect( bungee.getTranslation( "proxy_full" ) );
        return;
    }

    // If offline mode and they are already on, don't allow connect
    // We can just check by UUID here as names are based on UUID
    if ( !isOnlineMode() && bungee.getPlayer( getUniqueId() ) != null )
    {
        disconnect( bungee.getTranslation( "already_connected_proxy" ) );
        return;
    }

    Callback<PreLoginEvent> callback = new Callback<PreLoginEvent>()
    {

        @Override
        public void done(PreLoginEvent result, Throwable error)
        {
            if ( result.isCancelled() )
            {
                disconnect( result.getCancelReasonComponents() );
                return;
            }
            if ( ch.isClosed() )
            {
                return;
            }
            unsafe().sendPacket( request = EncryptionUtil.encryptRequest() );
            thisState = State.ENCRYPT;
        }
    };

    // fire pre login event
    bungee.getPluginManager().callEvent( new PreLoginEvent( InitialHandler.this, callback ) );
}

@Override
public void handle(final EncryptionResponse encryptResponse) throws Exception
{
    Preconditions.checkState( thisState == State.ENCRYPT, "Not expecting ENCRYPT" );

    SecretKey sharedKey = EncryptionUtil.getSecret( encryptResponse, request );
    BungeeCipher decrypt = EncryptionUtil.getCipher( false, sharedKey );
    ch.addBefore( PipelineUtils.FRAME_DECODER, PipelineUtils.DECRYPT_HANDLER, new CipherDecoder( decrypt ) );
    BungeeCipher encrypt = EncryptionUtil.getCipher( true, sharedKey );
    ch.addBefore( PipelineUtils.FRAME_PREPENDER, PipelineUtils.ENCRYPT_HANDLER, new CipherEncoder( encrypt ) );

    String encName = URLEncoder.encode( InitialHandler.this.getName(), "UTF-8" );

    MessageDigest sha = MessageDigest.getInstance( "SHA-1" );
    for ( byte[] bit : new byte[][]
    {
        request.getServerId().getBytes( "ISO_8859_1" ), sharedKey.getEncoded(), EncryptionUtil.keys.getPublic().getEncoded()
    } )
    {
        sha.update( bit );
    }
    String encodedHash = URLEncoder.encode( new BigInteger( sha.digest() ).toString( 16 ), "UTF-8" );

    String preventProxy = ( ( BungeeCord.getInstance().config.isPreventProxyConnections() ) ? "&ip=" + URLEncoder.encode( getAddress().getAddress().getHostAddress(), "UTF-8" ) : "" );
    String authURL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=" + encName + "&serverId=" + encodedHash + preventProxy;

    Callback<String> handler = new Callback<String>()
    {
        @Override
        public void done(String result, Throwable error)
        {
            if ( error == null )
            {
                LoginResult obj = BungeeCord.getInstance().gson.fromJson( result, LoginResult.class );
                if ( obj != null && obj.getId() != null )
                {
                    loginProfile = obj;
                    name = obj.getName();
                    uniqueId = Util.getUUID( obj.getId() );
                    authenticated = true;
                    finish();
                    return;


          }
            if(isOnlineMode()) {
                disconnect(bungee.getTranslation("offline_mode_player"));
                return;
            }

            finish();
            return;
        } else
        {
            disconnect( bungee.getTranslation( "mojang_fail" ) );
            bungee.getLogger().log( Level.SEVERE, "Error authenticating " + getName() + " with minecraft.net", error );
        }
    }
};

HttpClient.get( authURL, ch.getHandle().eventLoop(), handler );
}

1 个答案:

答案 0 :(得分:0)

我认为这是不可能的,因为 Minecraft 没有交换会话细节。我知道运行第二个代理的服务器为高级用户启用了在线模式,以处理导致“后面”相同 bukkit 服务器的会话详细信息。