java - Spigot API - 从玩家获取多个字符串

标签 java minecraft bukkit

我试图做到这一点,当您单击一个标志时,它会显示“输入您的标签”,然后您输入您的标签,然后它会显示“输入您的括号颜色”,然后您输入您的括号颜色,然后输入您的命名颜色,然后聊天颜色。它有效,但有时括号颜色设置名称颜色,一旦我设置聊天颜色,它可能会这样做。谁能告诉我我的代码有什么问题吗?

ArrayList<Player>  input = new ArrayList<Player>();
@EventHandler
public void onChat(AsyncPlayerChatEvent event){
    Player p = event.getPlayer();
    if(input.contains(p)){
        String tag = event.getMessage();
        event.setCancelled(true);
        if(tag.equals("clear")){
            UUID playerid = p.getUniqueId();
            FileConfiguration cfg = getTagsConfig();
            cfg.set("tags." + playerid + ".tag", null);
            p.sendMessage(ChatColor.GREEN + "Tag Cleared!");
        }
        else{
            UUID playerid = p.getUniqueId();
            FileConfiguration cfg = getTagsConfig();
            cfg.set("tags." + playerid + ".tag", tag);
            p.sendMessage(ChatColor.GREEN + "Your prefix is now " + tag);
        }
        saveTagsFile();
        input.remove(p);
        p.sendMessage(ChatColor.RED + "Enter a bracket color (lowercase)");
        p.sendMessage(ChatColor.GREEN + "a" + ChatColor.AQUA + "b" + ChatColor.RED + "c" + ChatColor.LIGHT_PURPLE + "d" + ChatColor.YELLOW + "e" + ChatColor.WHITE + "f" + ChatColor.DARK_BLUE + "1" + ChatColor.DARK_GREEN + "2" + ChatColor.DARK_AQUA + "3" + ChatColor.DARK_RED + "4" + ChatColor.DARK_PURPLE + "5" + ChatColor.GOLD + "6" + ChatColor.GRAY + "7" + ChatColor.DARK_GRAY + "8" + ChatColor.BLUE + "9" + ChatColor.BLACK + "0");
        input2.add(p);
    }
}
ArrayList<Player> input2 = new ArrayList<Player>();
@EventHandler
public void onChat2(AsyncPlayerChatEvent event){
    Player p = event.getPlayer();
    if(input2.contains(p)){
        p.sendMessage(ChatColor.RED + "Enter a bracket color (lowercase)");
        p.sendMessage(ChatColor.GREEN + "a" + ChatColor.AQUA + "b" + ChatColor.RED + "c" + ChatColor.LIGHT_PURPLE + "d" + ChatColor.YELLOW + "e" + ChatColor.WHITE + "f" + ChatColor.DARK_BLUE + "1" + ChatColor.DARK_GREEN + "2" + ChatColor.DARK_AQUA + "3" + ChatColor.DARK_RED + "4" + ChatColor.DARK_PURPLE + "5" + ChatColor.GOLD + "6" + ChatColor.GRAY + "7" + ChatColor.DARK_GRAY + "8" + ChatColor.BLUE + "9" + ChatColor.BLACK + "0");
        String brackets = event.getMessage();
        event.setCancelled(true);
        if(brackets.equals("a")||brackets.equals("b")||brackets.equals("c")||brackets.equals("d")||brackets.equals("e")||brackets.equals("f")||brackets.equals("1")||brackets.equals("2")||brackets.equals("3")||brackets.equals("4")||brackets.equals("5")||brackets.equals("6")||brackets.equals("7")||brackets.equals("8")||brackets.equals("9")||brackets.equals("0")){
            UUID playerid = p.getUniqueId();
            FileConfiguration cfg = getTagsConfig();
            cfg.set("tags." + playerid + ".brackets", brackets);
            p.sendMessage(ChatColor.GREEN + "Bracket color set to: " + "" + brackets);
        }
        else{
            return;     
        }
        saveTagsFile();
        input2.remove(p);
        p.sendMessage(ChatColor.RED + "Enter a name color (lowercase)");
        p.sendMessage(ChatColor.GREEN + "a" + ChatColor.AQUA + "b" + ChatColor.RED + "c" + ChatColor.LIGHT_PURPLE + "d" + ChatColor.YELLOW + "e" + ChatColor.WHITE + "f" + ChatColor.DARK_BLUE + "1" + ChatColor.DARK_GREEN + "2" + ChatColor.DARK_AQUA + "3" + ChatColor.DARK_RED + "4" + ChatColor.DARK_PURPLE + "5" + ChatColor.GOLD + "6" + ChatColor.GRAY + "7" + ChatColor.DARK_GRAY + "8" + ChatColor.BLUE + "9");
        input3.add(p);
    }
}
ArrayList<Player> input3 = new ArrayList<Player>();
@EventHandler
public void onChat3(AsyncPlayerChatEvent event){
    Player p = event.getPlayer();
    if(input3.contains(p)){
        p.sendMessage(ChatColor.RED + "Enter a name color (lowercase)");
        p.sendMessage(ChatColor.GREEN + "a" + ChatColor.AQUA + "b" + ChatColor.RED + "c" + ChatColor.LIGHT_PURPLE + "d" + ChatColor.YELLOW + "e" + ChatColor.WHITE + "f" + ChatColor.DARK_BLUE + "1" + ChatColor.DARK_GREEN + "2" + ChatColor.DARK_AQUA + "3" + ChatColor.DARK_RED + "4" + ChatColor.DARK_PURPLE + "5" + ChatColor.GOLD + "6" + ChatColor.GRAY + "7" + ChatColor.DARK_GRAY + "8" + ChatColor.BLUE + "9");
        String name = event.getMessage();
        event.setCancelled(true);
        if(name.equals("a")||name.equals("b")||name.equals("c")||name.equals("d")||name.equals("e")||name.equals("f")||name.equals("1")||name.equals("2")||name.equals("3")||name.equals("4")||name.equals("5")||name.equals("6")||name.equals("7")||name.equals("8")||name.equals("9")){
            UUID playerid = p.getUniqueId();
            FileConfiguration cfg = getTagsConfig();
            cfg.set("tags." + playerid + ".name", name);
            p.sendMessage(ChatColor.GREEN + "Name color set to: " + "" + name);
        }
        else{
            return;     
        }
        saveTagsFile();
        input3.remove(p);
    }
}

最佳答案

我当然理解您想要完成的任务以及您的“队列”概念(即 inputinput2 等)是可靠的。然而,实际控制的实现却让一些人 假设不一定成立。

重复提示
正如 MrDarkLynx 已经指出的那样,您正在发送双重提示。幸运的是,这只是 外观问题。

事件监听器调用顺序
鉴于您的事件处理程序都具有相同的事件优先级(正常),因此不能保证您 它们按照定义的顺序调用(即 onChat()onChat2()onChat3())。

维护事件监听器的

HandlerList 确实使用数组和 ArrayList,但是 SimplePluginManager 的监听器注册没有。您的 Activity 顺序可能会改变 服务器重新启动之间。调用顺序在服务器 session 期间将保持一致, 除非它们在插件管理器中取消注册并重新注册。

取消 Activity
取消事件不会阻止您或其他插件的事件处理程序处理该事件 事件。取消仅将事件标记为已取消。您必须明确检查它

if ( event.isCancelled() )
    return;

或在事件注释中指定

@EventHandler( ignoreCancelled=true )

DIY解决方案

本质上,您想要实现的是一个管理对话的状态机 在您的插件和玩家之间,在此期间系统会提示玩家提供答案,直到完成任务 已完成或取消。

DIY 方法是实现一个实际的状态机,其 每当玩家导致某些事件发生时就会触发执行。我引用这个 解决方案作为DIY,因为有更好的解决方案,我将在下面进一步介绍。

下面的代码是事件处理程序的一个模型,它是一个单一的状态机 能够维护多个玩家的状态。请记住,虽然此代码有效, 它纯粹用于教育和演示目的。使用风险由您自行承担。

public class DyiTagController implements Listener {
    private static final String VALID_COLOR_PAT = "^[a-f0-9]$";

    /** Valid controller states*/
    private enum TagState {
        PREFIX, BRACKET, NAME, DONE, ERR
    };

    /** Registry of current player sessions */
    private final Map<Player, PlayerSession> sessions = new HashMap<>();

    /**
     * Constructs a DyiTagController and self-registers itself as event listener
     * 
     * @param plugin main plugin reference
     */
    public DyiTagController( JavaPlugin plugin ) {
        Bukkit.getPluginManager().registerEvents( this, plugin );
    }

    /**
     * Only process un-cancelled chat events. If player has an active state, handle chat
     * message as input to be further validated; otherwise, ignore it.
     * 
     * @param event chat event record
     */
    @EventHandler( ignoreCancelled = true )
    public void onPlayerChat( AsyncPlayerChatEvent event ) {
        Player player = event.getPlayer();
        if ( sessions.containsKey( event.getPlayer() ) ) {
            processResponse( player, event.getMessage() );
            event.setCancelled( true );
        }
    }


    /**
     * Handle sign interaction to start a dialog.
     * 
     * @param event player interaction record
     */
    @EventHandler( ignoreCancelled = true )
    public void onPlayerInteract( PlayerInteractEvent event ) {
        if ( event.getAction() != Action.RIGHT_CLICK_BLOCK )
            return;

        Block block = event.getClickedBlock();

        if ( block.getType() == Material.WALL_SIGN ) {
            // Other tests to make sure its the right sign
            startTagController( event.getPlayer() );
        }
    }


    /**
     * Trigger the start of a player's conversation. This would be called by your sign
     * interaction handler or started by a command.
     * 
     * @param player player for which conversation is to be started
     */
    public void startTagController( Player player ) {
        if ( sessions.containsKey( player ) )
            return; // Already has a controller

        PlayerSession sess = new PlayerSession();
        sess.state = TagState.PREFIX;
        sessions.put( player, sess );
        promptPlayer( player );
    }


    /**
     * Display the appropriate prompt to the player depending on what state they are currently
     * in.
     * 
     * @param player player in conversation
     */
    private void promptPlayer( Player player ) {
        String prompt = null;
        switch ( sessions.get( player ).state ) {
            case PREFIX:
                prompt = "Enter a prefix (enter 'clear' to quit)";
                break;
            case BRACKET:
                prompt = "Enter a bracket color";
                break;
            case NAME:
                prompt = "Enter a name color";
                break;
            default:
                cleanup( player );
                player.sendMessage( "Sorry some bad has happened, exiting tag manager" );
                throw new IllegalStateException( "Unknown tag state" );
        }

        player.sendMessage( prompt );
    }


    /**
     * Process chat response. Evaluate message and determine next valid state. If next state
     * is anything other than DONE, set player's current state to it and prompt them. This will
     * also reprompt player should they enter an invalid value.
     * 
     * @param player player responding
     * @param msg response message
     */
    private void processResponse( Player player, String msg ) {
        PlayerSession sess = sessions.get( player );
        sess.state = evaluateState( player, sess, msg );

        if ( sess.state == TagState.DONE || sess.state == TagState.ERR )
            cleanup( player );
        else
            promptPlayer( player );
    }


    /**
     * Evaluate the given message according to the current state.
     * 
     * @param player player in conversation
     * @param state current player state
     * @param msg response message to evaluate
     * @return next valid state; same state if invalidation errors; or ERR if terminal error.
     */
    private TagState evaluateState( Player player, PlayerSession sess, String msg ) {
        if ( "clear".equalsIgnoreCase( msg ) )
            return TagState.DONE;

        switch ( sess.state ) {
            case PREFIX:
                if ( validatePrefix( msg ) ) {
                    // Do something with valid prefix
                    sess.prefix = msg;
                    return TagState.BRACKET;
                }
                // Bark error at player, don't change state.
                return sess.state;

            case BRACKET:
                if ( validateColor( msg ) ) {
                    // Do something with valid bracket color
                    sess.bracket = msg;
                    return TagState.NAME;
                }
                // Bark error at player, don't change state.
                return sess.state;

            case NAME:
                if ( validateColor( msg ) ) {
                    // Do something with valid name color
                    sess.name = msg;
                    return TagState.DONE;
                }
                // Bark error at player, don't change state.
                return sess.state;

            default:
                player.sendMessage( "Sorry some bad has happened, exiting tag manager" );
                return TagState.ERR;
        }
    }


    /**
     * Determine if the given prefix is valid.
     * 
     * @param prefix prefix to evaluate
     * @return true if valid; false otherwise.
     */
    private boolean validatePrefix( String prefix ) {
        // Check whether prefix is valid. Return true if it is and false if not
        return true;
    }


    /**
     * Determine if the given color key is valid.
     * 
     * @param color color key to evaluate
     * @return true if valid; false otherwise.
     */
    private boolean validateColor( String color ) {
        return color.matches( VALID_COLOR_PAT );
    }


    /**
     * Clean up after given player has completed or terminated conversation.
     * 
     * @param player conversation player
     */
    private void cleanup( Player player ) {
        PlayerSession sess = sessions.get( player );
        sessions.remove( player );

        if ( sess.state == TagState.DONE ) {
            player.sendMessage( "Prefix selected: " + sess.prefix );
            player.sendMessage( "Bracket color selected: " + sess.bracket );
            player.sendMessage( "Name color selected: " + sess.name );
        }
    }

    protected class PlayerSession {
        public TagState state;
        public String   prefix;
        public String   bracket;
        public String   name;
    }
}

Bukkit 对话

Bukkit 的一个罕见功能,除非有人指出或仔细阅读 Bukkit 的内容,否则很多人都不会发现 源代码是其 org.bukkit.conversations.ConversationFactory 资源。它是一个框架, 专门用于您想要实现的目标。它有几个好处

  • 无需自定义控制逻辑。对话可以支持大多数类型的有向图,例如 作为状态机。
  • 无需维护播放器存储空间。对话为每个玩家提供 session 存储。
  • 默认情况下对话是模态的。所有发送给玩家的正常消息都会被抑制。 对话也可以是非模式的,允许正常的消息流动。
  • 动态提示生成,可以更好地表示对话的状态。
  • 无需跟踪。对话可能会在一段时间后超时。
  • 不提供家政服务。如果玩家注销,对话就会自行释放。

Note the use of sendRawMessage() during the conversation, as it does not check conversation state as sendMessage() does.

以下代码是使用Conversations的实现。再次强调,此代码用于教育和 演示目的。使用风险由您自行承担。

public class ConvoTagController implements Listener {
    private static final String       VALID_COLOR_PAT = "^[a-f0-9]$";
    private final ConversationFactory factory;


    public ConvoTagController( JavaPlugin plugin ) {
        this.factory = new ConversationFactory( plugin );
        Bukkit.getPluginManager().registerEvents( this, plugin );
    }


    @EventHandler( ignoreCancelled = true )
    public void onPlayerInteract( PlayerInteractEvent event ) {
        if ( event.getAction() != Action.RIGHT_CLICK_BLOCK )
            return;

        Block block = event.getClickedBlock();

        Player player = event.getPlayer();
        if ( block.getType() == Material.WALL_SIGN ) {
            // Other tests to make sure its the right sign
            if ( !player.isConversing() )
                startConversation( player );
        }
    }


    /**
     * Trigger the start of a player's conversation. This would be called by your sign
     * interaction handler or started by a command.
     * 
     * @param player player for which conversation is to be started
     */
    private void startConversation( Player player ) {
        factory.withFirstPrompt( new PrefixPrompt() ).withPrefix( new TagPrefix() )
                .withLocalEcho( false ).withEscapeSequence( "clear" ).buildConversation( player )
                .begin();
    }

    public class TagPrefix implements ConversationPrefix {

        @Override
        public String getPrefix( ConversationContext context ) {
            return ChatColor.BLUE + "[TAGS] " + ChatColor.WHITE;
        }

    }

    public class PrefixPrompt extends ValidatingPrompt {

        @Override
        public String getPromptText( ConversationContext context ) {
            return "Enter a prefix (type 'clear' to quit)";
        }


        @Override
        protected boolean isInputValid( ConversationContext context, String input ) {
            // TODO Test to make sure prefix is valid
            return true;
        }


        @Override
        protected Prompt acceptValidatedInput( ConversationContext context, String input ) {
            context.setSessionData( "prefix", input );
            return new BracketColor();
        }
    }

    public class BracketColor extends ValidatingPrompt {

        @Override
        public String getPromptText( ConversationContext context ) {
            return "Enter a bracket color (type 'clear' to quit)";
        }


        @Override
        protected boolean isInputValid( ConversationContext context, String input ) {
            return input.matches( VALID_COLOR_PAT );
        }


        @Override
        protected Prompt acceptValidatedInput( ConversationContext context, String input ) {
            context.setSessionData( "bracket", input );
            return new NameColor();
        }

    }

    public class NameColor extends ValidatingPrompt {

        @Override
        public String getPromptText( ConversationContext context ) {
            return "Enter a name color (type 'clear' to quit)";
        }


        @Override
        protected boolean isInputValid( ConversationContext context, String input ) {
            return input.matches( VALID_COLOR_PAT );
        }


        @Override
        protected Prompt acceptValidatedInput( ConversationContext context, String input ) {
            context.setSessionData( "name", input );
            return new Done();
        }
    }

    public class Done implements Prompt {

        @Override
        public String getPromptText( ConversationContext context ) {
            Conversable c = context.getForWhom();
            String prefix = (String) context.getSessionData( "prefix" );
            String bracket = (String) context.getSessionData( "bracket" );
            String name = (String) context.getSessionData( "name" );

            c.sendRawMessage( "Prefix selected: " + prefix );
            c.sendRawMessage( "Bracket color selected: " + bracket );
            c.sendRawMessage( "Name color selected: " + name );

            if ( c instanceof Player ) {
                Player p = (Player) c;
                // Do whatever you need to do to player specifically.
            }
            return "Thank you, your settings will now take effect";
        }


        @Override
        public boolean blocksForInput( ConversationContext context ) {
            return false;
        }


        @Override
        public Prompt acceptInput( ConversationContext context, String input ) {
            return END_OF_CONVERSATION;
        }
    }
}

关于java - Spigot API - 从玩家获取多个字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42430102/

相关文章:

java - 确定 MQ 消息队列何时为空(所有消息均已读取)

java - ActiveMQ 在 Spring Boot 中延迟传递消息

php - 如何从该字符串返回多维数组?

java - Bukkit - EventHandler 方法出错

java - 是否有用于 Java (Android) 的 HTTP 客户端类?

java - 为部署到 heroku tomcat 的 Java Web WAR 找不到添加依赖项的类

java - 如何在 Minecraft 1.10 上编写速度破解代码?

java - 即使数据存在,我的 MongoDB .find() 方法也返回 null

java - 在 For 循环中等待 Bukkit 任务完成

java - 如何从YAML中获取有序的HashMap?