From 1161de3f638e187e17677697b3663e13381b9d3d Mon Sep 17 00:00:00 2001
From: rmichela <deltahat@gmail.com>
Date: Thu, 19 Jan 2012 03:32:05 -0500
Subject: [PATCH] [Bleeding] Added Conversations API. Addresses BUKKIT-864

---
 .../net/minecraft/server/MinecraftServer.java |  7 +--
 .../minecraft/server/NetServerHandler.java    | 12 +++-
 .../org/bukkit/craftbukkit/CraftServer.java   | 11 +++-
 .../command/ColouredConsoleSender.java        | 17 ++---
 .../command/CraftConsoleCommandSender.java    | 24 +++++++
 .../conversations/ConversationTracker.java    | 62 +++++++++++++++++++
 .../craftbukkit/entity/CraftPlayer.java       | 31 +++++++++-
 7 files changed, 147 insertions(+), 17 deletions(-)
 create mode 100644 src/main/java/org/bukkit/craftbukkit/conversations/ConversationTracker.java

diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index b4e886a637..be06b2c956 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1,6 +1,5 @@
 package net.minecraft.server;
 
-import java.awt.GraphicsEnvironment;
 import java.io.File;
 import java.io.IOException;
 import java.net.InetAddress;
@@ -570,8 +569,8 @@ public class MinecraftServer implements Runnable, ICommandListener, IMinecraftSe
             servercommand = new ServerCommand(event.getCommand(), servercommand.source);
             // CraftBukkit end
 
-            // this.consoleCommandHandler.handle(servercommand); // CraftBukkit - Removed its now called in server.dispatchCommand
-            this.server.dispatchCommand(this.console, servercommand); // CraftBukkit
+            // this.consoleCommandHandler.handle(servercommand); // CraftBukkit - Removed its now called in server.dispatchServerCommand
+            this.server.dispatchServerCommand(this.console, servercommand); // CraftBukkit
         }
     }
 
@@ -715,7 +714,7 @@ public class MinecraftServer implements Runnable, ICommandListener, IMinecraftSe
         this.server.getPluginManager().callEvent(event);
         ServerCommand servercommand = new ServerCommand(event.getCommand(), RemoteControlCommandListener.instance);
         // this.consoleCommandHandler.handle(new ServerCommand(s, RemoteControlCommandListener.instance)); // CraftBukkit - removed
-        this.server.dispatchCommand(this.remoteConsole, servercommand); // CraftBukkit
+        this.server.dispatchServerCommand(this.remoteConsole, servercommand); // CraftBukkit
         // CraftBukkit end
         return RemoteControlCommandListener.instance.b();
     }
diff --git a/src/main/java/net/minecraft/server/NetServerHandler.java b/src/main/java/net/minecraft/server/NetServerHandler.java
index 316de4afc8..8f27a7ee26 100644
--- a/src/main/java/net/minecraft/server/NetServerHandler.java
+++ b/src/main/java/net/minecraft/server/NetServerHandler.java
@@ -14,6 +14,7 @@ import org.bukkit.ChatColor;
 import org.bukkit.craftbukkit.ChunkCompressionThread;
 import org.bukkit.Location;
 import org.bukkit.command.CommandException;
+import org.bukkit.conversations.Conversable;
 import org.bukkit.craftbukkit.CraftWorld;
 import org.bukkit.craftbukkit.inventory.CraftInventoryView;
 import org.bukkit.craftbukkit.inventory.CraftItemStack;
@@ -145,6 +146,7 @@ public class NetServerHandler extends NetHandler implements ICommandListener {
             if (leaveMessage != null) {
                 this.minecraftServer.serverConfigurationManager.sendAll(new Packet3Chat(leaveMessage));
             }
+            getPlayer().disconnect(s);
             // CraftBukkit end
 
             this.minecraftServer.serverConfigurationManager.disconnect(this.player);
@@ -750,6 +752,11 @@ public class NetServerHandler extends NetHandler implements ICommandListener {
                 return false;
             }
 
+            if (getPlayer().isConversing()) {
+                getPlayer().acceptConversationInput(s);
+                return true;
+            }
+
             if (s.startsWith("/")) {
                 this.handleCommand(s);
                 return true;
@@ -801,7 +808,7 @@ public class NetServerHandler extends NetHandler implements ICommandListener {
         }
         // CraftBukkit end
 
-        /* // CraftBukkit start - No longer needed as we have already handled it in server.dispatchCommand above.
+        /* // CraftBukkit start - No longer needed as we have already handled it in server.dispatchServerCommand above.
         if (s.toLowerCase().startsWith("/me ")) {
             s = "* " + this.player.name + " " + s.substring(s.indexOf(" ")).trim();
             logger.info(s);
@@ -911,6 +918,9 @@ public class NetServerHandler extends NetHandler implements ICommandListener {
     }
 
     public void a(Packet255KickDisconnect packet255kickdisconnect) {
+        // CraftBukkit start
+        getPlayer().disconnect("disconnect.quitting");
+        // CraftBukkit end
         this.networkManager.a("disconnect.quitting", new Object[0]);
     }
 
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 7ff27f264a..75fb8d537d 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -58,6 +58,7 @@ import org.bukkit.command.SimpleCommandMap;
 import org.bukkit.configuration.ConfigurationSection;
 import org.bukkit.configuration.file.YamlConfiguration;
 import org.bukkit.configuration.serialization.ConfigurationSerialization;
+import org.bukkit.conversations.Conversable;
 import org.bukkit.craftbukkit.help.SimpleHelpMap;
 import org.bukkit.craftbukkit.inventory.CraftFurnaceRecipe;
 import org.bukkit.craftbukkit.inventory.CraftInventoryCustom;
@@ -443,7 +444,15 @@ public final class CraftServer implements Server {
     }
 
     // NOTE: Should only be called from MinecraftServer.b()
-    public boolean dispatchCommand(CommandSender sender, ServerCommand serverCommand) {
+    public boolean dispatchServerCommand(CommandSender sender, ServerCommand serverCommand) {
+        if (sender instanceof Conversable) {
+            Conversable conversable = (Conversable)sender;
+
+            if (conversable.isConversing()) {
+                conversable.acceptConversationInput(serverCommand.command);
+                return true;
+            }
+        }
         return dispatchCommand(sender, serverCommand.command);
     }
 
diff --git a/src/main/java/org/bukkit/craftbukkit/command/ColouredConsoleSender.java b/src/main/java/org/bukkit/craftbukkit/command/ColouredConsoleSender.java
index f1deb305c9..c5c817c8ec 100644
--- a/src/main/java/org/bukkit/craftbukkit/command/ColouredConsoleSender.java
+++ b/src/main/java/org/bukkit/craftbukkit/command/ColouredConsoleSender.java
@@ -42,16 +42,17 @@ public class ColouredConsoleSender extends CraftConsoleCommandSender {
     @Override
     public void sendMessage(String message) {
         if (terminal.isANSISupported()) {
-            String result = message;
-
-            for (ChatColor color : colors) {
-                if (replacements.containsKey(color)) {
-                    result = result.replaceAll(color.toString(), replacements.get(color));
-                } else {
-                    result = result.replaceAll(color.toString(), "");
+            if (!conversationTracker.isConversingModaly()) {
+                String result = message;
+                for (ChatColor color : colors) {
+                    if (replacements.containsKey(color)) {
+                        result = result.replaceAll(color.toString(), replacements.get(color));
+                    } else {
+                        result = result.replaceAll(color.toString(), "");
+                    }
                 }
+                System.out.println(result + ANSICodes.attrib(0));
             }
-            System.out.println(result + ANSICodes.attrib(0));
         } else {
             super.sendMessage(message);
         }
diff --git a/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java b/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java
index dbcfddc9ea..00f18d1d91 100644
--- a/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java
+++ b/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java
@@ -2,17 +2,25 @@ package org.bukkit.craftbukkit.command;
 
 import org.bukkit.ChatColor;
 import org.bukkit.command.ConsoleCommandSender;
+import org.bukkit.conversations.Conversation;
+import org.bukkit.craftbukkit.conversations.ConversationTracker;
 
 /**
  * Represents CLI input from a console
  */
 public class CraftConsoleCommandSender extends ServerCommandSender implements ConsoleCommandSender {
 
+    protected ConversationTracker conversationTracker = new ConversationTracker();
+
     protected CraftConsoleCommandSender() {
         super();
     }
 
     public void sendMessage(String message) {
+        sendRawMessage(message);
+    }
+    
+    public void sendRawMessage(String message) {
         System.out.println(ChatColor.stripColor(message));
     }
 
@@ -33,4 +41,20 @@ public class CraftConsoleCommandSender extends ServerCommandSender implements Co
     public void setOp(boolean value) {
         throw new UnsupportedOperationException("Cannot change operator status of server console");
     }
+
+    public boolean beginConversation(Conversation conversation) {
+        return conversationTracker.beginConversation(conversation);
+    }
+
+    public void abandonConversation(Conversation conversation) {
+        conversationTracker.abandonConversation(conversation);
+    }
+
+    public void acceptConversationInput(String input) {
+        conversationTracker.acceptConversationInput(input);
+    }
+
+    public boolean isConversing() {
+        return conversationTracker.isConversing();
+    }
 }
diff --git a/src/main/java/org/bukkit/craftbukkit/conversations/ConversationTracker.java b/src/main/java/org/bukkit/craftbukkit/conversations/ConversationTracker.java
new file mode 100644
index 0000000000..46771728df
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/conversations/ConversationTracker.java
@@ -0,0 +1,62 @@
+package org.bukkit.craftbukkit.conversations;
+
+import org.bukkit.conversations.Conversation;
+
+import java.util.Deque;
+import java.util.LinkedList;
+
+/**
+ */
+public class ConversationTracker {
+
+    private Deque<Conversation> conversationQueue = new LinkedList<Conversation>();
+
+    public synchronized boolean beginConversation(Conversation conversation) {
+        if (!conversationQueue.contains(conversation)) {
+            conversationQueue.addLast(conversation);
+            if (conversationQueue.getFirst() == conversation) {
+                conversation.begin();
+                conversation.outputNextPrompt();
+                return true;
+            }
+        }
+        return true;
+    }
+
+    public synchronized void abandonConversation(Conversation conversation) {
+        if (!conversationQueue.isEmpty()) {
+            if (conversationQueue.getFirst() == conversation) {
+                conversation.abandon();
+            }
+            if (conversationQueue.contains(conversation)) {
+                conversationQueue.remove(conversation);
+            }
+            if (!conversationQueue.isEmpty()) {
+                conversationQueue.getFirst().outputNextPrompt();
+            }
+        }
+    }
+
+    public synchronized void abandonAllConversations() {
+
+        Deque<Conversation> oldQueue = conversationQueue;
+        conversationQueue = new LinkedList<Conversation>();
+        for(Conversation conversation : oldQueue) {
+            conversation.abandon();
+        }
+    }
+
+    public synchronized void acceptConversationInput(String input) {
+        if (isConversing()) {
+            conversationQueue.getFirst().acceptInput(input);
+        }
+    }
+
+    public synchronized boolean isConversing() {
+        return !conversationQueue.isEmpty();
+    }
+
+    public synchronized boolean isConversingModaly() {
+        return isConversing() && conversationQueue.getFirst().isModal();
+    }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index b065d47dfd..039cf9b57f 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -37,6 +37,8 @@ import org.bukkit.Statistic;
 import org.bukkit.World;
 import org.bukkit.block.BlockFace;
 import org.bukkit.configuration.serialization.DelegateDeserialization;
+import org.bukkit.conversations.Conversation;
+import org.bukkit.craftbukkit.conversations.ConversationTracker;
 import org.bukkit.craftbukkit.CraftEffect;
 import org.bukkit.craftbukkit.CraftOfflinePlayer;
 import org.bukkit.craftbukkit.CraftServer;
@@ -59,6 +61,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
     private long firstPlayed = 0;
     private long lastPlayed = 0;
     private boolean hasPlayedBefore = false;
+    private ConversationTracker conversationTracker = new ConversationTracker();
     private Set<String> channels = new HashSet<String>();
     private Map<String, Player> hiddenPlayers = new MapMaker().softValues().makeMap();
     private int hash = 0;
@@ -133,9 +136,11 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
     }
 
     public void sendMessage(String message) {
-        this.sendRawMessage(message);
+        if (!conversationTracker.isConversingModaly()) {
+            this.sendRawMessage(message);
+        }
     }
-    
+
     public void sendMessage(String[] messages) {
         for (String message : messages) {
             sendMessage(message);
@@ -692,6 +697,22 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         data.setLong("lastPlayed", System.currentTimeMillis());
     }
 
+    public boolean beginConversation(Conversation conversation) {
+        return conversationTracker.beginConversation(conversation);
+    }
+
+    public void abandonConversation(Conversation conversation) {
+        conversationTracker.abandonConversation(conversation);
+    }
+
+    public void acceptConversationInput(String input) {
+        conversationTracker.acceptConversationInput(input);
+    }
+
+    public boolean isConversing() {
+        return conversationTracker.isConversing();
+    }
+
     public void sendPluginMessage(Plugin source, String channel, byte[] message) {
         StandardMessenger.validatePluginMessage(server.getMessenger(), source, channel, message);
 
@@ -773,4 +794,8 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         getHandle().setContainerData(container, prop.getId(), value);
         return true;
     }
-}
+
+    public void disconnect(String reason) {
+        conversationTracker.abandonAllConversations();
+    }
+}
\ No newline at end of file