Author: bdonlan
Date: 2005-05-24 22:59:42 -0400 (Tue, 24 May 2005)
New Revision: 731

Added:
   trunk/clients/Javer2/src/org/haverdev/haver/server/DefaultLobby.java
   trunk/clients/Javer2/src/org/haverdev/haver/server/InitContext.java
   trunk/clients/Javer2/src/org/haverdev/haver/server/LoginContext.java
   trunk/clients/Javer2/src/org/haverdev/haver/server/MergingContext.java
   trunk/clients/Javer2/src/org/haverdev/haver/server/NormalContext.java
   trunk/clients/Javer2/src/org/haverdev/haver/server/UserCommandHandler.java
   trunk/clients/Javer2/src/org/haverdev/haver/server/UserCommandReflect.java
   trunk/clients/Javer2/src/org/haverdev/haver/server/UserContextFactory.java
   trunk/clients/Javer2/src/org/haverdev/haver/server/UserEntity.java
   trunk/clients/Javer2/src/org/haverdev/javer2/ChannelPane.form
   trunk/clients/Javer2/src/org/haverdev/javer2/ChannelPane.java
   trunk/clients/Javer2/src/org/haverdev/javer2/JaverForm.form
   trunk/clients/Javer2/src/org/haverdev/javer2/JaverForm.java
   trunk/clients/Javer2/src/org/haverdev/javer2/Pane.java
   trunk/clients/Javer2/src/org/haverdev/javer2/QueryPane.java
   trunk/clients/Javer2/src/org/haverdev/javer2/ServerPane.java
   trunk/clients/Javer2/src/org/haverdev/javer2/TextPane.form
   trunk/clients/Javer2/src/org/haverdev/javer2/TextPane.java
Modified:
   trunk/clients/Javer2/nbproject/project.properties
   trunk/clients/Javer2/server.conf
   trunk/clients/Javer2/src/org/haverdev/haver/Callback.java
   trunk/clients/Javer2/src/org/haverdev/haver/Client.java
   trunk/clients/Javer2/src/org/haverdev/haver/server/Channel.java
   trunk/clients/Javer2/src/org/haverdev/haver/server/EntityLoader.java
   trunk/clients/Javer2/src/org/haverdev/haver/server/Lobby.java
   trunk/clients/Javer2/src/org/haverdev/haver/server/Main.java
   trunk/clients/Javer2/src/org/haverdev/haver/server/Misc.java
   trunk/clients/Javer2/src/org/haverdev/haver/server/UserConnection.java
   trunk/clients/Javer2/src/org/haverdev/javer2/CMod.java
Log:
Lots of things happened. Like the config file, that's important. Or the client 
GUI (still woefully incomplete).

Modified: trunk/clients/Javer2/nbproject/project.properties
===================================================================
--- trunk/clients/Javer2/nbproject/project.properties   2005-05-24 21:42:18 UTC 
(rev 730)
+++ trunk/clients/Javer2/nbproject/project.properties   2005-05-25 02:59:42 UTC 
(rev 731)
@@ -37,7 +37,7 @@
 javadoc.use=true
 javadoc.version=false
 javadoc.windowtitle=
-main.class=org.haverdev.haver.server.AcceptLoop
+main.class=org.haverdev.javer2.JaverForm
 manifest.file=manifest.mf
 platform.active=Java_HotSpot_TM__Client_VM_1.4.2_08-b03
 run.classpath=\

Modified: trunk/clients/Javer2/server.conf
===================================================================
--- trunk/clients/Javer2/server.conf    2005-05-24 21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/server.conf    2005-05-25 02:59:42 UTC (rev 731)
@@ -1,11 +1,56 @@
 #Tue May 24 16:20:29 EDT 2005
+
+# This file is absolutely vital for the server to start. Sorry.
+
+# Which port to listen on
 haver.ListenPort=7070
+
+# Time after a S: PING before disconnect
+haver.PingTimeout=30
+
+# Time after a C: PONG before the next ping
+haver.PingInterval=60
+
+# The amount of time the full login negotiation (from connect to S: ACCEPT) is 
allowed
+haver.HandshakeTimeout=120
+
+# Default chat channels
 haver.ChatChannels=lobby,conspiracy,haver,creatures
 
+# The lobby implementation.
+haver.Lobby=org.haverdev.haver.server.DefaultLobby
+
+# State configurations. You can list more than one command handler here and
+# they will be tried in listed order
+haver.state.init=init
+haver.state.login=login
+haver.state.normal=normal
+
+# The implementation class
+init.class=org.haverdev.haver.server.InitContext
+# The next class in the state machine
+init.successor=login
+
+login.class=org.haverdev.haver.server.LoginContext
+login.successor=normal
+
+normal.class=org.haverdev.haver.server.NormalContext
+
+# Optional stugg
+
+# Plugin classes to load
 haver.Plugins=org.haverdev.haver.server.EntityLoader
-org.haverdev.haver.server.EntityLoader.entities=&stats
-&stats.class=org.haverdev.haver.server.StatsBot
 
+# Entities to load
+org.haverdev.haver.server.EntityLoader.entities=stats
+
+# Entity implementation
+stats.class=org.haverdev.haver.server.StatsBot
+
+# What string to pass to the constructor
+stats.argument=&StatsBot
+
+# Defaults
 log4j.rootLogger=DEBUG, __A1
 log4j.appender.__A1=org.apache.log4j.ConsoleAppender
 log4j.appender.__A1.layout=org.apache.log4j.PatternLayout

Modified: trunk/clients/Javer2/src/org/haverdev/haver/Callback.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/Callback.java   2005-05-24 
21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/haver/Callback.java   2005-05-25 
02:59:42 UTC (rev 731)
@@ -81,4 +81,7 @@
     public void onPart(Client source, String channel, String who) throws 
Throwable {}
     
     public void onQuit(Client source, String who, String type, String detail) 
throws Throwable {}
+    
+    // XXX: this is a hack and should not be used in apps
+    void onFail(Client source, String[] args) throws Throwable {};
 }

Modified: trunk/clients/Javer2/src/org/haverdev/haver/Client.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/Client.java     2005-05-24 
21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/haver/Client.java     2005-05-25 
02:59:42 UTC (rev 731)
@@ -32,6 +32,10 @@
     String host;
     int port;
     
+    public String getName() {
+        return name;
+    }
+    
     /**
      * Mutable boolean flag
      */
@@ -336,6 +340,20 @@
                 }
             }
         }
+        
+        void onFail(Client source, String[] args) throws IOException {
+            Iterator i = iterator();
+            while (i.hasNext()) {
+                Callback c = (Callback)i.next();
+                try {
+                    c.onFail(source, args);
+                } catch (IOException e) {
+                    throw e;
+                } catch (Throwable e) {
+                    e.printStackTrace();
+                }
+            }
+        }
       
     }
     
@@ -376,6 +394,7 @@
             ioLoop(new Flag());
         } catch (IOException e) {
             try {
+                // XXX: may be called more than once
                 dist.onDisconnected(this, e);
             } catch (IOException e2) {
             }
@@ -388,7 +407,7 @@
      * @param args Arguments of the line to send
      * @throws java.io.IOException 
      */
-    protected synchronized void sendLine(String[] args) throws IOException {
+    protected synchronized void sendLine(String[] args) {
         if (writer != null) {
             String l = encodeLine(args);
             System.out.print("C: " + l);
@@ -468,6 +487,11 @@
          */
         private Object ret = null;
         
+        protected synchronized void fail(IOException e) {
+            fail = e;
+            done(null);
+        }
+        
         public SyncMonitor() {
             addNotify(this);
         }
@@ -487,13 +511,11 @@
         public synchronized void onDisconnected(Client source, IOException e) {
             if (e == null)
                 e = new IOException("Disconnected unexpectedly");
-            fail = e;
-            done(null);
+            fail(e);
         }
         
         public synchronized void onConnectFailed(Client source, IOException e) 
{
-            fail = e;
-            done(null);
+            fail(e);
         }
         
         /**
@@ -538,9 +560,14 @@
             source.ident(name);
         }
 
-        public synchronized void onAccept(Client source, String name) {
+        public void onAccept(Client source, String name) {
             done(null);
         }
+        
+        void onFail(Client source, String[] args) {
+            // XXX
+            fail(new IOException("Something broke: " + args[2]));
+        }
     }
     
     /**
@@ -649,7 +676,7 @@
      * @param namespace Namespace to filter results by
      * @throws java.io.IOException 
      */
-    public void requestList(String channel, String namespace) throws 
IOException {
+    public void requestList(String channel, String namespace) {
         String[] cmd = {"LIST", channel, namespace};
         sendLine(cmd);
     }
@@ -687,6 +714,11 @@
                 done(list);
             }
         }
+        void onFail(Client source, String[] args) {
+            // XXX
+            if (args.length >= 4 && args[1].equals("LIST") && 
args[3].equals(channel))
+                fail(new IOException("Something broke: " + args[2]));
+        }
     }
     
     /**
@@ -719,7 +751,7 @@
         dist.onPublicMessage(this, channel, from, type, margs);
     }
     
-    public void sendPublicMessage(String channel, String type, String[] margs) 
throws IOException {
+    public void sendPublicMessage(String channel, String type, String[] margs) 
 {
         String[] cmd = new String[margs.length + 3];
         cmd[0] = "IN";
         cmd[1] = channel;
@@ -728,7 +760,7 @@
         sendLine(cmd);
     }
     
-    public void sendPublicMessage(String channel, String type, String arg) 
throws IOException {
+    public void sendPublicMessage(String channel, String type, String arg) {
         String[] args = {arg};
         sendPublicMessage(channel, type, args);
     }
@@ -747,7 +779,7 @@
         dist.onPrivateMessage(this, user, type, margs);
     }
     
-    public void sendPrivateMessage (String target, String type, String[] 
margs) throws IOException {
+    public void sendPrivateMessage (String target, String type, String[] 
margs) {
         String[] args = new String[margs.length + 3];
         args[0] = "TO";
         args[1] = target;
@@ -756,7 +788,7 @@
         sendLine(args);
     }
     
-    public void sendPrivateMessage (String target, String type, String arg) 
throws IOException {
+    public void sendPrivateMessage (String target, String type, String arg) {
         String[] args = {arg};
         sendPrivateMessage(target, type, args);
     }
@@ -780,7 +812,7 @@
         dist.onQuit(this, who, type, detail);
     }
     
-    public void sendJoin(String channel) throws IOException {
+    public void sendJoin(String channel) {
         String[] cmd = {"JOIN", channel};
         sendLine(cmd);
     }
@@ -797,6 +829,12 @@
                 this.done(null);
             }
         }
+        
+        void onFail(Client source, String[] args) {
+            // XXX
+            if (args.length >= 4 && args[1].equals("JOIN") && 
args[3].equals(channel))
+                fail(new IOException("Something broke: " + args[2]));
+        }
     }
     
     public void syncJoin(String channel) throws IOException, 
InterruptedException {
@@ -822,6 +860,12 @@
                 this.done(null);
             }
         }
+        
+        void onFail(Client source, String[] args) {
+            // XXX
+            if (args.length >= 4 && args[1].equals("PART") && 
args[3].equals(channel))
+                fail(new IOException("Something broke: " + args[2]));
+        }
     }
     
     public void syncPart(String channel) throws IOException, 
InterruptedException {
@@ -834,4 +878,9 @@
         killConnection();
     }
     
+    public void handle_FAIL(String[] args) throws IOException {
+        // XXX
+        dist.onFail(this, args);
+    }
+    
 }

Modified: trunk/clients/Javer2/src/org/haverdev/haver/server/Channel.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/server/Channel.java     
2005-05-24 21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/haver/server/Channel.java     
2005-05-25 02:59:42 UTC (rev 731)
@@ -5,7 +5,7 @@
  */
 
 package org.haverdev.haver.server;
-
+import org.haverdev.haver.server.exceptions.*;
 /**
  * A representation of a Haver channel
  * @author bdonlan
@@ -64,17 +64,17 @@
      * @param from Entity originating the message
      * @param args The message itself
      */
-    public void distributePublicMessage(Entity from, String[] args);
+    public void distributePublicMessage(Entity from, String[] args) throws 
PropagatedException;
     /**
      * Notify all interested members of the channel about a join event
      * @param who The entity that has joined
      */
-    public void distributeJoin(Entity who);
+    public void distributeJoin(Entity who) throws PropagatedException;
     /**
      * Notify all interested members of the channel about a part event
      * @param who The entity that left
      */
-    public void distributePart(Entity who);
+    public void distributePart(Entity who) throws PropagatedException;
     
     /**
      * Find an entity with the given name and namespace in the channel

Added: trunk/clients/Javer2/src/org/haverdev/haver/server/DefaultLobby.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/server/DefaultLobby.java        
2005-05-24 21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/haver/server/DefaultLobby.java        
2005-05-25 02:59:42 UTC (rev 731)
@@ -0,0 +1,40 @@
+/*
+ * Lobby.java
+ *
+ * Created on May 23, 2005, 7:41 PM
+ */
+
+package org.haverdev.haver.server;
+import org.haverdev.haver.server.exceptions.*;
+import java.util.*;
+
+/**
+ *
+ * @author bdonlan
+ */
+public final class DefaultLobby extends ChannelBase {
+    String name;
+    
+    public DefaultLobby(String name) {
+        this.name = name;
+    }
+    
+    public void distributePart(Entity who) {
+    }
+
+    public void distributeJoin(Entity who) {
+    }
+
+    public void distributePublicMessage(Entity from, String[] args) throws 
PropagatedException {
+        throw new Forbidden("&lobby");
+    }
+
+    public User[] quitListeners() {
+        return new User[0];
+    }
+
+    public String getName() {
+        return "&lobby";
+    }
+    
+}


Property changes on: 
trunk/clients/Javer2/src/org/haverdev/haver/server/DefaultLobby.java
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: trunk/clients/Javer2/src/org/haverdev/haver/server/EntityLoader.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/server/EntityLoader.java        
2005-05-24 21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/haver/server/EntityLoader.java        
2005-05-25 02:59:42 UTC (rev 731)
@@ -22,6 +22,7 @@
     
     static final void initEntity(String name) throws Throwable {
         String clas = System.getProperty(name + ".class");
+        String arg  = System.getProperty(name + ".argument", name);
         
         if (clas == null)
             throw new IllegalArgumentException("Missing " + name + ".class 
property");
@@ -29,7 +30,7 @@
         ClassLoader loader = ClassLoader.getSystemClassLoader();
         Class theClass = loader.loadClass(clas);
         Class[] proto = {name.getClass()};
-        Object[] args = {name};
+        Object[] args = {arg};
         Constructor cons = theClass.getConstructor(proto);
         Object ret = cons.newInstance(args);
         Lobby.theLobby.register((Entity)ret);

Added: trunk/clients/Javer2/src/org/haverdev/haver/server/InitContext.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/server/InitContext.java 
2005-05-24 21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/haver/server/InitContext.java 
2005-05-25 02:59:42 UTC (rev 731)
@@ -0,0 +1,35 @@
+/*
+ * InitContext.java
+ *
+ * Created on May 24, 2005, 7:39 PM
+ */
+
+package org.haverdev.haver.server;
+import org.haverdev.haver.server.exceptions.*;
+
+/**
+ *
+ * @author bdonlan
+ */
+public class InitContext extends UserCommandReflect {
+    static final String[] greeting = { "HAVER", Misc.version };
+    
+    public InitContext(UserConnection conn, String name) {
+        super(conn, name);
+    }
+    
+    public void handle_HAVER(String[] args) throws PropagatedException {
+        log.info("Client identifies itself as " + args[1]);
+        conn.sendLine(greeting);
+        //conn.context = new LoginContext(conn, "login");
+        advance();
+    }
+    
+    public void processCommand(String[] cmd) {
+        try {
+            super.processCommand(cmd);
+        } catch (PropagatedException e) {
+            conn.cutConnection();
+        }
+    }
+}
\ No newline at end of file


Property changes on: 
trunk/clients/Javer2/src/org/haverdev/haver/server/InitContext.java
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: trunk/clients/Javer2/src/org/haverdev/haver/server/Lobby.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/server/Lobby.java       
2005-05-24 21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/haver/server/Lobby.java       
2005-05-25 02:59:42 UTC (rev 731)
@@ -1,41 +1,34 @@
 /*
  * Lobby.java
  *
- * Created on May 23, 2005, 7:41 PM
+ * Created on May 24, 2005, 6:31 PM
  */
 
 package org.haverdev.haver.server;
-import java.util.*;
-
+import java.lang.reflect.*;
 /**
  *
  * @author bdonlan
  */
-public final class Lobby extends ChannelBase {
-    public static final Lobby theLobby = new Lobby();
+public final class Lobby {
     
-    /** There can be only one */
+    /** Creates a new instance of Lobby */
     private Lobby() {
-        register(this);
-        register(new ChatChannel("test"));
     }
     
-    public void distributePart(Entity who) {
-    }
-
-    public void distributeJoin(Entity who) {
-    }
-
-    public void distributePublicMessage(Entity from, String[] args) {
-        throw new UnsupportedOperationException("&lobby does not support 
messaging.");
-    }
-
-    public User[] quitListeners() {
-        return new User[0];
-    }
-
-    public String getName() {
-        return "&lobby";
-    }
+    public static Channel theLobby = null;
     
+    public synchronized static void initLobby(String classname) throws 
Throwable {
+        if (theLobby != null)
+            throw new IllegalStateException("Lobby is already set");
+        ClassLoader l = ClassLoader.getSystemClassLoader();
+        Class clas = l.loadClass(classname);
+        Class[] args_p = {"&lobby".getClass()};
+        Object[] args = {"&lobby"};
+        
+        Constructor m = clas.getConstructor(args_p);
+        Channel c = (Channel)m.newInstance(args);
+        c.register(c);
+        theLobby = c;
+    }
 }

Added: trunk/clients/Javer2/src/org/haverdev/haver/server/LoginContext.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/server/LoginContext.java        
2005-05-24 21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/haver/server/LoginContext.java        
2005-05-25 02:59:42 UTC (rev 731)
@@ -0,0 +1,39 @@
+/*
+ * LoginContext.java
+ *
+ * Created on May 24, 2005, 7:40 PM
+ */
+
+package org.haverdev.haver.server;
+import org.haverdev.haver.server.exceptions.*;
+
+/**
+ *
+ * @author bdonlan
+ */
+public class LoginContext extends UserCommandReflect {
+    
+    public LoginContext(UserConnection conn, String name) {
+        super(conn, name);
+    }
+    
+    public void handle_IDENT(String[] args) throws PropagatedException {
+        synchronized (conn.pingLock) {
+            conn.handshakeAbort.cancel();
+            if (conn.writer == null) // Too late
+                return;
+        }
+        if (Lobby.theLobby.contains("user", args[1]))
+            throw new UserAlreadyExists(args[1]);
+        if (!Misc.checkName(args[1]))
+            throw new BadNameException(args[1]);
+        log.info("User logged in: " + args[1]);
+        String[] msg = {"HELLO", args[1]};
+        conn.sendLine(msg);
+        conn.entity = new UserEntity(conn, args[1]);
+        //conn.context = new NormalContext(conn, "normal");
+        advance();
+        conn.readThread.setName("User " + args[1]);
+        conn.schedulePing();
+    }
+}
\ No newline at end of file


Property changes on: 
trunk/clients/Javer2/src/org/haverdev/haver/server/LoginContext.java
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: trunk/clients/Javer2/src/org/haverdev/haver/server/Main.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/server/Main.java        
2005-05-24 21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/haver/server/Main.java        
2005-05-25 02:59:42 UTC (rev 731)
@@ -23,6 +23,10 @@
     public static final Properties buildDefaults() {
         Properties defaults = new Properties(System.getProperties());
         defaults.setProperty("haver.ListenPort", "7070");
+        defaults.setProperty("haver.PingTimeout", "30");
+        defaults.setProperty("haver.PingInterval", "60");
+        defaults.setProperty("haver.HandshakeTimeout", "120");
+        defaults.setProperty("haver.Lobby", 
"org.haverdev.haver.server.DefaultLobby");
         defaults.setProperty("log4j.rootLogger", "DEBUG, __A1");
         defaults.setProperty("log4j.appender.__A1", 
"org.apache.log4j.ConsoleAppender");
         defaults.setProperty("log4j.appender.__A1.layout", 
"org.apache.log4j.PatternLayout");
@@ -60,6 +64,8 @@
         System.setProperties(props);
         PropertyConfigurator.configure(props);
         
+        Lobby.initLobby(props.getProperty("haver.Lobby"));
+        
         String portStr = props.getProperty("haver.ListenPort", "7070");
         int port = Integer.decode(portStr).intValue();
         

Added: trunk/clients/Javer2/src/org/haverdev/haver/server/MergingContext.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/server/MergingContext.java      
2005-05-24 21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/haver/server/MergingContext.java      
2005-05-25 02:59:42 UTC (rev 731)
@@ -0,0 +1,30 @@
+/*
+ * MergingContext.java
+ *
+ * Created on May 24, 2005, 6:48 PM
+ */
+
+package org.haverdev.haver.server;
+import java.util.*;
+import org.haverdev.haver.server.exceptions.*;
+/**
+ *
+ * @author bdonlan
+ */
+public class MergingContext extends java.util.LinkedHashSet implements 
UserCommandHandler {
+    
+    public synchronized void processCommand(String[] cmd) throws 
PropagatedException {
+        Iterator i = iterator();
+        while (i.hasNext()) {
+            UserCommandHandler ctx = (UserCommandHandler)i.next();
+            try {
+                ctx.processCommand(cmd);
+                return;
+            } catch (UnknownClientCommandException e) {
+                continue;
+            }
+        }
+        throw new UnknownClientCommandException(cmd);
+    }
+    
+}


Property changes on: 
trunk/clients/Javer2/src/org/haverdev/haver/server/MergingContext.java
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: trunk/clients/Javer2/src/org/haverdev/haver/server/Misc.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/server/Misc.java        
2005-05-24 21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/haver/server/Misc.java        
2005-05-25 02:59:42 UTC (rev 731)
@@ -36,4 +36,83 @@
             return false;
         return true;
     }
+    
+        
+    /**
+     * Reverses Haver escaping.
+     * @param val String to unescape
+     * @return The unescaped string
+     */
+    protected static final String unescape(String val) {
+        //if (val != null) return val;
+        StringBuffer out = new StringBuffer();
+        int pin = 0;
+        int nextEsc = -1;
+        while (-1 != (nextEsc = val.indexOf("\033", pin))) {
+            out.append(val.substring(0, nextEsc - 1));
+            switch (val.charAt(nextEsc + 1)) {
+                case 'r':
+                    out.append('\r');
+                    break;
+                case 'n':
+                    out.append('\n');
+                    break;
+                case 'e':
+                    out.append('\033');
+                    break;
+                case 't':
+                    out.append('\t');
+                    break;
+                default:
+                    out.append(val.charAt(nextEsc + 1));
+                    break;
+            }
+            pin = nextEsc + 2;
+        }
+        out.append(val.substring(pin));
+        return out.toString();
+    }
+    
+    /**
+     * Escapes a string according to the Haver protocol
+     * @param val String to escape
+     * @return Escaped string
+     */
+    protected static final String escape(String val) {
+        return val.replaceAll("\033", "\033e")
+               .replaceAll("\n", "\033n")
+               .replaceAll("\r", "\033r")
+               .replaceAll("\t", "\033t");
+    }
+    
+    /**
+     * Decodes a raw line from the Haver protocol.
+     * @param inLine Line to decode. May end in '\r\n' but must only be one 
line.
+     * @return An array containing the unescaped arguments of the line.
+     */
+    protected static final String[] decodeLine(String inLine) {
+        inLine = inLine.replaceAll("[\n\r]", ""); // We'll assume you only 
passed one
+                                            // line in.
+        String[] args = inLine.split("\t");
+        for (int i = 0; i < args.length; i++) {
+            args[i] = unescape(args[i]);
+        }
+        return args;
+    }
+    
+    /**
+     * Encodes a set of arguments for line transmission
+     * @param args Unescaped arguments to encode
+     * @return Escaped and joined line ready for transmission, complete with 
ending \r\n
+     */
+    protected static final String encodeLine(String[] args) {
+        StringBuffer out = new StringBuffer();
+        for (int i = 0; i < args.length; i++) {
+            if (i != 0)
+                out.append("\t");
+            out.append(escape(args[i]));
+        }
+        out.append("\r\n");
+        return out.toString();
+    }
 }

Added: trunk/clients/Javer2/src/org/haverdev/haver/server/NormalContext.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/server/NormalContext.java       
2005-05-24 21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/haver/server/NormalContext.java       
2005-05-25 02:59:42 UTC (rev 731)
@@ -0,0 +1,99 @@
+/*
+ * NormalContext.java
+ *
+ * Created on May 24, 2005, 7:44 PM
+ */
+
+package org.haverdev.haver.server;
+import org.haverdev.haver.server.exceptions.*;
+import java.util.*;
+
+/**
+ *
+ * @author bdonlan
+ */
+public class NormalContext extends UserCommandReflect {
+    public NormalContext(UserConnection conn, String name) {
+        super(conn, name);
+    }
+    
+    public void handle_TO(String[] args) throws PropagatedException {
+        String who = args[1];
+        User them = (User)Lobby.theLobby.lookup("user", who);
+        if (them == null)
+            throw new UserNotFound(who);
+        String margs[] = new String[args.length - 2];
+        if (margs.length == 0)
+            throw new MissingMessageType();
+        System.arraycopy(args, 2, margs, 0, margs.length);
+        them.sendPrivateMessage(conn.entity.getName(), margs);
+    }
+    
+    public void handle_LIST(String[] args) throws PropagatedException {
+        String channel = args[1];
+        String filter  = args[2];
+        Channel theChannel = (Channel)Lobby.theLobby.lookup("channel", 
channel);
+        if (theChannel == null)
+            throw new ChannelNotFound(channel);
+        String[] members = theChannel.getNames(filter);
+        String[] response = new String[members.length + 3];
+        System.arraycopy(args, 0, response, 0, 3);
+        System.arraycopy(members, 0, response, 3, members.length);
+        conn.sendLine(response);
+    }
+    
+    public void handle_JOIN(String[] args) throws PropagatedException {
+        String channel = args[1];
+        Channel chan_e = (Channel)Lobby.theLobby.lookup("channel", channel);
+        if (chan_e == null)
+            throw new ChannelNotFound(channel);
+        // Special case!
+        if (chan_e == Lobby.theLobby)
+            throw new Forbidden("&lobby");
+        conn.entity.join(chan_e);
+    }
+    
+    public void handle_PART(String[] args) throws PropagatedException {
+        String channel = args[1];
+        Channel chan_e = (Channel)Lobby.theLobby.lookup("channel", channel);
+        // Special case!
+        if (chan_e == Lobby.theLobby)
+            throw new Forbidden("&lobby");
+        conn.entity.part(chan_e);
+    }
+    
+    public void handle_IN(String[] args) throws PropagatedException {
+        String channel = args[1]; // TODO: check presence
+        Channel chan_e = (Channel)Lobby.theLobby.lookup("channel", channel);
+        if (chan_e == null)
+            throw new ChannelNotFound(channel);
+        String[] margs = new String[args.length - 2];
+        if (margs.length == 0)
+            throw new MissingMessageType();
+        System.arraycopy(args, 2, margs, 0, margs.length);
+        chan_e.distributePublicMessage(conn.entity, margs);
+    }
+    
+    public void handle_POKE(String[] args) {
+        args[0] = "OUCH";
+        conn.sendLine(args);
+    }
+    
+    public void handle_PONG(String[] args) {
+        conn.ackPong(args[1]);
+    }
+    
+    public void handle_BYE(String[] args) {
+        String detail = args.length > 1 ? args[1] : null;
+        conn.cutConnection("active", detail);
+    }
+    
+    // Debug hack
+    public void handle_NOPING(String[] args) {
+        synchronized (conn.pingLock) {
+            if (conn.pingTimer != null) conn.pingTimer.cancel();
+            if (conn.pongTimer != null) conn.pongTimer.cancel();
+            conn.pingTimer = conn.pongTimer = null;
+        }
+    }
+}
\ No newline at end of file


Property changes on: 
trunk/clients/Javer2/src/org/haverdev/haver/server/NormalContext.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: 
trunk/clients/Javer2/src/org/haverdev/haver/server/UserCommandHandler.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/server/UserCommandHandler.java  
2005-05-24 21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/haver/server/UserCommandHandler.java  
2005-05-25 02:59:42 UTC (rev 731)
@@ -0,0 +1,15 @@
+/*
+ * UserCommandContext.java
+ *
+ * Created on May 24, 2005, 6:44 PM
+ */
+
+package org.haverdev.haver.server;
+import org.haverdev.haver.server.exceptions.*;
+/**
+ *
+ * @author bdonlan
+ */
+public interface UserCommandHandler {
+    public void processCommand(String[] cmd) throws PropagatedException;
+}


Property changes on: 
trunk/clients/Javer2/src/org/haverdev/haver/server/UserCommandHandler.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: 
trunk/clients/Javer2/src/org/haverdev/haver/server/UserCommandReflect.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/server/UserCommandReflect.java  
2005-05-24 21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/haver/server/UserCommandReflect.java  
2005-05-25 02:59:42 UTC (rev 731)
@@ -0,0 +1,64 @@
+/*
+ * UserCommandReflect.java
+ *
+ * Created on May 24, 2005, 6:45 PM
+ */
+
+package org.haverdev.haver.server;
+import org.haverdev.haver.server.exceptions.*;
+import java.lang.reflect.*;
+import org.apache.log4j.Logger;
+
+/**
+ *
+ * @author bdonlan
+ */
+public abstract class UserCommandReflect implements UserCommandHandler {
+    Logger log = Logger.getLogger(getClass());
+    
+    protected UserConnection conn;
+    protected String name;
+    
+    public UserCommandReflect(UserConnection conn, String name) {
+        this.conn = conn;
+        this.name = name;
+    }
+    
+    protected void advance() throws PropagatedException {
+        try {
+            synchronized (conn) { // XXX: racy?
+                conn.context = UserContextFactory.constructState(conn, 
System.getProperty(name + ".successor"));
+            }
+        } catch (PropagatedException e) {
+            throw e;
+        } catch (Throwable t) {
+            throw new InternalCommandException("UNKNOWN", t);
+        }
+    }
+    
+    public void processCommand(String[] cmd) throws PropagatedException {
+        if (!cmd[0].toUpperCase().equals(cmd[0])) throw new 
UnknownClientCommandException(cmd);
+        Class myClass = getClass();
+        Class[] argTypes = {cmd.getClass()};
+        Method m;
+        try {
+            m = myClass.getMethod("handle_" + cmd[0], argTypes);
+        } catch(NoSuchMethodException e) {
+            throw new UnknownClientCommandException(cmd);
+        }
+        Object[] args = {cmd};
+        try {
+            m.invoke(this, args);
+        } catch (InvocationTargetException e) {
+            if (e.getTargetException() instanceof PropagatedException) {
+                PropagatedException pe = 
(PropagatedException)e.getTargetException();
+                pe.setCmd(cmd[0]);
+                throw pe;
+            }
+            log.warn(e.getTargetException());
+            throw new InternalCommandException(cmd[0], e);
+        } catch (Throwable t) {
+            throw new InternalCommandException(cmd[0], t);
+        }
+    }
+}


Property changes on: 
trunk/clients/Javer2/src/org/haverdev/haver/server/UserCommandReflect.java
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: trunk/clients/Javer2/src/org/haverdev/haver/server/UserConnection.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/server/UserConnection.java      
2005-05-24 21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/haver/server/UserConnection.java      
2005-05-25 02:59:42 UTC (rev 731)
@@ -24,95 +24,69 @@
     
     static Timer timer = new Timer(true);
     
-    TimerTask pingTimer = null;
-    
     Socket sock;
     NonblockingOutputStream nws;
     PrintWriter writer;
     BufferedReader reader;
     Thread readThread;
-    UserCommandContext context = new InitContext();
-    UserEntity e = null;
+    UserCommandHandler context;
+    UserEntity entity = null;
     
-    String lastPing = "";
-    String lastPong = "";
+    String livePing = null;
+    TimerTask handshakeAbort = null;
+    TimerTask pingTimer = null;
+    TimerTask pongTimer = null;
     
-    /**
-     * Reverses Haver escaping.
-     * @param val String to unescape
-     * @return The unescaped string
-     */
-    protected static final String unescape(String val) {
-        //if (val != null) return val;
-        StringBuffer out = new StringBuffer();
-        int pin = 0;
-        int nextEsc = -1;
-        while (-1 != (nextEsc = val.indexOf("\033", pin))) {
-            out.append(val.substring(0, nextEsc - 1));
-            switch (val.charAt(nextEsc + 1)) {
-                case 'r':
-                    out.append('\r');
-                    break;
-                case 'n':
-                    out.append('\n');
-                    break;
-                case 'e':
-                    out.append('\033');
-                    break;
-                case 't':
-                    out.append('\t');
-                    break;
-                default:
-                    out.append(val.charAt(nextEsc + 1));
-                    break;
+    Integer pingLock = new Integer(0);
+    
+    protected void doPing() {
+        synchronized (pingLock) {
+            if (pingTimer != null) {
+                pingTimer.cancel();
+                pingTimer = null;
             }
-            pin = nextEsc + 2;
+            if (pongTimer != null) {
+                pongTimer.cancel();
+                pongTimer = null;
+            }
+            String[] line = { "PING", livePing = "" + 
System.currentTimeMillis() };
+            sendLine(line);
+            pongTimer = new TimerTask() {
+                public void run() {
+                    cutConnection("ping", null);
+                }
+            };
+            synchronized (timer) {
+                timer.schedule(pongTimer,
+                    Integer.getInteger("haver.PingTimeout").intValue() * 1000);
+            }
         }
-        out.append(val.substring(pin));
-        return out.toString();
     }
     
-    /**
-     * Escapes a string according to the Haver protocol
-     * @param val String to escape
-     * @return Escaped string
-     */
-    protected static final String escape(String val) {
-        return val.replaceAll("\033", "\033e")
-               .replaceAll("\n", "\033n")
-               .replaceAll("\r", "\033r")
-               .replaceAll("\t", "\033t");
-    }
-    
-    /**
-     * Decodes a raw line from the Haver protocol.
-     * @param inLine Line to decode. May end in '\r\n' but must only be one 
line.
-     * @return An array containing the unescaped arguments of the line.
-     */
-    protected static final String[] decodeLine(String inLine) {
-        inLine = inLine.replaceAll("[\n\r]", ""); // We'll assume you only 
passed one
-                                            // line in.
-        String[] args = inLine.split("\t");
-        for (int i = 0; i < args.length; i++) {
-            args[i] = unescape(args[i]);
+    protected void ackPong(String pong) {
+        synchronized (pingLock) {
+            if (!pong.equals(livePing)) return;
+            if (pongTimer != null) pongTimer.cancel();
+            pongTimer = null;
+            schedulePing();
         }
-        return args;
     }
     
-    /**
-     * Encodes a set of arguments for line transmission
-     * @param args Unescaped arguments to encode
-     * @return Escaped and joined line ready for transmission, complete with 
ending \r\n
-     */
-    protected static final String encodeLine(String[] args) {
-        StringBuffer out = new StringBuffer();
-        for (int i = 0; i < args.length; i++) {
-            if (i != 0)
-                out.append("\t");
-            out.append(escape(args[i]));
+    protected void schedulePing() {
+        synchronized (pingLock) {
+            if (pingTimer != null) pingTimer.cancel();
+            if (pongTimer != null) pongTimer.cancel();
+            pongTimer = null;
+            pingTimer = new TimerTask() {
+                public void run() {
+                    doPing();
+                }
+            };
+            synchronized (timer) {
+                timer.schedule(pingTimer,
+                    Integer.getInteger("haver.PingInterval").intValue() * 
1000);
+            }
         }
-        out.append("\r\n");
-        return out.toString();
     }
     
     public final static String join(String joinBy, String[] parts) {
@@ -128,6 +102,13 @@
     
     /** Creates a new instance of UserConnection */
     public UserConnection(Socket s) throws IOException {
+        try {
+            context = UserContextFactory.constructState(this, "init");
+        } catch (Throwable t) {
+            log.error("When initializing client context", t);
+            s.close();
+            return;
+        }
         sock = s;
         nws = new NonblockingOutputStream(new 
BufferedOutputStream(s.getOutputStream()));
         nws.setAutoFlush(true);
@@ -142,22 +123,18 @@
         readThread.setName(Misc.prettySocket(s));
         readThread.start();
         
-        pingTimer = new TimerTask() {
+        handshakeAbort = new TimerTask() {
             public void run() {
-                if (!(context instanceof NormalContext)){
-                    // XXX
-                    ioExcept(new IOException("Took too long to handshake"));
+                synchronized (pingLock) {
+                    if (entity == null)
+                        cutConnection(); // Took too long to handshake
                 }
-                if (lastPing.equals(lastPong)) {
-                    lastPing = "" + System.currentTimeMillis();
-                    String[] ping = {"PING", lastPing};
-                    sendLine(ping);
-                } else {
-                    cutConnection("ping", "Ping timeout: 30 secs");
-                }
             }
         };
-        timer.schedule(pingTimer, 120 * 1000, 30 * 1000);
+        synchronized (timer) {
+            timer.schedule(handshakeAbort, 
+                Integer.getInteger("haver.HandshakeTimeout").intValue() * 
1000);
+        }
     }
     
     public static final String[] greeting = {"HAVER", Misc.version};
@@ -187,14 +164,20 @@
     }
     
     protected synchronized void cutConnection() {
-        try { writer.close(); } catch (Throwable t) { }
-        try {
-            Thread.sleep(1000); // Hopefully flush the writer
-                                // XXX: blocking close/flush
-        } catch (InterruptedException e) {}
-        try { sock.close(); } catch (Throwable t) { }
-        writer = null;
-        pingTimer.cancel();
+        synchronized (writer) {
+            try { writer.close(); } catch (Throwable t) { }
+            try {
+                Thread.sleep(1000); // Hopefully flush the writer
+                                    // XXX: blocking close/flush
+            } catch (InterruptedException e) {}
+            try { sock.close(); } catch (Throwable t) { }
+            writer = null;
+        }
+        synchronized (pingLock) {
+            if (pingTimer != null) pingTimer.cancel();
+            if (pongTimer != null) pongTimer.cancel();
+            if (handshakeAbort != null) handshakeAbort.cancel();
+        }
     }
     
     protected synchronized void cutConnection(String reason, String detail) {
@@ -204,9 +187,9 @@
         if (detail != null)
             sayonara[2] = detail;
         sendLine(sayonara);
-        if (this.e != null)
-            this.e.quit(reason, detail);
-        e = null;
+        if (this.entity != null)
+            this.entity.quit(reason, detail);
+        entity = null;
         cutConnection();
     }
     
@@ -215,255 +198,19 @@
         cutConnection("error", e.getMessage());
     }
     
-    public synchronized void sendLine(String[] args) {
-        if (writer == null) return;
-        String line = encodeLine(args);
-        log.debug("S: " + line);
-        writer.print(line);
-        writer.flush();
+    public void sendLine(String[] args) {
+        String line = Misc.encodeLine(args);
+        synchronized (writer) {
+            if (writer == null) return;
+            log.debug("S: " + line);
+            writer.print(line);
+            writer.flush();
+        }
     }
     
     public synchronized void processLine(String line) throws 
PropagatedException {
-        String[] cmd = decodeLine(line);
+        String[] cmd = Misc.decodeLine(line);
         context.processCommand(cmd);
     }
-    
-    class UserEntity implements User {
-        String name;
-        HashSet channels = new HashSet();
-        
-        UserEntity(String name) throws PropagatedException {
-            this.name = name.intern();
-            join(Lobby.theLobby);
-        }
-        
-        public void notifyPart(String channel, Entity what) {
-            String[] msg = {"PART", channel, what.getName()};
-            sendLine(msg);
-        }
 
-        public void notifyJoin(String channel, Entity what) {
-            String[] msg = {"JOIN", channel, what.getName()};
-            sendLine(msg);
-        }
-
-        public void notifyPublicMessage(String channel, String from, String[] 
args) {
-            String[] msg = new String[args.length + 3];
-            msg[0] = "IN";
-            msg[1] = channel;
-            msg[2] = from;
-            System.arraycopy(args, 0, msg, 3, args.length);
-            sendLine(msg);
-        }
-
-        public final String getNamespace() {
-            return "user";
-        }
-
-        public String getName() {
-            return name;
-        }
-
-        public void sendPrivateMessage(String from, String[] args) {
-            String[] msg = new String[args.length + 2];
-            msg[0] = "FROM";
-            msg[1] = from;
-            System.arraycopy(args, 0, msg, 2, args.length);
-            sendLine(msg);
-        }
-
-        public void notifyQuit(String who, String type, String detail) {
-            if (detail != null) {
-                String[] msg = { "QUIT", who, type, detail };
-                sendLine(msg);
-            } else {
-                String[] msg = { "QUIT", who, type };
-                sendLine(msg);
-            }
-        }
-
-        public boolean equals(Object obj) {
-            if (obj == null)
-                return false;
-            if (!(obj instanceof User))
-                return false;
-            return ((User)obj).getName().equals(name);
-        }
-
-        public String toString() {
-            return "user: " + name;
-        }
-
-        public int hashCode() {
-            return name.hashCode() ^ "user".hashCode();
-        }
-        
-        public void quit(String why, String detail) {
-            Iterator i = channels.iterator();
-            // XXX
-            HashSet interested_parties = new HashSet();
-            while (i.hasNext()) {
-                Channel c = (Channel)i.next();
-                c.unregister(this);
-                // XXX: use set
-                User[] quit_notice = c.quitListeners();
-                for (int j = 0; j < quit_notice.length; j++)
-                    interested_parties.add(quit_notice[j]);
-            }
-            i = interested_parties.iterator();
-            while (i.hasNext()) {
-                User u = (User)i.next();
-                u.notifyQuit(name, why, detail);
-            }
-        }
-        
-        public synchronized void join(Channel channel) throws 
PropagatedException {
-            if (channels.contains(channel)) throw new 
AlreadyThereJoin(channel.getName());
-            channel.register(this);
-            channel.distributeJoin(this);
-            channels.add(channel);
-        }
-        
-        public synchronized void part(Channel channel) throws 
PropagatedException {
-            if (!channels.contains(channel)) throw new 
NotPresentPart(channel.getName());
-            channel.distributePart(this);
-            channel.unregister(this);
-            channels.remove(channel);
-        }
-    }
-    
-    public interface UserCommandContext {
-        public void processCommand(String[] cmd) throws PropagatedException;
-    }
-    
-    public abstract class UserCommandReflect implements UserCommandContext {
-        public void processCommand(String[] cmd) throws PropagatedException {
-            if (!cmd[0].toUpperCase().equals(cmd[0])) throw new 
UnknownClientCommandException(cmd);
-            Class myClass = getClass();
-            Class[] argTypes = {cmd.getClass()};
-            Method m;
-            try {
-                m = myClass.getMethod("handle_" + cmd[0], argTypes);
-            } catch(NoSuchMethodException e) {
-                throw new UnknownClientCommandException(cmd);
-            }
-            Object[] args = {cmd};
-            try {
-                m.invoke(this, args);
-            } catch (InvocationTargetException e) {
-                if (e.getTargetException() instanceof PropagatedException) {
-                    PropagatedException pe = 
(PropagatedException)e.getTargetException();
-                    pe.setCmd(cmd[0]);
-                    throw pe;
-                }
-                log.warn(e.getTargetException());
-                throw new InternalCommandException(cmd[0], e);
-            } catch (Throwable t) {
-                throw new InternalCommandException(cmd[0], t);
-            }
-        }
-    }
-    
-    public class InitContext extends UserCommandReflect {
-        public void handle_HAVER(String[] args) {
-            log.info("Client identifies itself as " + args[1]);
-            sendLine(greeting);
-            context = new LoginContext();
-        }
-        
-        public void processCommand(String[] cmd) {
-            try {
-                super.processCommand(cmd);
-            } catch (PropagatedException e) {
-                cutConnection();
-            }
-        }
-    }
-    
-    public class LoginContext extends UserCommandReflect {
-        public void handle_IDENT(String[] args) throws PropagatedException {
-            if (Lobby.theLobby.contains("user", args[1]))
-                throw new UserAlreadyExists(args[1]);
-            if (!Misc.checkName(args[1]))
-                throw new BadNameException(args[1]);
-            log.info("User logged in: " + args[1]);
-            String[] msg = {"HELLO", args[1]};
-            sendLine(msg);
-            e = new UserEntity(args[1]);
-            context = new NormalContext();
-            readThread.setName("User " + args[1]);
-        }
-    }
-    
-    public class NormalContext extends UserCommandReflect {
-        public void handle_TO(String[] args) throws PropagatedException {
-            String who = args[1];
-            User them = (User)Lobby.theLobby.lookup("user", who);
-            if (them == null)
-                throw new UserNotFound(who);
-            String margs[] = new String[args.length - 2];
-            if (margs.length == 0)
-                throw new MissingMessageType();
-            System.arraycopy(args, 2, margs, 0, margs.length);
-            them.sendPrivateMessage(e.getName(), margs);
-        }
-        
-        public void handle_LIST(String[] args) throws PropagatedException {
-            String channel = args[1];
-            String filter  = args[2];
-            Channel theChannel = (Channel)Lobby.theLobby.lookup("channel", 
channel);
-            if (theChannel == null)
-                throw new ChannelNotFound(channel);
-            String[] members = theChannel.getNames(filter);
-            String[] response = new String[members.length + 3];
-            System.arraycopy(args, 0, response, 0, 3);
-            System.arraycopy(members, 0, response, 3, members.length);
-            sendLine(response);
-        }
-        
-        public void handle_JOIN(String[] args) throws PropagatedException {
-            String channel = args[1];
-            Channel chan_e = (Channel)Lobby.theLobby.lookup("channel", 
channel);
-            // Special case!
-            if (chan_e == Lobby.theLobby)
-                throw new Forbidden("&lobby");
-            e.join(chan_e);
-        }
-        
-        public void handle_PART(String[] args) throws PropagatedException {
-            String channel = args[1];
-            Channel chan_e = (Channel)Lobby.theLobby.lookup("channel", 
channel);
-            // Special case!
-            if (chan_e == Lobby.theLobby)
-                throw new Forbidden("&lobby");
-            e.part(chan_e);
-        }
-        
-        public void handle_IN(String[] args) throws PropagatedException {
-            String channel = args[1]; // TODO: check presence
-            Channel chan_e = (Channel)Lobby.theLobby.lookup("channel", 
channel);
-            if (chan_e == null)
-                throw new ChannelNotFound(channel);
-            String[] margs = new String[args.length - 2];
-            if (margs.length == 0)
-                throw new MissingMessageType();
-            System.arraycopy(args, 2, margs, 0, margs.length);
-            chan_e.distributePublicMessage(e, margs);
-        }
-        
-        public void handle_POKE(String[] args) {
-            args[0] = "OUCH";
-            sendLine(args);
-        }
-        
-        public void handle_PONG(String[] args) {
-            lastPong = args[1];
-        }
-        
-        public void handle_BYE(String[] args) {
-            String detail = args.length > 1 ? args[1] : null;
-            cutConnection("active", detail);
-        }
-        
-    }
 }

Added: 
trunk/clients/Javer2/src/org/haverdev/haver/server/UserContextFactory.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/server/UserContextFactory.java  
2005-05-24 21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/haver/server/UserContextFactory.java  
2005-05-25 02:59:42 UTC (rev 731)
@@ -0,0 +1,38 @@
+/*
+ * UserContextFactory.java
+ *
+ * Created on May 24, 2005, 6:54 PM
+ */
+
+package org.haverdev.haver.server;
+import java.lang.reflect.*;
+
+/**
+ *
+ * @author bdonlan
+ */
+public final class UserContextFactory {
+    
+    public static UserCommandHandler constructState(UserConnection conn, 
String stateName) throws Exception {
+        MergingContext ctx = new MergingContext();
+        String subcontexts = System.getProperty("haver.state." + stateName);
+        if (subcontexts == null)
+            throw new IllegalArgumentException("Subhandler " + stateName + " 
not configured");
+        String[] ctx_arr = subcontexts.split("[, ]+");
+        for (int i = 0; i < ctx_arr.length; i++)
+            ctx.add(constructSubhandler(conn, ctx_arr[i]));
+        return ctx;
+    }
+    
+    public static UserCommandHandler constructSubhandler(UserConnection conn, 
String name) throws Exception {
+        String classname = System.getProperty(name + ".class");
+        ClassLoader c = ClassLoader.getSystemClassLoader();
+        Class clas = c.loadClass(classname);
+        
+        Class[] proto = {conn.getClass(), name.getClass()};
+        Object[] args = {conn, name};
+        Constructor cons = clas.getConstructor(proto);
+        Object ret = cons.newInstance(args);
+        return (UserCommandHandler)ret;
+    }
+}


Property changes on: 
trunk/clients/Javer2/src/org/haverdev/haver/server/UserContextFactory.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/clients/Javer2/src/org/haverdev/haver/server/UserEntity.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/server/UserEntity.java  
2005-05-24 21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/haver/server/UserEntity.java  
2005-05-25 02:59:42 UTC (rev 731)
@@ -0,0 +1,119 @@
+/*
+ * UserEntity.java
+ *
+ * Created on May 24, 2005, 7:42 PM
+ */
+
+package org.haverdev.haver.server;
+import org.haverdev.haver.server.exceptions.*;
+import java.util.*;
+
+/**
+ *
+ * @author bdonlan
+ */
+ class UserEntity implements User {
+     String name;
+     HashSet channels = new HashSet();
+     UserConnection conn;
+     
+     UserEntity(UserConnection conn, String name) throws PropagatedException {
+         this.name = name.intern();
+         this.conn = conn;
+         join(Lobby.theLobby);
+     }
+     
+     public void notifyPart(String channel, Entity what) {
+         String[] msg = {"PART", channel, what.getName()};
+         conn.sendLine(msg);
+     }
+     
+     public void notifyJoin(String channel, Entity what) {
+         String[] msg = {"JOIN", channel, what.getName()};
+         conn.sendLine(msg);
+     }
+     
+     public void notifyPublicMessage(String channel, String from, String[] 
args) {
+         String[] msg = new String[args.length + 3];
+         msg[0] = "IN";
+         msg[1] = channel;
+         msg[2] = from;
+         System.arraycopy(args, 0, msg, 3, args.length);
+         conn.sendLine(msg);
+     }
+     
+     public final String getNamespace() {
+         return "user";
+     }
+     
+     public String getName() {
+         return name;
+     }
+     
+     public void sendPrivateMessage(String from, String[] args) {
+         String[] msg = new String[args.length + 2];
+         msg[0] = "FROM";
+         msg[1] = from;
+         System.arraycopy(args, 0, msg, 2, args.length);
+         conn.sendLine(msg);
+     }
+     
+     public void notifyQuit(String who, String type, String detail) {
+         if (detail != null) {
+             String[] msg = { "QUIT", who, type, detail };
+             conn.sendLine(msg);
+         } else {
+             String[] msg = { "QUIT", who, type };
+             conn.sendLine(msg);
+         }
+     }
+     
+     public boolean equals(Object obj) {
+         if (obj == null)
+             return false;
+         if (!(obj instanceof User))
+             return false;
+         return ((User)obj).getName().equals(name);
+     }
+     
+     public String toString() {
+         return "user: " + name;
+     }
+     
+     public int hashCode() {
+         return name.hashCode() ^ "user".hashCode();
+     }
+     
+     public void quit(String why, String detail) {
+         Iterator i = channels.iterator();
+         // XXX
+         HashSet interested_parties = new HashSet();
+         while (i.hasNext()) {
+             Channel c = (Channel)i.next();
+             c.unregister(this);
+             // XXX: use set
+             User[] quit_notice = c.quitListeners();
+             for (int j = 0; j < quit_notice.length; j++)
+                 interested_parties.add(quit_notice[j]);
+         }
+         i = interested_parties.iterator();
+         while (i.hasNext()) {
+             User u = (User)i.next();
+             u.notifyQuit(name, why, detail);
+         }
+     }
+     
+     public synchronized void join(Channel channel) throws PropagatedException 
{
+         if (channels.contains(channel)) throw new 
AlreadyThereJoin(channel.getName());
+         channel.register(this);
+         channel.distributeJoin(this);
+         channels.add(channel);
+     }
+     
+     public synchronized void part(Channel channel) throws PropagatedException 
{
+         if (!channels.contains(channel)) throw new 
NotPresentPart(channel.getName());
+         channel.distributePart(this);
+         channel.unregister(this);
+         channels.remove(channel);
+     }
+ }
\ No newline at end of file


Property changes on: 
trunk/clients/Javer2/src/org/haverdev/haver/server/UserEntity.java
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: trunk/clients/Javer2/src/org/haverdev/javer2/CMod.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/javer2/CMod.java      2005-05-24 
21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/javer2/CMod.java      2005-05-25 
02:59:42 UTC (rev 731)
@@ -46,7 +46,8 @@
     }
     
     public static void main(String[] args) throws Throwable {
-        new CMod("localhost", 15678, "cmod", "test");
+        for (int i = 0; i < 300; i++)
+            new CMod("localhost", 7070, "cmod-" + i, "lobby");
     }
     
     static final int STATE_IDLE = 0;

Added: trunk/clients/Javer2/src/org/haverdev/javer2/ChannelPane.form
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/javer2/ChannelPane.form       
2005-05-24 21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/javer2/ChannelPane.form       
2005-05-25 02:59:42 UTC (rev 731)
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.0" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+
+  <Layout 
class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
+  <SubComponents>
+    <Container class="javax.swing.JSplitPane" name="jSplitPane1">
+      <Constraints>
+        <Constraint 
layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" 
value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
+          <BorderConstraints direction="Center"/>
+        </Constraint>
+      </Constraints>
+
+      <Layout 
class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
+      <SubComponents>
+        <Container class="javax.swing.JScrollPane" name="jScrollPane1">
+          <Constraints>
+            <Constraint 
layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"
 
value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
+              <JSplitPaneConstraints position="left"/>
+            </Constraint>
+          </Constraints>
+
+          <Layout 
class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+          <SubComponents>
+            <Component class="javax.swing.JTextArea" name="jTextArea1">
+              <Properties>
+                <Property name="editable" type="boolean" value="false"/>
+                <Property name="lineWrap" type="boolean" value="true"/>
+                <Property name="wrapStyleWord" type="boolean" value="true"/>
+              </Properties>
+            </Component>
+          </SubComponents>
+        </Container>
+        <Container class="javax.swing.JScrollPane" name="jScrollPane2">
+          <Constraints>
+            <Constraint 
layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"
 
value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
+              <JSplitPaneConstraints position="right"/>
+            </Constraint>
+          </Constraints>
+
+          <Layout 
class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+          <SubComponents>
+            <Component class="javax.swing.JList" name="userList">
+            </Component>
+          </SubComponents>
+        </Container>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>

Added: trunk/clients/Javer2/src/org/haverdev/javer2/ChannelPane.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/javer2/ChannelPane.java       
2005-05-24 21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/javer2/ChannelPane.java       
2005-05-25 02:59:42 UTC (rev 731)
@@ -0,0 +1,157 @@
+/*
+ * ChannelPane.java
+ *
+ * Created on May 24, 2005, 9:31 PM
+ */
+
+package org.haverdev.javer2;
+import org.haverdev.haver.*;
+import java.util.*;
+import javax.swing.*;
+
+/**
+ *
+ * @author  bdonlan
+ */
+public class ChannelPane extends javax.swing.JPanel implements Pane {
+    
+    Client client;
+    String channel;
+    Listener l = new Listener();
+    TreeSet users = new TreeSet();
+    
+    class Listener extends Callback {
+        // TODO: part self
+        
+        public void onQuit(Client source, String who, String type, String 
detail) throws Throwable {
+            synchronized (users) {
+                if (users.contains(who)) {
+                    users.remove(who);
+                    putLine("--- " + who + " has left the channel: " + type + 
" " + detail);
+                    refreshList();
+                }
+            }
+        }
+
+        public void onPart(Client source, String chan, String who) throws 
Throwable {
+            if (!channel.equals(chan))
+                return;
+            synchronized (users) {
+                users.remove(who);
+            }
+            putLine("--- " + who + " has left the channel.");
+            refreshList();
+        }
+
+        public void onJoin(Client source, String chan, String who) throws 
Throwable {
+            if (!channel.equals(chan))
+                return;
+            synchronized (users) {
+                users.add(who);
+            }
+            putLine("--- " + who + " has joined the channel.");
+            refreshList();
+        }
+
+        public void onReceivedList(Client source, String chan, String 
namespace, String[] list) throws Throwable {
+            if (!channel.equals(chan))
+                return;
+            synchronized (users) {
+                TreeSet nu = new TreeSet();
+                for (int i = 0; i < list.length; i++)
+                    nu.add(list[i]);
+                users = nu;
+            }
+            refreshList();
+        }
+
+        public void onPublicMessage(Client source, String chan, String from, 
String type, String[] args) throws Throwable {
+            if (!channel.equals(chan))
+                return;
+            if (type.equals("say"))
+                putLine("<" + from + "> " + args[0]);
+            else if (type.equals("act"))
+                putLine("* " + from + " " + args[0]);
+        }
+    }
+    
+    /** Creates new form ChannelPane */
+    public ChannelPane(Client cli, String channel) {
+        initComponents();
+        client = cli;
+        this.channel = channel;
+        client.addNotify(l);
+        client.sendJoin(channel);
+        client.requestList(channel, "user");
+    }
+
+    public void speak(String line) {
+        client.sendPublicMessage(channel, "say", line);
+    }
+
+    public void putLine(final String line) {
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                String txt = jTextArea1.getText();
+                jTextArea1.setText(txt + "\n" + line);
+            }
+        });
+    }
+
+    public void act(String line) {
+        client.sendPublicMessage(channel, "act", line);
+    }
+
+    public String getCaption() {
+        return "Channel " + channel;
+    }
+    
+    public void refreshList() {
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                synchronized (users) {
+                    userList.setListData(users.toArray());
+                }
+            }
+        });
+    }
+    
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    private void initComponents() {//GEN-BEGIN:initComponents
+        jSplitPane1 = new javax.swing.JSplitPane();
+        jScrollPane1 = new javax.swing.JScrollPane();
+        jTextArea1 = new javax.swing.JTextArea();
+        jScrollPane2 = new javax.swing.JScrollPane();
+        userList = new javax.swing.JList();
+
+        setLayout(new java.awt.BorderLayout());
+
+        jTextArea1.setEditable(false);
+        jTextArea1.setLineWrap(true);
+        jTextArea1.setWrapStyleWord(true);
+        jScrollPane1.setViewportView(jTextArea1);
+
+        jSplitPane1.setLeftComponent(jScrollPane1);
+
+        jScrollPane2.setViewportView(userList);
+
+        jSplitPane1.setRightComponent(jScrollPane2);
+
+        add(jSplitPane1, java.awt.BorderLayout.CENTER);
+
+    }//GEN-END:initComponents
+    
+    
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JScrollPane jScrollPane1;
+    private javax.swing.JScrollPane jScrollPane2;
+    private javax.swing.JSplitPane jSplitPane1;
+    private javax.swing.JTextArea jTextArea1;
+    private javax.swing.JList userList;
+    // End of variables declaration//GEN-END:variables
+    
+}


Property changes on: 
trunk/clients/Javer2/src/org/haverdev/javer2/ChannelPane.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/clients/Javer2/src/org/haverdev/javer2/JaverForm.form
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/javer2/JaverForm.form 2005-05-24 
21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/javer2/JaverForm.form 2005-05-25 
02:59:42 UTC (rev 731)
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.0" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+
+  <Layout 
class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
+  <SubComponents>
+    <Container class="javax.swing.JTabbedPane" name="jTabbedPane1">
+      <Constraints>
+        <Constraint 
layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" 
value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
+          <BorderConstraints direction="Center"/>
+        </Constraint>
+      </Constraints>
+
+      <Layout 
class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
+    </Container>
+    <Container class="javax.swing.JPanel" name="jPanel1">
+      <Constraints>
+        <Constraint 
layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" 
value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
+          <BorderConstraints direction="South"/>
+        </Constraint>
+      </Constraints>
+
+      <Layout 
class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JTextField" name="inputBox">
+          <Events>
+            <EventHandler event="keyTyped" 
listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" 
handler="inputBoxKeyTyped"/>
+          </Events>
+          <Constraints>
+            <Constraint 
layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" 
value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
+              <BorderConstraints direction="Center"/>
+            </Constraint>
+          </Constraints>
+        </Component>
+        <Component class="javax.swing.JButton" name="jButton1">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="Send"/>
+            <Property name="actionCommand" type="java.lang.String" 
value="Send"/>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" 
listener="java.awt.event.ActionListener" 
parameters="java.awt.event.ActionEvent" handler="sendLine"/>
+          </Events>
+          <Constraints>
+            <Constraint 
layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" 
value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
+              <BorderConstraints direction="East"/>
+            </Constraint>
+          </Constraints>
+        </Component>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>

Added: trunk/clients/Javer2/src/org/haverdev/javer2/JaverForm.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/javer2/JaverForm.java 2005-05-24 
21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/javer2/JaverForm.java 2005-05-25 
02:59:42 UTC (rev 731)
@@ -0,0 +1,258 @@
+/*
+ * JaverForm.java
+ *
+ * Created on May 24, 2005, 8:09 PM
+ */
+
+package org.haverdev.javer2;
+import javax.swing.*;
+import java.util.*;
+import org.haverdev.haver.*;
+import java.lang.reflect.*;
+import java.io.*;
+
+/**
+ *
+ * @author  bdonlan
+ */
+public class JaverForm extends javax.swing.JPanel {
+    
+    Thread connectThread = null;
+    Client cli = null;
+    Watcher watch = null;
+    
+    class Watcher extends Callback {
+        public void onPrivateMessage(Client source, final String from, final 
String type, final String[] args) throws Throwable {
+            SwingUtilities.invokeLater(new Runnable() {
+                public void run() {
+                    QueryPane q = findPrivateQuery(from);
+                    q.onIncomingMessage(type, args[0]);
+                }
+            });
+        }
+        
+    }
+    
+    void doConnect(String nick) {
+        cli = new Client();
+        watch = new Watcher();
+        cli.addNotify(watch);
+        try {
+            cli.syncConnect("localhost", 7070, nick);
+            serverpane.putLine("Connected!");
+        } catch (final Throwable t) {
+            SwingUtilities.invokeLater(new Runnable() {
+                public void run() {
+                    connectThread = null;
+                    cli = null;
+                    watch = null;
+                    serverpane.putLine("Connection failed: " + t.toString());
+                }
+            });
+        }
+    }
+    
+    public final static void main(String[] args) {
+        javax.swing.SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                JFrame f = new JFrame("test");
+                JaverForm form = new JaverForm();
+                f.getContentPane().add(form);
+                f.pack();
+                f.setVisible(true);
+                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+            }
+        });
+    }
+    
+    ServerPane serverpane;
+    
+    /** Creates new form JaverForm */
+    public JaverForm() {
+        initComponents();
+        serverpane = new ServerPane();
+        addPane(serverpane);
+        serverpane.putLine("Welcome to haver!");
+    }
+    
+    HashMap panes = new HashMap();
+    
+    void addPane(Object c) {
+        int paneCount = panes.size();
+        jTabbedPane1.addTab(((Pane)c).getCaption(), (JComponent)c);
+        panes.put(c, new Integer(paneCount));
+    }
+    
+    void removePane(JComponent c) {
+        Integer paneid_o = (Integer)panes.get(c);
+        if (paneid_o == null)
+            return;
+        int paneid = paneid_o.intValue();
+        panes.remove(c);
+        jTabbedPane1.remove(c);
+        Iterator i = panes.keySet().iterator();
+        while (i.hasNext()) {
+            Object o = i.next();
+            Integer v = (Integer)panes.get(o);
+            if (v.intValue() > paneid)
+                panes.put(o, new Integer(v.intValue() - 1));
+        }
+    }
+    
+    Pane currentPane() {
+        return (Pane)jTabbedPane1.getSelectedComponent();
+    }
+    
+    HashMap channelQueries = new HashMap();
+    
+    Pane findChannelPane(String channel) {
+        channel = channel.toLowerCase();
+        if (channelQueries.containsKey(channel))
+            return (Pane)channelQueries.get(channel);
+        Pane chan = new ChannelPane(cli, channel);
+        addPane(chan);
+        channelQueries.put(channel, chan);
+        return chan;
+    }
+    
+    HashMap privateQueries = new HashMap();
+    
+    QueryPane findPrivateQuery(String name) {
+        name = name.toLowerCase();
+        if (privateQueries.containsKey(name))
+            return (QueryPane)privateQueries.get(name);
+        QueryPane p = new QueryPane(cli, name);
+        addPane(p);
+        privateQueries.put(name, p);
+        return p;
+    }
+    
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    private void initComponents() {//GEN-BEGIN:initComponents
+        jTabbedPane1 = new javax.swing.JTabbedPane();
+        jPanel1 = new javax.swing.JPanel();
+        inputBox = new javax.swing.JTextField();
+        jButton1 = new javax.swing.JButton();
+
+        setLayout(new java.awt.BorderLayout());
+
+        add(jTabbedPane1, java.awt.BorderLayout.CENTER);
+
+        jPanel1.setLayout(new java.awt.BorderLayout());
+
+        inputBox.addKeyListener(new java.awt.event.KeyAdapter() {
+            public void keyTyped(java.awt.event.KeyEvent evt) {
+                inputBoxKeyTyped(evt);
+            }
+        });
+
+        jPanel1.add(inputBox, java.awt.BorderLayout.CENTER);
+
+        jButton1.setText("Send");
+        jButton1.setActionCommand("Send");
+        jButton1.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                sendLine(evt);
+            }
+        });
+
+        jPanel1.add(jButton1, java.awt.BorderLayout.EAST);
+
+        add(jPanel1, java.awt.BorderLayout.SOUTH);
+
+    }//GEN-END:initComponents
+
+    private void inputBoxKeyTyped(java.awt.event.KeyEvent evt) 
{//GEN-FIRST:event_inputBoxKeyTyped
+        if (evt.getKeyChar() == '\n')
+            sendLine(null);
+    }//GEN-LAST:event_inputBoxKeyTyped
+
+    private void sendLine(java.awt.event.ActionEvent evt) 
{//GEN-FIRST:event_sendLine
+        if (connectThread != null) {
+            serverpane.putLine("Still connecting, please wait...");
+            return;
+        }
+        String text = inputBox.getText();
+        inputBox.setText("");
+        if (cli == null) {
+            final String nick = text;
+            serverpane.putLine("Trying to connect with nick " + nick);
+            new Thread(new Runnable() {
+                public void run() {
+                    doConnect(nick);
+                }
+            }).start();
+            return;
+        }
+        
+        if (text.charAt(0) == '/') {
+            if (text.charAt(1) == ' ') {
+                text = text.substring(2);
+            } else {
+                String cmd, arg;
+                int split = text.indexOf(' ');
+                if (split == -1) {
+                    cmd = text.substring(1);
+                    arg = "";
+                } else {
+                    cmd = text.substring(1, split);
+                    arg = text.substring(split + 1);
+                }
+                // XXX: is this ok in applets?
+                
+                Class[] proto = {text.getClass()};
+                Object[] args = {arg};
+                try {
+                    Class cls = getClass();
+                    Method m = cls.getDeclaredMethod("slash_" + 
cmd.toUpperCase(), proto);
+                    m.invoke(this, args);
+                } catch (Throwable t) {
+                    ((Pane)currentPane()).putLine("Error: " + t.toString());
+                }
+                return;
+            }
+        }
+        Pane current = (Pane)currentPane();
+        current.speak(text);
+    }//GEN-LAST:event_sendLine
+    
+    void putLine(String line) {
+        ((Pane)currentPane()).putLine(line);
+    }
+    
+    void slash_XYZZY(String arg) {
+        ((Pane)currentPane()).putLine("Nothing happens.");
+    }
+    
+    void slash_MSG(String arg) throws IOException {
+        int split = arg.indexOf(' ');
+        if (split == -1) {
+            putLine("Usage: /msg <user> <message>");
+            return;
+        }
+        String to = arg.substring(0, split);
+        String msg = arg.substring(split + 1);
+        
+        findPrivateQuery(to).speak(msg);
+    }
+    
+    void slash_JOIN(String arg) throws IOException {
+        findChannelPane(arg);
+    }
+    
+    void slash_ME(String arg) {
+        currentPane().act(arg);
+    }
+    
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JTextField inputBox;
+    private javax.swing.JButton jButton1;
+    private javax.swing.JPanel jPanel1;
+    private javax.swing.JTabbedPane jTabbedPane1;
+    // End of variables declaration//GEN-END:variables
+    
+}


Property changes on: trunk/clients/Javer2/src/org/haverdev/javer2/JaverForm.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/clients/Javer2/src/org/haverdev/javer2/Pane.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/javer2/Pane.java      2005-05-24 
21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/javer2/Pane.java      2005-05-25 
02:59:42 UTC (rev 731)
@@ -0,0 +1,19 @@
+/*
+ * Pane.java
+ *
+ * Created on May 24, 2005, 8:19 PM
+ */
+
+package org.haverdev.javer2;
+
+/**
+ *
+ * @author bdonlan
+ */
+public interface Pane {
+    public String getCaption();
+    public void putLine(String line);
+    public void speak(String line);
+
+    public void act(String line);
+}


Property changes on: trunk/clients/Javer2/src/org/haverdev/javer2/Pane.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/clients/Javer2/src/org/haverdev/javer2/QueryPane.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/javer2/QueryPane.java 2005-05-24 
21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/javer2/QueryPane.java 2005-05-25 
02:59:42 UTC (rev 731)
@@ -0,0 +1,58 @@
+/*
+ * QueryPane.java
+ *
+ * Created on May 24, 2005, 10:11 PM
+ */
+
+package org.haverdev.javer2;
+import org.haverdev.haver.*;
+
+/**
+ *
+ * @author bdonlan
+ */
+public class QueryPane extends TextPane {
+    
+    Client client;
+    String name;
+    Listener listener;
+    
+    class Listener extends Callback {
+        public void onQuit(String who, String reason, String detail) {
+            if (who.equals(name))
+                putLine("--- " + who + " quit: " + reason + " " + detail);
+        }
+    }
+    
+    /** Creates a new instance of QueryPane */
+    public QueryPane(Client client, String name) {
+        this.client = client;
+        this.name = name;
+        // XXX: this reference never dies. fixit
+        client.addNotify(listener = new Listener());
+    }
+
+    public void onIncomingMessage(String type, String arg) {
+        // This is not in a client listener because we need to hook the first
+        // message in the root frame anyway
+        if (type.equals("say"))
+            putLine("<" + name + "> " + arg);
+        else if (type.equals("act"))
+            putLine("* " + name + " " + arg);
+    }
+    
+    public void speak(String line) {
+        putLine("<" + client.getName() + "> " + line);
+        client.sendPrivateMessage(name, "say", line);
+    }
+
+    public void act(String line) {
+        putLine("* " + client.getName() + " " + line);
+        client.sendPrivateMessage(name, "act", line);
+    }
+
+    public String getCaption() {
+        return "Private query: " + name;
+    }
+    
+}


Property changes on: trunk/clients/Javer2/src/org/haverdev/javer2/QueryPane.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/clients/Javer2/src/org/haverdev/javer2/ServerPane.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/javer2/ServerPane.java        
2005-05-24 21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/javer2/ServerPane.java        
2005-05-25 02:59:42 UTC (rev 731)
@@ -0,0 +1,30 @@
+/*
+ * ServerPane.java
+ *
+ * Created on May 24, 2005, 8:21 PM
+ */
+
+package org.haverdev.javer2;
+
+/**
+ *
+ * @author bdonlan
+ */
+public class ServerPane extends TextPane {
+    
+    /** Creates a new instance of ServerPane */
+    public ServerPane() {
+    }
+
+    public void speak(String line) {
+        putLine("Error: Can't type to this window");
+    }
+    
+    public void act(String line) {
+        putLine("Error: Can't act in this window");
+    }
+    
+    public String getCaption() {
+        return "Server";
+    }
+}


Property changes on: 
trunk/clients/Javer2/src/org/haverdev/javer2/ServerPane.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/clients/Javer2/src/org/haverdev/javer2/TextPane.form
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/javer2/TextPane.form  2005-05-24 
21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/javer2/TextPane.form  2005-05-25 
02:59:42 UTC (rev 731)
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.0" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+
+  <Layout 
class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
+  <SubComponents>
+    <Container class="javax.swing.JScrollPane" name="jScrollPane1">
+      <Properties>
+        <Property name="autoscrolls" type="boolean" value="true"/>
+      </Properties>
+      <Constraints>
+        <Constraint 
layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" 
value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
+          <BorderConstraints direction="Center"/>
+        </Constraint>
+      </Constraints>
+
+      <Layout 
class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JTextArea" name="textPane">
+          <Properties>
+            <Property name="editable" type="boolean" value="false"/>
+            <Property name="lineWrap" type="boolean" value="true"/>
+            <Property name="wrapStyleWord" type="boolean" value="true"/>
+          </Properties>
+          <AuxValues>
+            <AuxValue name="JavaCodeGenerator_VariableModifier" 
type="java.lang.Integer" value="4"/>
+          </AuxValues>
+        </Component>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>

Added: trunk/clients/Javer2/src/org/haverdev/javer2/TextPane.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/javer2/TextPane.java  2005-05-24 
21:42:18 UTC (rev 730)
+++ trunk/clients/Javer2/src/org/haverdev/javer2/TextPane.java  2005-05-25 
02:59:42 UTC (rev 731)
@@ -0,0 +1,58 @@
+/*
+ * TextPane.java
+ *
+ * Created on May 24, 2005, 8:14 PM
+ */
+
+package org.haverdev.javer2;
+
+/**
+ *
+ * @author  bdonlan
+ */
+public abstract class TextPane extends javax.swing.JPanel implements Pane {
+    
+    /** Creates new form TextPane */
+    public TextPane() {
+        initComponents();
+    }
+    
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    private void initComponents() {//GEN-BEGIN:initComponents
+        jScrollPane1 = new javax.swing.JScrollPane();
+        textPane = new javax.swing.JTextArea();
+
+        setLayout(new java.awt.BorderLayout());
+
+        jScrollPane1.setAutoscrolls(true);
+        textPane.setEditable(false);
+        textPane.setLineWrap(true);
+        textPane.setWrapStyleWord(true);
+        jScrollPane1.setViewportView(textPane);
+
+        add(jScrollPane1, java.awt.BorderLayout.CENTER);
+
+    }//GEN-END:initComponents
+    
+    
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JScrollPane jScrollPane1;
+    protected javax.swing.JTextArea textPane;
+    // End of variables declaration//GEN-END:variables
+    
+    public synchronized void putLine(final String line) {
+        javax.swing.SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                String text = textPane.getText();
+                String newtext = text + "\n" + line;
+                textPane.setText(newtext);
+            }
+        });
+    }
+    
+    
+}


Property changes on: trunk/clients/Javer2/src/org/haverdev/javer2/TextPane.java
___________________________________________________________________
Name: svn:eol-style
   + native


Reply via email to