[skycastle-commits] SF.net SVN: skycastle: [437] trunk/skycastle/modules/client

  • From: zzorn@xxxxxxxxxxxxxxxxxxxxx
  • To: skycastle-commits@xxxxxxxxxxxxx
  • Date: Thu, 03 Apr 2008 15:14:54 -0700

Revision: 437
          http://skycastle.svn.sourceforge.net/skycastle/?rev=437&view=rev
Author:   zzorn
Date:     2008-04-03 15:14:54 -0700 (Thu, 03 Apr 2008)

Log Message:
-----------
Moved previous 3D chat client to a 'hardcoded' pacakge, and started 
implementing the message based Skycastle client.  Implemented the basic message 
handling, and wrote down open questions.

Modified Paths:
--------------
    trunk/skycastle/modules/client/pom.xml

Added Paths:
-----------
    
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/SkycastleClient.java
    trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/
    
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/ChatClient.java
    
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/ChatInputHandler.java
    
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/ChatOutputHandler.java
    
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/ChatUserHandler.java
    
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/Simple3DClient.java

Modified: trunk/skycastle/modules/client/pom.xml
===================================================================
--- trunk/skycastle/modules/client/pom.xml      2008-04-03 21:31:48 UTC (rev 
436)
+++ trunk/skycastle/modules/client/pom.xml      2008-04-03 22:14:54 UTC (rev 
437)
@@ -33,7 +33,7 @@
 
                     <archive>
                         <manifest>
-                            
<mainClass>org.skycastle.client.Simple3DClient</mainClass>
+                            
<mainClass>org.skycastle.client.hardcoded.Simple3DClient</mainClass>
                         </manifest>
                     </archive>
                 </configuration>
@@ -70,7 +70,7 @@
             -->
             <!--
         <manifest>
-            <mainClass>org.skycastle.client.Simple3DClient</mainClass>
+            
<mainClass>org.skycastle.client.hardcoded.Simple3DClient</mainClass>
             -->
             <!--
                                         
<packageName>org.skycastle.client</packageName>

Added: 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/SkycastleClient.java
===================================================================
--- 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/SkycastleClient.java
                              (rev 0)
+++ 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/SkycastleClient.java
      2008-04-03 22:14:54 UTC (rev 437)
@@ -0,0 +1,242 @@
+package org.skycastle.client;
+
+import com.sun.sgs.client.ClientChannel;
+import com.sun.sgs.client.ClientChannelListener;
+import com.sun.sgs.client.simple.SimpleClient;
+import com.sun.sgs.client.simple.SimpleClientListener;
+import org.skycastle.messaging.Message;
+import org.skycastle.messaging.updates.UpdateMessage;
+import org.skycastle.protocol.ProtocolException;
+import org.skycastle.protocol.negotiation.ClientSideProtocolNegotiator;
+import org.skycastle.protocol.negotiation.ProtocolNegotiator;
+import org.skycastle.protocol.registry.ProtocolRegistryImpl;
+
+import java.io.IOException;
+import java.net.PasswordAuthentication;
+import java.util.Properties;
+import java.util.logging.Logger;
+
+/**
+ * The main entry point for the Skycastle client.
+ *
+ * @author Hans Häggström
+ */
+// IDEA: The client could be connected to multiple servers at once, or logged 
into multiple accounts at once.
+// The GameObjects provided by different servers / accounts would need to be 
in different namespaces
+// Possibly also connected to multiple local servers?  E.g. editing some 
designs on a local server,
+// browsing some object store server, and keeping an eye on their characters 
statuses on their favourite servers.
+// We need a SimpleClient for each connected remote server.
+
+// TODO: The client could probably get the servers from a metaserver, or from 
user specified string,
+// or it could run a server locally (not necessarily backed by SGS? Just using 
normal client side GameObjects?)
+
+// TODO: We should also initialize the client side UI.
+// Each server+account can provide their own view, but the client itself needs 
to provide an
+// overall view (maybe some menu / menu bar + keyboard binding to access it if 
not visible)
+// When connecting to a server, the view is represented as GameObjects that 
get moved to the client
+// They might need to keep some state and maybe calculation on the client 
side?  Or pipe everything through the server?
+// The client UI:s need to be stored locally though.
+// Advantage of keeping UI:s on the server is that they are the same 
regardless of from which machine you connect,
+// but drawback is that they need to be configured to suit you on each server 
and account.
+// What kind of exchange format could be used?  Maybe upload UI designs from 
the client to the server?
+// --> We need the possibility of a ProxyGameObject to be of some specified 
subtype, that is specified by the server.
+// Or maybe they could be POJO game objects..
+// They should implement some interface that has getJComponent and similar 
methods
+// So client could do something like 
getAccountObject().getUiRoot().createJComponent()    
+public class SkycastleClient
+        implements SimpleClientListener
+{
+
+    //======================================================================
+    // Private Fields
+
+    private final SimpleClient myDarkstarClient;
+    private final ProtocolNegotiator myProtocolNegotiator;
+
+    //======================================================================
+    // Private Constants
+
+    /**
+     * The name of the host property.
+     */
+    private static final String HOST_PROPERTY = "skycastle.host";
+
+    /**
+     * The default hostname.
+     */
+    private static final String DEFAULT_HOST = "localhost";
+
+    /**
+     * The name of the port property.
+     */
+    private static final String PORT_PROPERTY = "skycastle.port";
+
+    /**
+     * The default port.
+     */
+    private static final String DEFAULT_PORT = "1139";
+
+    private static final String TEST_USER_NAME = "TestUser";
+
+    /**
+     * The {@link java.util.logging.Logger} for this class.
+     */
+    private static final Logger LOGGER =
+            Logger.getLogger( SkycastleClient.class.getName() );
+    private static final String CLIENT_TYPE = "SkycastleClient";
+    private static final String CLIENT_VERSION = "0.1";
+    private static final ProtocolRegistryImpl PROTOCOL_REGISTRY = new 
ProtocolRegistryImpl();
+
+    //======================================================================
+    // Public Methods
+
+    //----------------------------------------------------------------------
+    // Constructors
+
+    /**
+     * Creates a new {@link org.skycastle.client.SkycastleClient}.
+     */
+    public SkycastleClient()
+    {
+        myDarkstarClient = new SimpleClient( this );
+
+        // DEBUG: For now, just try directly to log in.
+        login();
+
+        myProtocolNegotiator = new ClientSideProtocolNegotiator( 
PROTOCOL_REGISTRY,
+                                                                 CLIENT_TYPE,
+                                                                 
CLIENT_VERSION );
+    }
+
+    //----------------------------------------------------------------------
+    // ServerSessionListener Implementation
+
+    public ClientChannelListener joinedChannel( final ClientChannel 
clientChannel )
+    {
+        LOGGER.fine( "Joined channel '" + clientChannel + "'." );
+
+        return null;
+    }
+
+
+    public void receivedMessage( final byte[] serverMessage )
+    {
+        if ( myProtocolNegotiator.getStatus().isFinished() )
+        {
+            try
+            {
+                final Message message = 
myProtocolNegotiator.getProtocol().decode( serverMessage );
+
+                // TODO: Validate message to ensure a rogue server is not 
sending us trojans
+                // message.validate(  )
+
+                // TODO: Check that the message is an update message, as the 
client should only get those.
+                final UpdateMessage updateMessage = (UpdateMessage) message;
+
+                // Apply the update message to the client side model
+                // TODO: Use cient side GameContext directly instead, and 
specify a namespace prefix for this server and account
+                updateMessage.applyStateChangeToModel();
+            }
+            catch ( ProtocolException e )
+            {
+                // TODO: Notify user, reset server connection?
+                e.printStackTrace();  //To change body of catch statement use 
File | Settings | File Templates.
+            }
+        }
+        else
+        {
+            final byte[] reply = myProtocolNegotiator.handleMessage( 
serverMessage );
+            try
+            {
+                myDarkstarClient.send( reply );
+            }
+            catch ( IOException e )
+            {
+                // TODO: Notify user, reset server connection?
+                e.printStackTrace();  //To change body of catch statement use 
File | Settings | File Templates.
+            }
+        }
+    }
+
+
+    public void reconnecting()
+    {
+        LOGGER.fine( "Reconnecting" );
+    }
+
+
+    public void reconnected()
+    {
+        LOGGER.fine( "Reconnected" );
+    }
+
+
+    public void disconnected( final boolean b, final String s )
+    {
+        LOGGER.fine( "Disconnected.  Graceful: '" + b + "'.  Message: '" + s + 
"'." );
+    }
+
+    //----------------------------------------------------------------------
+    // SimpleClientListener Implementation
+
+    public PasswordAuthentication getPasswordAuthentication()
+    {
+        // TODO: Query user about password
+
+        // DEBUG
+        return new PasswordAuthentication( TEST_USER_NAME, 
"testPassword".toCharArray() );
+    }
+
+
+    public void loggedIn()
+    {
+        LOGGER.fine( "Logged In" );
+
+        if ( myProtocolNegotiator.startsNegotiations() )
+        {
+            try
+            {
+                myDarkstarClient.send( myProtocolNegotiator.handleMessage( 
null ) );
+            }
+            catch ( IOException e )
+            {
+                // TODO: Notify user about failure, reset server connection?
+                e.printStackTrace();  //To change body of catch statement use 
File | Settings | File Templates.
+            }
+        }
+    }
+
+
+    public void loginFailed( final String s )
+    {
+        LOGGER.fine( "Login Failed.  Reason: '" + s + "'." );
+    }
+
+    //======================================================================
+    // Private Methods
+
+    /**
+     * Initiates asynchronous login to the SGS server specified by the host 
and port properties.
+     */
+    // IDEA: This could be one action provided by a client side GameObject 
representing a server
+    private void login()
+    {
+        final String host = System.getProperty( HOST_PROPERTY, DEFAULT_HOST );
+        final String port = System.getProperty( PORT_PROPERTY, DEFAULT_PORT );
+
+        try
+        {
+            final Properties connectProps = new Properties();
+            connectProps.setProperty( "host", host );
+            connectProps.setProperty( "port", port );
+            myDarkstarClient.login( connectProps );
+        }
+        catch ( Exception e )
+        {
+            // TODO: Report error to user
+            e.printStackTrace();
+            disconnected( false, e.getMessage() );
+        }
+    }
+
+}

Copied: 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/ChatClient.java
 (from rev 433, 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/ChatClient.java)
===================================================================
--- 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/ChatClient.java
                         (rev 0)
+++ 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/ChatClient.java
 2008-04-03 22:14:54 UTC (rev 437)
@@ -0,0 +1,590 @@
+/* =========================================================================
+ *
+ * This file is part of Skycastle.
+ * 
+ * Skycastle is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public License as published by the 
+ * Free Software Foundation; either version 2 of the License, or (at your 
+ * option) any later version.
+ * 
+ * Skycastle is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License 
+ * for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along 
+ * with Skycastle; if not, write to the Free Software Foundation, 
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 
+ * ========================================================================= */
+
+package org.skycastle.client.hardcoded;
+
+import com.sun.sgs.client.ClientChannel;
+import com.sun.sgs.client.ClientChannelListener;
+import com.sun.sgs.client.SessionId;
+import com.sun.sgs.client.simple.SimpleClient;
+import com.sun.sgs.client.simple.SimpleClientListener;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.net.PasswordAuthentication;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Random;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Chat client. This client connects to the SGS Skycastle server and processes 
chat messages on a channel.
+ * Until we have implemented a real chat protocol, the client understands the 
following special messages.
+ * <p/>
+ * From server:
+ * <p/>
+ * JOIN [sessionId] [nickname] - User with nickname and sessionId has joined 
the chat.
+ * <p/>
+ * QUIT [sessionId] [nickname] - User with nickname and sessionId has left the 
chat.
+ * <p/>
+ * From client:
+ * <p/>
+ * NICK [newNickname] - User has changed nickname (TODO: this is very sloppy, 
with no protection whatsoever
+ * against duplicates).
+ * <p/>
+ * TODO:
+ * <p/>
+ * The channel-based chat has some issues since the communication is 
effectively client-to-client, even though
+ * the server acts as the hub for dispatching messages.. It is difficult to 
enforce any rules and protect
+ * clients from invalid messages. The state of the network may be difficult to 
keep consistent across clients
+ * (for example, when mapping sessions to nicknames. Each client has to do its 
own validity checking and
+ * enforce unique nicknames.). See also SkycastleServerListener.
+ */
+public class ChatClient
+        implements SimpleClientListener, ClientChannelListener
+{
+
+    //======================================================================
+    // Private Fields
+
+    /**
+     * The random number generator for login names.
+     */
+    private final Random random = new Random();
+
+    //======================================================================
+    // Non-Private Fields
+
+    /**
+     * The {@link SimpleClient} instance for this client.
+     */
+    protected final SimpleClient simpleClient;
+
+    /**
+     * Username. A placeholder until the system can handle actual user 
accounts.
+     */
+    protected String username = "";
+
+    /**
+     * Map that associates a channel name with a {@link ClientChannel}.
+     */
+    protected final Map<String, ClientChannel> channelsByName =
+            new HashMap<String, ClientChannel>();
+
+    /**
+     * Map that associates a session ID with a nickname.
+     */
+    protected final Map<SessionId, String> nicknamesById =
+            new HashMap<SessionId, String>();
+
+    /**
+     * Handler for chat messages that should be displayed.
+     */
+    protected ChatOutputHandler outputHandler = null;
+
+    /**
+     * Handler for chat user events.
+     */
+    protected ChatUserHandler userHandler;
+
+    //======================================================================
+    // Public Constants
+
+    /**
+     * The name of the general chat channel: '{@value #GENERAL_CHAT}'
+     */
+    public static final String GENERAL_CHAT = "General";
+
+    /**
+     * The name of the host property.
+     */
+    public static final String HOST_PROPERTY = "skycastle.host";
+
+    /**
+     * The default hostname.
+     */
+    public static final String DEFAULT_HOST = "localhost";
+
+    /**
+     * The name of the port property.
+     */
+    public static final String PORT_PROPERTY = "skycastle.port";
+
+    /**
+     * The default port.
+     */
+    public static final String DEFAULT_PORT = "1139";
+
+    /**
+     * The message encoding.
+     */
+    public static final String MESSAGE_CHARSET = "UTF-8";
+
+    //======================================================================
+    // Private Constants
+
+    /**
+     * The version of the serialized form of this class.
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * The {@link Logger} for this class.
+     */
+    private static final Logger logger =
+            Logger.getLogger( ChatClient.class.getName() );
+
+    //======================================================================
+    // Public Methods
+
+    //----------------------------------------------------------------------
+    // Constructors
+
+    /**
+     * Create a new chat client.
+     */
+    public ChatClient()
+    {
+        simpleClient = new SimpleClient( this );
+        // TODO: Placeholder until we have something real.
+        setUsername( "guest-" + random.nextInt( 1000 ) );
+    }
+
+
+    /**
+     * Create a new chat client.
+     *
+     * @param initOutputHandler Handler for chat messages to be displayed.
+     */
+    public ChatClient( ChatOutputHandler initOutputHandler,
+                       ChatUserHandler initUserHandler )
+    {
+        this();
+        outputHandler = initOutputHandler;
+        userHandler = initUserHandler;
+    }
+
+    //----------------------------------------------------------------------
+    // ClientChannelListener Implementation
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * This is called when a message arrives on a channel.
+     */
+    public void receivedMessage( ClientChannel channel,
+                                 SessionId sender,
+                                 byte[] message )
+    {
+        String msg = decodeString( message );
+        logger.log( Level.INFO, "Channel message: [" + channel.getName()
+                                + ", " + sender + "] " + msg );
+        if ( outputHandler != null )
+        {
+            if ( sender != null )
+            {
+                // Message from client.
+                /* Do some quick and dirty server message handling until we
+                   have implemented a real chat protocol. */
+                String clientName = nicknamesById.get( sender );
+                if ( clientName != null )
+                {
+                    /* Change nickname.
+                       NICK <nickname> */
+                    /* ----- This doesn't work well yet ----- //
+                    if (msg.startsWith("NICK"))
+                    {
+                        final String[] parts = msg.split(" ");
+                        if (parts.length >= 2)
+                        {
+                            String newNick = parts[1];
+                            nicknamesById.put(sender, newNick);
+                            getChatOutputHandler().appendChatOutput(null,
+                                clientName + " changed nickname to "
+                                + newNick + ".");
+                            getChatUserHandler().removeChatUser(clientName);
+                            getChatUserHandler().addChatUser(newNick);
+                        }
+                    } else
+                    // ----- */
+                    getChatOutputHandler().appendChatOutput( clientName, msg );
+                }
+                else
+                {
+                    getChatOutputHandler().appendChatOutput( null,
+                                                             "UNNAMED-client: 
" + msg );
+                }
+            }
+            else
+            {
+                // Message from server.
+                receivedMessage( message );
+            }
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * This is called when the user leaves a channel.
+     */
+    public void leftChannel( ClientChannel channel )
+    {
+        channelsByName.remove( channel.getName() );
+        logger.log( Level.INFO, "Left channel: " + channel.getName() );
+        // TODO: Let the user know.
+    }
+
+    //----------------------------------------------------------------------
+    // ServerSessionListener Implementation
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * This is called when the user joins a channel.
+     */
+    public ClientChannelListener joinedChannel( ClientChannel channel )
+    {
+        channelsByName.put( channel.getName(), channel );
+        logger.log( Level.INFO, "Joined channel: " + channel.getName() );
+        // TODO: Let the user know.
+        return this;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * This is called when the client receives a message from the server.
+     */
+    public void receivedMessage( byte[] message )
+    {
+        String msg = decodeString( message );
+        logger.log( Level.INFO, "Message from server: " + msg );
+        /* Do some quick and dirty server message handling until we
+           have implemented a real chat protocol. */
+        if ( msg.startsWith( "JOIN" ) )
+        {
+            /* Let users know that a client has joined the chat.
+               JOIN <sessionId> <nickname> */
+            final String[] parts = msg.split( " " );
+            if ( parts.length >= 3 )
+            {
+                SessionId senderId =
+                        SessionId.fromBytes( decodeHexString( parts[ 1 ] ) );
+                nicknamesById.put( senderId, parts[ 2 ] );
+                getChatOutputHandler().appendChatOutput( null, parts[ 2 ]
+                                                               + " joined the 
chat." );
+                getChatUserHandler().addChatUser( parts[ 2 ] );
+            }
+        }
+        else if ( msg.startsWith( "QUIT" ) )
+        {
+            /* Let users know that a client has left the chat.
+               QUIT <sessionId> <nickname> */
+            final String[] parts = msg.split( " " );
+            if ( parts.length >= 3 )
+            {
+                SessionId senderId =
+                        SessionId.fromBytes( decodeHexString( parts[ 1 ] ) );
+                String victim = nicknamesById.get( senderId );
+                if ( victim != null )
+                {
+                    nicknamesById.remove( senderId );
+                    getChatOutputHandler().appendChatOutput( null,
+                                                             victim + " left 
the chat." );
+                    getChatUserHandler().removeChatUser( parts[ 2 ] );
+                }
+            }
+        }
+        else
+        {
+            getChatOutputHandler().appendChatOutput( null,
+                                                     "Server: " + msg );
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * This is called when reconnection is attempted.
+     */
+    public void reconnecting()
+    {
+        logger.log( Level.INFO, "Reconnecting." );
+        // TODO: Let the user know.
+    }
+
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * This is called on a successful reconnect.
+     */
+    public void reconnected()
+    {
+        logger.log( Level.INFO, "Reconnected successfully." );
+        // TODO: Let the user know.
+    }
+
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * This is called when the user is disconnected.
+     */
+    public void disconnected( boolean graceful, String reason )
+    {
+        logger.log( Level.INFO, "Disconnected (" + graceful + ", " + reason
+                                + ")." );
+        // TODO: Let the user know.
+    }
+
+    //----------------------------------------------------------------------
+    // SimpleClientListener Implementation
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Returns dummy credentials where user is "guest-&lt;random&gt;" and the 
password is "guest."
+     */
+    public PasswordAuthentication getPasswordAuthentication()
+    {
+        logger.log( Level.INFO, "Logging in as " + username );
+        // TODO: Let the user know.
+        String password = "guest";
+        return new PasswordAuthentication( username, password.toCharArray() );
+    }
+
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * This is called on a successful login.
+     */
+    public void loggedIn()
+    {
+        logger.log( Level.INFO, "Logged in successfully." );
+        // TODO: Let the user know.
+    }
+
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * This is called on a failed login.
+     */
+    public void loginFailed( String reason )
+    {
+        logger.log( Level.WARNING, "Login failed (" + reason + ")!" );
+        // TODO: Let the user know.
+    }
+
+    //----------------------------------------------------------------------
+    // Other Public Methods
+
+    /**
+     * Send a message to the general chat.
+     *
+     * @param message Message to be sent.
+     */
+    public void send( String message )
+    {
+        ClientChannel channel = channelsByName.get( GENERAL_CHAT );
+        if ( channel != null )
+        {
+            try
+            {
+                channel.send( encodeString( message ) );
+            }
+            catch ( IOException e )
+            {
+                logger.log( Level.WARNING, "Could not send message on channel: 
"
+                                           + GENERAL_CHAT );
+            }
+        }
+        else
+        {
+            logger.log( Level.WARNING, "Client is not joined to channel: "
+                                       + GENERAL_CHAT );
+        }
+    }
+
+
+    /**
+     * Set the handler for chat output.
+     */
+    public void setChatOutputHandler( ChatOutputHandler handler )
+    {
+        outputHandler = handler;
+    }
+
+
+    /**
+     * Get the handler for local chat output.
+     *
+     * @return Handler for local chat output.
+     */
+    public ChatOutputHandler getChatOutputHandler()
+    {
+        return outputHandler;
+    }
+
+
+    /**
+     * Set the handler for chat user events.
+     */
+    public void setChatUserHandler( ChatUserHandler handler )
+    {
+        userHandler = handler;
+    }
+
+
+    /**
+     * Get the handler for chat user events.
+     *
+     * @return Handler for chat user events.
+     */
+    public ChatUserHandler getChatUserHandler()
+    {
+        return userHandler;
+    }
+
+
+    /**
+     * Get username.
+     */
+    public String getUsername()
+    {
+        return username;
+    }
+
+
+    /**
+     * Set username.
+     *
+     * @param newUsername Username.
+     */
+    public void setUsername( String newUsername )
+    {
+        username = newUsername;
+    }
+
+    //======================================================================
+    // Protected Methods
+
+    /**
+     * Initiates asynchronous login to the SGS server specified by the host 
and port properties.
+     */
+    protected void login()
+    {
+        String host = System.getProperty( HOST_PROPERTY, DEFAULT_HOST );
+        String port = System.getProperty( PORT_PROPERTY, DEFAULT_PORT );
+
+        try
+        {
+            Properties connectProps = new Properties();
+            connectProps.put( "host", host );
+            connectProps.put( "port", port );
+            simpleClient.login( connectProps );
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+            disconnected( false, e.getMessage() );
+        }
+    }
+
+
+    /**
+     * Encodes a {@code String} into an array of bytes.
+     *
+     * @param s the string to encode
+     *
+     * @return the byte array which encodes the given string
+     */
+    protected static byte[] encodeString( String s )
+    {
+        try
+        {
+            return s.getBytes( MESSAGE_CHARSET );
+        }
+        catch ( UnsupportedEncodingException e )
+        {
+            throw new Error( "Required character set " + MESSAGE_CHARSET
+                             + " not found", e );
+        }
+    }
+
+
+    /**
+     * Decodes an array of bytes into a {@code String}.
+     *
+     * @param bytes the bytes to decode
+     *
+     * @return the decoded string
+     */
+    protected static String decodeString( byte[] bytes )
+    {
+        try
+        {
+            return new String( bytes, MESSAGE_CHARSET );
+        }
+        catch ( UnsupportedEncodingException e )
+        {
+            throw new Error( "Required character set " + MESSAGE_CHARSET
+                             + " not found", e );
+        }
+    }
+
+
+    /**
+     * Encode an array of bytes into a hexadecimal string.
+     *
+     * @param bytes The bytes to be encoded.
+     *
+     * @return The encoded string.
+     */
+    protected static String encodeHexString( byte[] bytes )
+    {
+        BigInteger bi = new BigInteger( bytes );
+        return bi.toString( 16 );
+    }
+
+
+    /**
+     * Decode a hexadecimal string into an array of bytes.
+     *
+     * @param source The string to be decoded.
+     *
+     * @return The decoded string.
+     */
+    protected static byte[] decodeHexString( String source )
+    {
+        BigInteger bi = new BigInteger( source, 16 );
+        return bi.toByteArray();
+    }
+
+}

Copied: 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/ChatInputHandler.java
 (from rev 433, 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/ChatInputHandler.java)
===================================================================
--- 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/ChatInputHandler.java
                           (rev 0)
+++ 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/ChatInputHandler.java
   2008-04-03 22:14:54 UTC (rev 437)
@@ -0,0 +1,34 @@
+/* =========================================================================
+ *
+ * This file is part of Skycastle.
+ * 
+ * Skycastle is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your 
+ * option) any later version.
+ * 
+ * Skycastle is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty of 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along 
+ * with Skycastle; if not, write to the Free Software Foundation, 
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 
+ * ========================================================================= */
+
+package org.skycastle.client.hardcoded;
+
+/**
+ * Handler for chat text entered by the local user.
+ */
+public interface ChatInputHandler
+{
+    /**
+     * Process a line of chat input.
+     *
+     * @param chatMessage Text of the chat message.
+     */
+    public void processChatInput( String chatMessage );
+}

Copied: 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/ChatOutputHandler.java
 (from rev 433, 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/ChatOutputHandler.java)
===================================================================
--- 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/ChatOutputHandler.java
                          (rev 0)
+++ 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/ChatOutputHandler.java
  2008-04-03 22:14:54 UTC (rev 437)
@@ -0,0 +1,35 @@
+/* =========================================================================
+ *
+ * This file is part of Skycastle.
+ * 
+ * Skycastle is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your 
+ * option) any later version.
+ * 
+ * Skycastle is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty of 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along 
+ * with Skycastle; if not, write to the Free Software Foundation, 
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 
+ * ========================================================================= */
+
+package org.skycastle.client.hardcoded;
+
+/**
+ * Handler for chat text recieved from other users.
+ */
+public interface ChatOutputHandler
+{
+    /**
+     * Append a line of chat output.
+     *
+     * @param who         User from which the chat message has been received.
+     * @param chatMessage Text of the chat message.
+     */
+    void appendChatOutput( String who, String chatMessage );
+}

Copied: 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/ChatUserHandler.java
 (from rev 433, 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/ChatUserHandler.java)
===================================================================
--- 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/ChatUserHandler.java
                            (rev 0)
+++ 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/ChatUserHandler.java
    2008-04-03 22:14:54 UTC (rev 437)
@@ -0,0 +1,42 @@
+/* =========================================================================
+ *
+ * This file is part of Skycastle.
+ * 
+ * Skycastle is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your 
+ * option) any later version.
+ * 
+ * Skycastle is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty of 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along 
+ * with Skycastle; if not, write to the Free Software Foundation, 
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 
+ * ========================================================================= */
+
+package org.skycastle.client.hardcoded;
+
+/**
+ * Handler for chat user events. Classes which implement this interface handle 
events that pertain to users of
+ * the chat, such as adding a user to the chat or removing a user from the 
chat.
+ */
+public interface ChatUserHandler
+{
+    /**
+     * Add a user to the chat.
+     *
+     * @param who User to be added.
+     */
+    void addChatUser( String who );
+
+    /**
+     * Remove a user from the chat.
+     *
+     * @param who User to be removed.
+     */
+    void removeChatUser( String who );
+}

Copied: 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/Simple3DClient.java
 (from rev 433, 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/Simple3DClient.java)
===================================================================
--- 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/Simple3DClient.java
                             (rev 0)
+++ 
trunk/skycastle/modules/client/src/main/java/org/skycastle/client/hardcoded/Simple3DClient.java
     2008-04-03 22:14:54 UTC (rev 437)
@@ -0,0 +1,551 @@
+/* =========================================================================
+ *
+ * This file is part of Skycastle.
+ * 
+ * Skycastle is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public License as published by the 
+ * Free Software Foundation; either version 2 of the License, or (at your 
+ * option) any later version.
+ * 
+ * Skycastle is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License 
+ * for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along 
+ * with Skycastle; if not, write to the Free Software Foundation, 
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 
+ * ========================================================================= */
+
+package org.skycastle.client.hardcoded;
+
+import com.jme.app.SimpleGame;
+import com.jme.bounding.BoundingBox;
+import com.jme.image.Texture;
+import com.jme.input.*;
+import com.jme.math.Vector3f;
+import com.jme.renderer.ColorRGBA;
+import com.jme.renderer.Renderer;
+import com.jme.scene.Controller;
+import com.jme.scene.Node;
+import com.jme.scene.SceneElement;
+import com.jme.scene.shape.Box;
+import com.jme.scene.state.LightState;
+import com.jme.scene.state.TextureState;
+import com.jme.util.TextureManager;
+import com.jmex.awt.swingui.JMEDesktop;
+
+import javax.swing.*;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Simple 3D client. A simple 3D client for Skycastle. This version displays a 
chat window and a rotating box
+ * in the background. The client is based on the SimpleGame game skeleton  
provided by the jME (this will
+ * probably change in the future). Chat messages are processed by handlers 
which can be set using the
+ * setChatInputHandler() and setChatOutputHandler() methods. 
setChatUserHandler() is used to specify the user
+ * event handler, which at this time handles adding and removing user names 
from the list. Right now,
+ * everything is handled by the Simple3DClient itself.
+ */
+public class Simple3DClient
+        extends SimpleGame
+        implements ChatInputHandler, ChatOutputHandler, ChatUserHandler
+{
+
+    //======================================================================
+    // Non-Private Fields
+
+    /**
+     * The color to be used as background color for the desktop. This is 
completely transparent right now.
+     */
+    protected static final Color DESKTOP_BACKGROUND_COLOR = new Color(
+            0, 1, 0, 0.0f );
+
+    /**
+     * Keyboard look input handler.
+     */
+    protected KeyboardLookHandler lookHandler;
+    /**
+     * Scene node for the GUI.
+     */
+    protected Node guiNode;
+    /**
+     * JME desktop on which the GUI will be shown.
+     */
+    protected JMEDesktop desktop;
+    /**
+     * Frame containing the chat UI.
+     */
+    protected JInternalFrame chatFrame;
+    /**
+     * Text field used for chat input.
+     */
+    protected JTextField chatInput;
+    /**
+     * Text pane used for chat output.
+     */
+    protected JTextPane chatOutput;
+    /**
+     * List model for the chat user list.
+     */
+    protected DefaultListModel chatUserListModel;
+    /**
+     * List view used for the chat user list.
+     */
+    protected JList chatUserList;
+    /**
+     * Handler for incoming local chat messages.
+     */
+    protected ChatInputHandler chatInputHandler;
+    /**
+     * Handler for chat messages that should be displayed.
+     */
+    protected ChatOutputHandler chatOutputHandler;
+    /**
+     * Handler for chat user events.
+     */
+    protected ChatUserHandler chatUserHandler;
+    /**
+     * Chat client.
+     */
+    protected ChatClient chatClient;
+
+    //======================================================================
+    // Private Constants
+
+    private static final Logger LOGGER = Logger.getLogger( 
Simple3DClient.class.getName() );
+
+    //======================================================================
+    // Public Methods
+
+    //----------------------------------------------------------------------
+    // Main Method
+
+    /**
+     * Start everything.
+     */
+    public static void main( String[] args )
+    {
+        LOGGER.log( Level.INFO, "Skycastle 3D Client starting up." );
+
+        // Do not show logging output below the WARNING level (JME outputs a 
lot of debugging info at INFO level).
+        // This way the console output is a bit more relevant.
+        LOGGER.setLevel( Level.WARNING );
+
+        Simple3DClient client = new Simple3DClient();
+        client.start();
+    }
+
+    //----------------------------------------------------------------------
+    // ChatInputHandler Implementation
+
+    /**
+     * Process a line of input from the chat text field. This gets called in 
the Swing thread when the enters
+     * a line of chat text. Implemented from ChatInputHandler.
+     *
+     * @param chatMessage Text of the chat message.
+     */
+    public void processChatInput( String chatMessage )
+    {
+        // Print to the console and show in chat output window.
+        LOGGER.log( Level.INFO, "chat: " + chatMessage );
+        getChatOutputHandler().appendChatOutput( chatClient.getUsername(),
+                                                 chatMessage );
+        chatClient.send( chatMessage );
+    }
+
+    //----------------------------------------------------------------------
+    // ChatOutputHandler Implementation
+
+    /**
+     * Append a line of chat text to the chat output area. Implemented from 
ChatOutputHandler.
+     *
+     * @param who         User from which the chat message has been received.
+     * @param chatMessage Text of the chat message.
+     */
+    public void appendChatOutput( String who, String chatMessage )
+    {
+        // Show in chat output window.
+        if ( who != null )
+        {
+            chatOutput.setText( chatOutput.getText() + "\n"
+                                + "<" + who + "> " + chatMessage );
+        }
+        else
+        {
+            chatOutput.setText( chatOutput.getText() + "\n" + chatMessage );
+        }
+    }
+
+    //----------------------------------------------------------------------
+    // ChatUserHandler Implementation
+
+    /**
+     * Add a user to the chat.
+     *
+     * @param who User to be added.
+     */
+    public void addChatUser( String who )
+    {
+        boolean found = false;
+        int i = 0;
+        while ( !found
+                && ( i < chatUserListModel.size() ) )
+        {
+            if ( chatUserListModel.elementAt( i ).equals( who ) )
+            {
+                found = true;
+            }
+            else
+            {
+                i++;
+            }
+        }
+        if ( found )
+        {
+            LOGGER.log( Level.WARNING, "User '" + who + "' is already in the 
list!" );
+            return;
+        }
+        chatUserListModel.addElement( who );
+    }
+
+
+    /**
+     * Remove a user from the chat.
+     *
+     * @param who User to be removed.
+     */
+    public void removeChatUser( String who )
+    {
+        boolean found = false;
+        int i = 0;
+        while ( !found
+                && ( i < chatUserListModel.size() ) )
+        {
+            if ( chatUserListModel.elementAt( i ).equals( who ) )
+            {
+                found = true;
+            }
+            else
+            {
+                i++;
+            }
+        }
+        if ( found )
+        {
+            chatUserListModel.removeElementAt( i );
+        }
+        else
+        {
+            LOGGER.log( Level.WARNING, "User '" + who + "' is not in the 
list!" );
+        }
+    }
+
+    //----------------------------------------------------------------------
+    // Other Public Methods
+
+    /**
+     * Set up key bindings.
+     */
+    public void initKeyBindings()
+    {
+        /* Remove some keybindings established by BaseSimpleGame, since they
+           are needed for chatting.
+           TODO: Find a more elegant solution. Maybe bind/rebind depending on
+                 focus, or handling these during update.
+                 Eventually, we may also need to implement our own base game
+                 class.
+         */
+        KeyBindingManager.getKeyBindingManager().remove( "toggle_pause" );
+        KeyBindingManager.getKeyBindingManager().remove( "step" );
+        KeyBindingManager.getKeyBindingManager().remove( "toggle_wire" );
+        KeyBindingManager.getKeyBindingManager().remove( "toggle_lights" );
+        KeyBindingManager.getKeyBindingManager().remove( "toggle_bounds" );
+        KeyBindingManager.getKeyBindingManager().remove( "toggle_normals" );
+        KeyBindingManager.getKeyBindingManager().remove( "camera_out" );
+        KeyBindingManager.getKeyBindingManager().remove( "mem_report" );
+
+        // Bind the BaseSimpleGame functions to other keys.
+        KeyBindingManager.getKeyBindingManager().set( "toggle_pause", 
KeyInput.KEY_F4 );
+        KeyBindingManager.getKeyBindingManager().set( "step", KeyInput.KEY_F5 
);
+        KeyBindingManager.getKeyBindingManager().set( "toggle_wire", 
KeyInput.KEY_F6 );
+        KeyBindingManager.getKeyBindingManager().set( "toggle_lights", 
KeyInput.KEY_F7 );
+        KeyBindingManager.getKeyBindingManager().set( "toggle_bounds", 
KeyInput.KEY_F8 );
+        KeyBindingManager.getKeyBindingManager().set( "toggle_normals", 
KeyInput.KEY_F9 );
+        KeyBindingManager.getKeyBindingManager().set( "camera_out", 
KeyInput.KEY_F10 );
+        KeyBindingManager.getKeyBindingManager().set( "mem_report", 
KeyInput.KEY_F11 );
+    }
+
+
+    /**
+     * Set the handlers for chat events.
+     *
+     * @param inputHandler  Handler for chat input entered by the local user.
+     * @param outputHandler Handler for chat message sto be displayed.
+     * @param userHandler   Handler for chat user events.
+     */
+    public void setChatHandlers( ChatInputHandler inputHandler,
+                                 ChatOutputHandler outputHandler, 
ChatUserHandler userHandler )
+    {
+        setChatInputHandler( inputHandler );
+        setChatOutputHandler( outputHandler );
+        setChatUserHandler( userHandler );
+    }
+
+
+    /**
+     * Set the handler for local chat input.
+     */
+    public void setChatInputHandler( ChatInputHandler handler )
+    {
+        chatInputHandler = handler;
+    }
+
+
+    /**
+     * Get the handler for local chat input.
+     *
+     * @return Handler for local chat input.
+     */
+    public ChatInputHandler getChatInputHandler()
+    {
+        return chatInputHandler;
+    }
+
+
+    /**
+     * Set the handler for chat output.
+     */
+    public void setChatOutputHandler( ChatOutputHandler handler )
+    {
+        chatOutputHandler = handler;
+    }
+
+
+    /**
+     * Get the handler for local chat output.
+     *
+     * @return Handler for local chat output.
+     */
+    public ChatOutputHandler getChatOutputHandler()
+    {
+        return chatOutputHandler;
+    }
+
+
+    /**
+     * Set the handler for chat user events.
+     */
+    public void setChatUserHandler( ChatUserHandler handler )
+    {
+        chatUserHandler = handler;
+    }
+
+
+    /**
+     * Get the handler for chat user events.
+     *
+     * @return Handler for chat user events.
+     */
+    public ChatUserHandler getChatUserHandler()
+    {
+        return chatUserHandler;
+    }
+
+    //======================================================================
+    // Protected Methods
+
+    /**
+     * Create the UI. Create the elements of the Swing UI.
+     */
+    protected void initUI()
+    {
+        /// JME part of the GUI creation.
+        guiNode = new Node( "gui" );
+        guiNode.setRenderQueueMode( Renderer.QUEUE_ORTHO );
+
+        desktop = new JMEDesktop(
+                "desktop",
+                display.getWidth(),
+                display.getHeight(),
+                input
+        );
+        guiNode.attachChild( desktop );
+        desktop.getLocalTranslation().set(
+                display.getWidth() / 2,
+                display.getHeight() / 2, 0
+        );
+
+        // AWT/Swing part of the GUI creation.
+        desktop.getJDesktop().setBackground( DESKTOP_BACKGROUND_COLOR );
+
+        chatFrame = new JInternalFrame( "Chat" );
+        chatFrame.setLocation( 10, 10 );
+        chatFrame.setResizable( true );
+        Container contentPane = chatFrame.getContentPane();
+        contentPane.setLayout( new BorderLayout() );
+
+        chatOutput = new JTextPane();
+        chatOutput.setPreferredSize( new Dimension( 400, 300 ) );
+        chatOutput.setEditable( false );
+        chatOutput.setText( "Welcome to Skycastle!" );
+
+        final JScrollPane scrollPane = new JScrollPane( chatOutput );
+
+        chatUserListModel = new DefaultListModel();
+
+        chatUserList = new JList( chatUserListModel );
+        chatUserList.setPreferredSize( new Dimension( 80, 300 ) );
+
+        final JSplitPane splitPane = new JSplitPane(
+                JSplitPane.HORIZONTAL_SPLIT,
+                chatUserList,
+                scrollPane
+        );
+
+        chatInput = new JTextField();
+        chatInput.addActionListener(
+                new ActionListener()
+                {
+
+                    public void actionPerformed( ActionEvent e )
+                    {
+                        getChatInputHandler().processChatInput(
+                                chatInput.getText() );
+                        chatInput.setText( "" );
+                    }
+
+                }
+        );
+
+        contentPane.add( splitPane, BorderLayout.CENTER );
+        contentPane.add( chatInput, BorderLayout.SOUTH );
+
+        chatFrame.setVisible( true );
+        chatFrame.pack();
+
+        desktop.getJDesktop().add( chatFrame );
+    }
+
+
+    /**
+     * Create the 3D scene. In this case, this is just the rotating box.
+     */
+    protected void create3DScene()
+    {
+        final Vector3f axis = new Vector3f( 1, 1, 0.5f ).normalizeLocal();
+        final Box box = new Box( "Box",
+                                 new Vector3f( -5, -5, -5 ),
+                                 new Vector3f( 5, 5, 5 )
+        );
+
+        box.setModelBound( new BoundingBox() );
+        box.updateModelBound();
+        box.setLocalTranslation( new Vector3f( 0, 0, -10 ) );
+        box.setLightCombineMode( LightState.OFF );
+
+        TextureState ts = display.getRenderer().createTextureState();
+        ts.setEnabled( true );
+        ts.setTexture( TextureManager.loadTexture(
+                Simple3DClient.class.getClassLoader().getResource(
+                        "org/skycastle/client/data/skycastle.png" ),
+                Texture.MM_LINEAR,
+                Texture.FM_LINEAR )
+        );
+        box.setRenderState( ts );
+        box.addController(
+                // Controller which rotates the box.
+                new Controller()
+                {
+
+                    private static final long serialVersionUID = 1L;
+
+                    public void update( float time )
+                    {
+                        box.getLocalRotation().fromAngleNormalAxis(
+                                timer.getTimeInSeconds(), axis );
+                    }
+
+                }
+        );
+
+        rootNode.attachChild( box );
+    }
+
+
+    /**
+     * Initialize the chat client.
+     */
+    protected void initChatClient()
+    {
+        /* Use this Simple3DClient for handling chat output and user events. */
+        chatClient = new ChatClient( this, this );
+        chatClient.login();
+    }
+
+
+    /**
+     * Initialize game. Overridden from SimpleGame.
+     */
+    protected void simpleInitGame()
+    {
+        display.setTitle( "Skycastle 3D Client" );
+        display.getRenderer().setBackgroundColor( ColorRGBA.black );
+
+        // Set up the keyboard look handler.
+        input = new InputHandler();
+        lookHandler = new KeyboardLookHandler( cam, 50, 1 );
+        input.addToAttachedHandlers( lookHandler );
+        // Some changes to the BaseSimpleGame keyboard bindings.
+        initKeyBindings();
+
+        // Set up GUI, static 3D scene and chat client.
+        initUI();
+        create3DScene();
+        initChatClient();
+
+        // Handle chat input messages as well as chat output and user events.
+        setChatHandlers( this, this, this );
+
+        // Always show the the GUI.
+        guiNode.setCullMode( SceneElement.CULL_NEVER );
+        guiNode.setLightCombineMode( LightState.OFF );
+        guiNode.updateRenderState();
+        guiNode.updateGeometricState( 0, true );
+        MouseInput.get().setCursorVisible( true );
+    }
+
+
+    /**
+     * Update hook. Overridden from SimpleGame.
+     */
+    protected void simpleUpdate()
+    {
+        // Ignore keyboard events if the chat input field has the focus.
+        if ( chatFrame.getFocusOwner() != chatInput )
+        {
+            lookHandler.setEnabled( true );
+        }
+        else
+        {
+            lookHandler.setEnabled( false );
+        }
+    }
+
+
+    /**
+     * Render hook. Overridden from SimpleGame.
+     */
+    protected void simpleRender()
+    {
+        display.getRenderer().draw( guiNode );
+    }
+
+}


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

Other related posts:

  • » [skycastle-commits] SF.net SVN: skycastle: [437] trunk/skycastle/modules/client