Anyone Know Anything about Bukkit's Conversation API?

Discussion in 'Plugin Development' started by Kodfod, Jul 19, 2012.

Thread Status:
Not open for further replies.
  1. Offline

    Kodfod

    Here is what i get when i try to hook into it.

    Code:JAVA
    1. package me.kodfod.test;
    2.  
    3. import org.bukkit.conversations.Conversable;
    4. import org.bukkit.conversations.Conversation;
    5. import org.bukkit.conversations.ConversationAbandonedEvent;
    6. import org.bukkit.plugin.java.JavaPlugin;
    7.  
    8. public class Main extends JavaPlugin implements Conversable {
    9.  
    10. @Override
    11. public void abandonConversation(Conversation arg0) {
    12. // TODO Auto-generated method stub
    13.  
    14. }
    15.  
    16. @Override
    17. public void abandonConversation(Conversation arg0,
    18. ConversationAbandonedEvent arg1) {
    19. // TODO Auto-generated method stub
    20.  
    21. }
    22.  
    23. @Override
    24. public void acceptConversationInput(String arg0) {
    25. // TODO Auto-generated method stub
    26.  
    27. }
    28.  
    29. @Override
    30. public boolean beginConversation(Conversation arg0) {
    31. // TODO Auto-generated method stub
    32. return false;
    33. }
    34.  
    35. @Override
    36. public boolean isConversing() {
    37. // TODO Auto-generated method stub
    38. return false;
    39. }
    40.  
    41. @Override
    42. public void sendRawMessage(String arg0) {
    43. // TODO Auto-generated method stub
    44.  
    45. }
    46.  
    47. }


    If anyone has any insight, please post it here.

    I'm just messing around with this to see it's potential so, no rush =)
     
  2. Offline

    Sagacious_Zed Bukkit Docs

    This is something about the API
    http://forums.bukkit.org/threads/conversations-api-q-a-i-wrote-the-conversations-api-ama.68451/
     
  3. Offline

    Mitsugaru

    Kodfod

    Well, first thing is that a Conversable is an object that you can start a conversation with... so, you would have your main plugin implement it.

    Rather, to prepare for creating conversations and prompts, you will need an instance of the ConversationFactory.

    Code:
    ConversationFactory factory;
     
    //Constructor or whatever init method
    factory = new ConversationFactory(plugin)
     
    //Where plugin = your JavaPlugin class instance
    Once you have that, you can create conversations to use on objects that implement Conversable, such as a player.

    What you'll want custom classes of are Prompts, and there are a few basic ones that you can use for standard responses / situations. Think of a prompt as a node in a conversation tree / graph. It displays text to the user and (optionally) awaits input. Based on the input (and how you choose to handle it) it can display a different prompt (or the same one), request the input again (if it wasn't a valid response), or end the conversation.

    Here's a quick example of calling a custom prompt on a player.
    Code:
    if (sender instanceof Player)
    {
        final Map<Object, Object> map = new HashMap<Object, Object>();
        map.put("data", "first");
        Conversation conv = factory
                .withFirstPrompt(new TestPrompt())
                .withPrefix(new ConversationPrefix() {
     
                    @Override
                    public String getPrefix(ConversationContext arg0)
                    {
                        return ChatColor.GREEN + plugin.TAG
                                + ChatColor.WHITE + " ";
                    }
     
                }).withInitialSessionData(map).withLocalEcho(false)
                .buildConversation((Player) sender);
        conv.addConversationAbandonedListener(new ConversationAbandonedListener() {
     
            @Override
            public void conversationAbandoned(
                    ConversationAbandonedEvent event)
            {
                if (event.gracefulExit())
                {
                    plugin.getLogger().info("graceful exit");
                }
                try
                {
                    plugin.getLogger().info(
                            "Canceller"
                                    + event.getCanceller()
                                            .toString());
                }
                catch (NullPointerException n)
                {
                    // Was null
                    plugin.getLogger().info(
                            "null Canceller");
                }
            }
        });
        conv.begin();
    }
    So, you create a Conversation using the factory, and you give the factory as many details so that it can build the Conversation object how you need it. I'll go over some of the method calls...

    So to give the Conversation a starting point, you need to give it the first prompt to show:
    .withFirstPrompt(new TestPrompt())

    In the above case, I just created a new instance of my custom Prompt (which I'll show later).

    You can optionally force a conversation to always have a prefix, using the method:
    .withPrefix(new ConversationPrefix(){})

    I opted for an anonymous inner class, for simplicity. You could make a custom class that implements ConversationPrefix, but, sorta overkill as there's only one method and that's to return the prefix to use.

    More importantly, a Conversation has its own HashMap that holds whatever data you need. If you need your Conversation to have starting data, you would create your Map, populate it with whatever you need, and then give it to the Conversation as so:
    .withInitialSessionData(map)

    Also, you can set whether the player sees their responses in chat or not using the following method:
    .withLocalEcho(boolean)

    Lastly, you tell it to build the Conversation with a given Conversable object:
    .buildConversation((Player) sender);

    I casted the sender to player because this was inside the onCommand method. Player objects implement Conversable. The only other one at this time is ConsoleCommandSender.

    The second part lets me know how the conversation ended, using the ConversationAbandonedListener. That way, you can tell if the conversation naturally ended with a prompt that ended the conversation, or if it was cancelled by something else (like the player disconnecting, etc). You just attach that listener to the Conversation itself and implement the listener however you need to.

    Lastly, to actually start the conversation, you call:
    conv.begin();

    And the conversation will start with the first prompt on the player you gave it to.

    Now, for prompts, which are going to have (or at least have a reference to) pretty much anything conversation related that you want to display to the player and retrieve information from the player.

    Here's a super simple prompt, which I used in the above code:
    Code:
    package com.mitsugaru.KarmicQuest.conversations;
     
    import org.bukkit.Bukkit;
    import org.bukkit.conversations.ConversationContext;
    import org.bukkit.conversations.Prompt;
    import org.bukkit.conversations.ValidatingPrompt;
     
    public class TestPrompt extends ValidatingPrompt
    {
     
        @Override
        public String getPromptText(ConversationContext context)
        {
            Bukkit.getLogger().info("hit get prompt text for TestPrompt");
            return "Data: " + context.getSessionData("data");
        }
     
        @Override
        protected Prompt acceptValidatedInput(ConversationContext context, String in)
        {
            Bukkit.getLogger().info("hit accept validated input for TestPrompt");
            if(in.equalsIgnoreCase("stop") || in.equalsIgnoreCase("end") || in.equalsIgnoreCase("quit"))
            {
                return END_OF_CONVERSATION;
            }
            else
            {
                context.setSessionData("data", in);
            }
            return this;
        }
     
        @Override
        protected boolean isInputValid(ConversationContext context, String in)
        {
            Bukkit.getLogger().info("hit input valid for TestPrompt");
            return true;
        }
     
    }
    getPromptText() is called when your prompt will display text to the one in the conversation.

    The acceptValidatedInput() is called when user input given is deemed valid and will return the next prompt to display to the user.

    This basic prompt just returns what the player typed back to them, unless it was one of the stop inputs. In that case, it returns the END_OF_CONVERSATION prompt, which will do exactly what its name implies.

    Remember that HashMap we gave to the Conversation? Well, to get / modify that data, we grab it from the ConversationContext:
    context.getSessionData(key);

    Likewise, to add / update the data:
    context.setSessionData(key, object);

    Note, players in a conversation will not hear standard player chat. They will be locked in the conversation's chat until it ends. (Last I remembered, at least.)

    Also, if you need to send a message to a Conversable, you do not use the standard .sendMessage() that you would normally use. Instead, you use the sendRawMessage() within the Conversable class. You treat it the same way though, with ChatColors / formatting and what not.

    Ignore the fact that I used bukkit's main logger, this was strictly for testing purposes only.

    Credits go to kumpelblase2 for his presentation. Sorry if I missed anything, running short on time. However, it ought to be a decent overview of how to create and work with conversations. Definitely look over the javadocs for prompts and the factory for other settings, such as time out, etc.
     
    Atakyn, felixfritz, Maximvdw and 2 others like this.
  4. Offline

    Kodfod

    @Mitsugaru Thanks!
     
  5. kumpelblase2 has made an video how to use them, if you still have question, he may able to help you
     
  6. Offline

    Kodfod

    Thanks, will search for them. This sounds good
     
  7. I don't think you did. I would like to add one thing, though.

    This can be done a lot easier using an escape sequence (as it's called in the ConversationFactory). So what you can do with this is give the factory a string before building the conversation and whenever that string gets written by the player, the conversation automatically gets cancelled (same as the exact match conversation canceller).

    E.g.:
    Code:
    Conversation conv = factory
                .withFirstPrompt(new TestPrompt())
                .withEscapeSequence("/exit")
                .buildConversation((Player) sender);
    Whenever the player now types '/exit' in the chat, the conversation will end instantly. No checks of the input in your prompt will get executed. It wouldn't exit if you'd write like '/exit no I don't want'.

    Nothing to say anymore, as everything else has been well explained by Mitsugaru.
     
Thread Status:
Not open for further replies.

Share This Page