Revision: 432 http://skycastle.svn.sourceforge.net/skycastle/?rev=432&view=rev Author: zzorn Date: 2008-04-03 07:58:54 -0700 (Thu, 03 Apr 2008) Log Message: ----------- Copied the current hard coded chat client server implementation, and started working on a GameObject based server. Modified Paths: -------------- trunk/skycastle/modules/core/src/main/java/org/skycastle/core/clientside/ProxyGameObject.java trunk/skycastle/modules/core/src/main/java/org/skycastle/messaging/updates/object/GameObjectAddedMessage.java trunk/skycastle/modules/server/SkycastleServer.properties Added Paths: ----------- trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleClientSessionHandler.java trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleServer.java trunk/skycastle/modules/server/src/main/java/org/skycastle/server/hardcoded/ trunk/skycastle/modules/server/src/main/java/org/skycastle/server/hardcoded/SkycastleChannelListener.java trunk/skycastle/modules/server/src/main/java/org/skycastle/server/hardcoded/SkycastleClientSessionListener.java trunk/skycastle/modules/server/src/main/java/org/skycastle/server/hardcoded/SkycastleServerListener.java Removed Paths: ------------- trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleChannelListener.java trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleClientSessionListener.java trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleServerListener.java Modified: trunk/skycastle/modules/core/src/main/java/org/skycastle/core/clientside/ProxyGameObject.java =================================================================== --- trunk/skycastle/modules/core/src/main/java/org/skycastle/core/clientside/ProxyGameObject.java 2008-04-03 04:08:02 UTC (rev 431) +++ trunk/skycastle/modules/core/src/main/java/org/skycastle/core/clientside/ProxyGameObject.java 2008-04-03 14:58:54 UTC (rev 432) @@ -26,13 +26,11 @@ // Constructors /** - * @param id {@link GameObjectId} of this {@link ProxyGameObject}. - * @param model the {@link GameModel} that can be used to find other {@link GameObject}s by id. + * @param id {@link GameObjectId} of this {@link ProxyGameObject}. */ - public ProxyGameObject( final GameObjectId id, final GameModel model ) + public ProxyGameObject( final GameObjectId id ) { setId( id ); } - } Modified: trunk/skycastle/modules/core/src/main/java/org/skycastle/messaging/updates/object/GameObjectAddedMessage.java =================================================================== --- trunk/skycastle/modules/core/src/main/java/org/skycastle/messaging/updates/object/GameObjectAddedMessage.java 2008-04-03 04:08:02 UTC (rev 431) +++ trunk/skycastle/modules/core/src/main/java/org/skycastle/messaging/updates/object/GameObjectAddedMessage.java 2008-04-03 14:58:54 UTC (rev 432) @@ -77,7 +77,7 @@ { ParameterChecker.checkNotNull( gameModel, "gameModel" ); - gameModel.addGameObject( new ProxyGameObject( myAddedObjectId, gameModel ) ); + gameModel.addGameObject( new ProxyGameObject( myAddedObjectId ) ); } } Modified: trunk/skycastle/modules/server/SkycastleServer.properties =================================================================== --- trunk/skycastle/modules/server/SkycastleServer.properties 2008-04-03 04:08:02 UTC (rev 431) +++ trunk/skycastle/modules/server/SkycastleServer.properties 2008-04-03 14:58:54 UTC (rev 432) @@ -1,4 +1,4 @@ com.sun.sgs.app.name=SkycastleServer com.sun.sgs.app.root=data com.sun.sgs.app.port=1139 -com.sun.sgs.app.listener=org.skycastle.server.SkycastleServerListener +com.sun.sgs.app.listener=org.skycastle.server.hardcoded.SkycastleServerListener Deleted: trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleChannelListener.java =================================================================== --- trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleChannelListener.java 2008-04-03 04:08:02 UTC (rev 431) +++ trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleChannelListener.java 2008-04-03 14:58:54 UTC (rev 432) @@ -1,53 +0,0 @@ -/* ========================================================================= - * - * 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.server; - -import com.sun.sgs.app.*; - -import java.io.Serializable; -import java.io.UnsupportedEncodingException; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Channel listener for the Skycastle server. - * Listens for messages sent to a channel by a client. This is just used for - * logging right now. - */ -public class SkycastleChannelListener - implements Serializable, ChannelListener -{ - /** 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(SkycastleChannelListener.class.getName()); - - public void receivedMessage(Channel channel, ClientSession session, - byte[] message) - { - logger.log(Level.INFO, - "Channel message from {0} on channel {1}", - new Object[] { session.getName(), channel.getName() } - ); - } -} Copied: trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleClientSessionHandler.java (from rev 426, trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleClientSessionListener.java) =================================================================== --- trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleClientSessionHandler.java (rev 0) +++ trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleClientSessionHandler.java 2008-04-03 14:58:54 UTC (rev 432) @@ -0,0 +1,103 @@ +package org.skycastle.server; + +import com.sun.sgs.app.*; +import org.skycastle.server.hardcoded.SkycastleServerListener; + +import java.io.Serializable; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Initializes a client session and listens to messages (commands) and session + * events from the client. + */ +public class SkycastleClientSessionHandler + implements Serializable, ClientSessionListener +{ + + //====================================================================== + // Private Fields + + /** + * The session this {@code ClientSessionListener} is listening to. + */ + private final ClientSession mySession; + + //====================================================================== + // 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( org.skycastle.server.hardcoded.SkycastleClientSessionListener.class.getName() ); + + + /** + * The server listener. + * This is used for querying connected users etc. + */ + protected ManagedReference serverListener; + + //====================================================================== + // Public Methods + + //---------------------------------------------------------------------- + // Constructors + + /** + * Creates a new {@code HelloChannelsSessionListener} for the given + * session, and joins it to the given channels. + * + * @param session the session this listener is associated with + */ + public SkycastleClientSessionHandler( ClientSession session, + SkycastleServer initServerListener ) + { + this.mySession = session; + final DataManager dataManager = AppContext.getDataManager(); + serverListener = dataManager.createReference( initServerListener ); + + // Add client to general chat directly + final ChannelManager channelManager = AppContext.getChannelManager(); + final Channel generalChat = channelManager.getChannel( SkycastleServerListener.GENERAL_CHAT ); + generalChat.join( session, null ); + } + + //---------------------------------------------------------------------- + // ClientSessionListener Implementation + + + /** + * {@inheritDoc} + * <p/> + * Logs when data arrives from the client, and echoes the message back. + */ + public void receivedMessage( byte[] message ) + { + // DEBUG: + LOGGER.log( Level.INFO, "Direct message from {0}", mySession.getName() ); + } + + /** + * {@inheritDoc} + * <p/> + * Logs when the client disconnects. + */ + public void disconnected( boolean graceful ) + { + // DEBUG: + final String grace = graceful ? "graceful" : "forced"; + LOGGER.log( Level.INFO, + "User {0} has logged out {1}", + new Object[]{ mySession.getName(), grace } + ); + } + + +} \ No newline at end of file Property changes on: trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleClientSessionHandler.java ___________________________________________________________________ Name: svn:mime-type + text/plain Name: svn:keywords + Id Name: svn:eol-style + native Deleted: trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleClientSessionListener.java =================================================================== --- trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleClientSessionListener.java 2008-04-03 04:08:02 UTC (rev 431) +++ trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleClientSessionListener.java 2008-04-03 14:58:54 UTC (rev 432) @@ -1,218 +0,0 @@ -package org.skycastle.server; - -import com.sun.sgs.app.*; - -import java.io.Serializable; -import java.io.UnsupportedEncodingException; -import java.math.BigInteger; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Initializes a client session and listens to messages (commands) and session - * events from the client. - * <p/> - * TODO: - * <p/> - * See SkycastleServerListener. Like SkycastleServerListener, this is a - * general hub for dispatching network communication and should not - * specifically implement chat service features. - */ -public class SkycastleClientSessionListener - implements Serializable, ClientSessionListener -{ - - //====================================================================== - // Private Fields - - /** - * The session this {@code ClientSessionListener} is listening to. - */ - private final ClientSession session; - - //====================================================================== - // 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( SkycastleClientSessionListener.class.getName() ); - - /** - * The message encoding. - */ - public static final String MESSAGE_CHARSET = "UTF-8"; - - /** - * The server listener. - * This is used for querying connected users etc. - */ - protected ManagedReference serverListener; - - //====================================================================== - // Public Methods - - //---------------------------------------------------------------------- - // Constructors - - /** - * Creates a new {@code HelloChannelsSessionListener} for the given - * session, and joins it to the given channels. - * - * @param session the session this listener is associated with - */ - public SkycastleClientSessionListener( ClientSession session, - SkycastleServerListener initServerListener ) - { - this.session = session; - final DataManager dataManager = AppContext.getDataManager(); - serverListener = dataManager.createReference( initServerListener ); - - // Add client to general chat directly - final ChannelManager channelManager = AppContext.getChannelManager(); - final Channel generalChat = channelManager.getChannel( SkycastleServerListener.GENERAL_CHAT ); - generalChat.join( session, null ); - /* Let the connected users know that the user has joined the chat by - sending a JOIN message. */ - generalChat.send( encodeString( "JOIN " - + encodeHexString( session.getSessionId().getBytes() ) + " " - + session.getName() ) ); - /* Let the user know who else is there. */ - sendUserList(); - } - - //---------------------------------------------------------------------- - // ClientSessionListener Implementation - - - /** - * {@inheritDoc} - * <p/> - * Logs when data arrives from the client, and echoes the message back. - */ - public void receivedMessage( byte[] message ) - { - // DEBUG: - logger.log( Level.INFO, "Direct message from {0}", session.getName() ); - } - - /** - * {@inheritDoc} - * <p/> - * Logs when the client disconnects. - */ - public void disconnected( boolean graceful ) - { - final ChannelManager channelManager = AppContext.getChannelManager(); - final Channel generalChat = channelManager.getChannel( SkycastleServerListener.GENERAL_CHAT ); - final SkycastleServerListener sl = - serverListener.get( SkycastleServerListener.class ); - /* Let the connected users know that the user has left the chat by - sending a QUIT message. */ - generalChat.send( encodeString( "QUIT " - + encodeHexString( session.getSessionId().getBytes() ) + " " - + session.getName() ) ); - sl.removeUser( session.getSessionId() ); - // DEBUG: - final String grace = graceful ? "graceful" : "forced"; - logger.log( Level.INFO, - "User {0} has logged out {1}", - new Object[]{ session.getName(), grace } - ); - } - - /** - * Send the list of users as a sequence of join messages. - */ - protected void sendUserList() - { - final SkycastleServerListener sl = - serverListener.get( SkycastleServerListener.class ); - Set<Map.Entry<ClientSessionId, String>> entries = - sl.getConnectedUsers(); - for ( Map.Entry<ClientSessionId, String> e : entries ) - { - if ( !e.getKey().equals( session.getSessionId() ) ) - { - session.send( encodeString( "JOIN " - + encodeHexString( e.getKey().getBytes() ) + " " - + e.getValue() ) ); - } - } - } - - /** - * 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(); - } -} - Added: trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleServer.java =================================================================== --- trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleServer.java (rev 0) +++ trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleServer.java 2008-04-03 14:58:54 UTC (rev 432) @@ -0,0 +1,39 @@ +package org.skycastle.server; + +import com.sun.sgs.app.AppListener; +import com.sun.sgs.app.ClientSession; +import com.sun.sgs.app.ClientSessionListener; + +import java.io.Serializable; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * The Skycastle Server, running inside Darkstar. Recieves events when the server is started the first time, + * and when a client logs in. + * + * @author Hans Haggstrom + */ +public class SkycastleServer + implements Serializable, AppListener +{ + public void initialize( final Properties properties ) + { + } + + /** + * The {@link Logger} for this class. + */ + private static final Logger LOGGER = + Logger.getLogger( SkycastleServer.class.getName() ); + + public ClientSessionListener loggedIn( final ClientSession clientSession ) + { + // DEBUG: + LOGGER.log( Level.INFO, "User {0} has logged in", clientSession.getName() ); + + + return new SkycastleClientSessionHandler( clientSession, this ); + } +} Deleted: trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleServerListener.java =================================================================== --- trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleServerListener.java 2008-04-03 04:08:02 UTC (rev 431) +++ trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleServerListener.java 2008-04-03 14:58:54 UTC (rev 432) @@ -1,124 +0,0 @@ -package org.skycastle.server; - -import com.sun.sgs.app.*; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - - -/** - * The Skycastle Server, running inside Darkstar. Recieves events when the server is started the first time, - * and when a client logs in. - * <p/> - * This also implements some features of a chat server right now, which means maintaining the list of users - * for the general chat. - * <p/> - * TODO: - * <p/> - * 1) Channels are probably not the right way to do implement a chat, since they are essentially a method for - * peer-to-peer communication, not message dispatching. It is not possible to enforce any state since clients - * are free to post arbitrary messages on the channel which are then received by all other clients. There is - * no possibility for the server to filter out bogus messages. - * <p/> - * 2) Chat functionality should be handled by a dedicated service, not the server listener, which is the point - * where new connections to the game in general are accepted. Chat is (if at all) just one of the services - * offered by the Skycastle server. - * <p/> - * 3) Implement a proper chat protocol, or even better, use idioms that will remain valid as development on - * other game features progresses. - */ -public class SkycastleServerListener - implements Serializable, AppListener -{ - - //====================================================================== - // Public Constants - - /** - * The name of the general chat channel: '{@value #GENERAL_CHAT}' - */ - public static final String GENERAL_CHAT = "General"; - - //====================================================================== - // 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( SkycastleServerListener.class.getName() ); - - /** - * Map that associates a session ID with a nickname. - */ - protected final Map<ClientSessionId, String> nicknamesById = - new HashMap<ClientSessionId, String>(); - - //====================================================================== - // Public Methods - - //---------------------------------------------------------------------- - // AppListener Implementation - - /** - * {@inheritDoc} - * <p/> - * Creates the general chat channel. Channels persist across server restarts, so they only need to be - * created here in {@code initialize}. - */ - public void initialize( Properties props ) - { - final ChannelManager channelManager = AppContext.getChannelManager(); - - channelManager.createChannel( GENERAL_CHAT, null, Delivery.RELIABLE ); - } - - - /** - * {@inheritDoc} - * <p/> - * Returns a {@link SkycastleClientSessionListener} for the logged-in session. - */ - public SkycastleClientSessionListener loggedIn( ClientSession session ) - { - // DEBUG: - logger.log( Level.INFO, "User {0} has logged in", session.getName() ); - nicknamesById.put( session.getSessionId(), session.getName() ); - - - return new SkycastleClientSessionListener( session, this ); - } - - /** - * Get a set of nicknames of currently connected users keyed by session ID. - * - * @return Set of nicknames. - */ - public Set<Map.Entry<ClientSessionId, String>> getConnectedUsers() - { - return nicknamesById.entrySet(); - } - - /** - * Remove the user with the specified session ID from the nickname list. - * - * @param sessionId session ID of the user to be removed - */ - public void removeUser( ClientSessionId sessionId ) - { - nicknamesById.remove( sessionId ); - } - -} - - Copied: trunk/skycastle/modules/server/src/main/java/org/skycastle/server/hardcoded/SkycastleChannelListener.java (from rev 426, trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleChannelListener.java) =================================================================== --- trunk/skycastle/modules/server/src/main/java/org/skycastle/server/hardcoded/SkycastleChannelListener.java (rev 0) +++ trunk/skycastle/modules/server/src/main/java/org/skycastle/server/hardcoded/SkycastleChannelListener.java 2008-04-03 14:58:54 UTC (rev 432) @@ -0,0 +1,58 @@ +/* ========================================================================= + * + * 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.server.hardcoded; + +import com.sun.sgs.app.Channel; +import com.sun.sgs.app.ChannelListener; +import com.sun.sgs.app.ClientSession; + +import java.io.Serializable; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Channel listener for the Skycastle server. + * Listens for messages sent to a channel by a client. This is just used for + * logging right now. + */ +public class SkycastleChannelListener + implements Serializable, ChannelListener +{ + /** + * 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( SkycastleChannelListener.class.getName() ); + + public void receivedMessage( Channel channel, ClientSession session, + byte[] message ) + { + logger.log( Level.INFO, + "Channel message from {0} on channel {1}", + new Object[]{ session.getName(), channel.getName() } + ); + } +} Copied: trunk/skycastle/modules/server/src/main/java/org/skycastle/server/hardcoded/SkycastleClientSessionListener.java (from rev 426, trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleClientSessionListener.java) =================================================================== --- trunk/skycastle/modules/server/src/main/java/org/skycastle/server/hardcoded/SkycastleClientSessionListener.java (rev 0) +++ trunk/skycastle/modules/server/src/main/java/org/skycastle/server/hardcoded/SkycastleClientSessionListener.java 2008-04-03 14:58:54 UTC (rev 432) @@ -0,0 +1,218 @@ +package org.skycastle.server.hardcoded; + +import com.sun.sgs.app.*; + +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Initializes a client session and listens to messages (commands) and session + * events from the client. + * <p/> + * TODO: + * <p/> + * See SkycastleServerListener. Like SkycastleServerListener, this is a + * general hub for dispatching network communication and should not + * specifically implement chat service features. + */ +public class SkycastleClientSessionListener + implements Serializable, ClientSessionListener +{ + + //====================================================================== + // Private Fields + + /** + * The session this {@code ClientSessionListener} is listening to. + */ + private final ClientSession session; + + //====================================================================== + // 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( SkycastleClientSessionListener.class.getName() ); + + /** + * The message encoding. + */ + public static final String MESSAGE_CHARSET = "UTF-8"; + + /** + * The server listener. + * This is used for querying connected users etc. + */ + protected ManagedReference serverListener; + + //====================================================================== + // Public Methods + + //---------------------------------------------------------------------- + // Constructors + + /** + * Creates a new {@code HelloChannelsSessionListener} for the given + * session, and joins it to the given channels. + * + * @param session the session this listener is associated with + */ + public SkycastleClientSessionListener( ClientSession session, + SkycastleServerListener initServerListener ) + { + this.session = session; + final DataManager dataManager = AppContext.getDataManager(); + serverListener = dataManager.createReference( initServerListener ); + + // Add client to general chat directly + final ChannelManager channelManager = AppContext.getChannelManager(); + final Channel generalChat = channelManager.getChannel( SkycastleServerListener.GENERAL_CHAT ); + generalChat.join( session, null ); + /* Let the connected users know that the user has joined the chat by + sending a JOIN message. */ + generalChat.send( encodeString( "JOIN " + + encodeHexString( session.getSessionId().getBytes() ) + " " + + session.getName() ) ); + /* Let the user know who else is there. */ + sendUserList(); + } + + //---------------------------------------------------------------------- + // ClientSessionListener Implementation + + + /** + * {@inheritDoc} + * <p/> + * Logs when data arrives from the client, and echoes the message back. + */ + public void receivedMessage( byte[] message ) + { + // DEBUG: + logger.log( Level.INFO, "Direct message from {0}", session.getName() ); + } + + /** + * {@inheritDoc} + * <p/> + * Logs when the client disconnects. + */ + public void disconnected( boolean graceful ) + { + final ChannelManager channelManager = AppContext.getChannelManager(); + final Channel generalChat = channelManager.getChannel( SkycastleServerListener.GENERAL_CHAT ); + final SkycastleServerListener sl = + serverListener.get( SkycastleServerListener.class ); + /* Let the connected users know that the user has left the chat by + sending a QUIT message. */ + generalChat.send( encodeString( "QUIT " + + encodeHexString( session.getSessionId().getBytes() ) + " " + + session.getName() ) ); + sl.removeUser( session.getSessionId() ); + // DEBUG: + final String grace = graceful ? "graceful" : "forced"; + logger.log( Level.INFO, + "User {0} has logged out {1}", + new Object[]{ session.getName(), grace } + ); + } + + /** + * Send the list of users as a sequence of join messages. + */ + protected void sendUserList() + { + final SkycastleServerListener sl = + serverListener.get( SkycastleServerListener.class ); + Set<Map.Entry<ClientSessionId, String>> entries = + sl.getConnectedUsers(); + for ( Map.Entry<ClientSessionId, String> e : entries ) + { + if ( !e.getKey().equals( session.getSessionId() ) ) + { + session.send( encodeString( "JOIN " + + encodeHexString( e.getKey().getBytes() ) + " " + + e.getValue() ) ); + } + } + } + + /** + * 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(); + } +} + Property changes on: trunk/skycastle/modules/server/src/main/java/org/skycastle/server/hardcoded/SkycastleClientSessionListener.java ___________________________________________________________________ Name: svn:mime-type + text/plain Name: svn:keywords + Id Name: svn:eol-style + native Copied: trunk/skycastle/modules/server/src/main/java/org/skycastle/server/hardcoded/SkycastleServerListener.java (from rev 426, trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleServerListener.java) =================================================================== --- trunk/skycastle/modules/server/src/main/java/org/skycastle/server/hardcoded/SkycastleServerListener.java (rev 0) +++ trunk/skycastle/modules/server/src/main/java/org/skycastle/server/hardcoded/SkycastleServerListener.java 2008-04-03 14:58:54 UTC (rev 432) @@ -0,0 +1,124 @@ +package org.skycastle.server.hardcoded; + +import com.sun.sgs.app.*; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + + +/** + * The Skycastle Server, running inside Darkstar. Recieves events when the server is started the first time, + * and when a client logs in. + * <p/> + * This also implements some features of a chat server right now, which means maintaining the list of users + * for the general chat. + * <p/> + * TODO: + * <p/> + * 1) Channels are probably not the right way to do implement a chat, since they are essentially a method for + * peer-to-peer communication, not message dispatching. It is not possible to enforce any state since clients + * are free to post arbitrary messages on the channel which are then received by all other clients. There is + * no possibility for the server to filter out bogus messages. + * <p/> + * 2) Chat functionality should be handled by a dedicated service, not the server listener, which is the point + * where new connections to the game in general are accepted. Chat is (if at all) just one of the services + * offered by the Skycastle server. + * <p/> + * 3) Implement a proper chat protocol, or even better, use idioms that will remain valid as development on + * other game features progresses. + */ +public class SkycastleServerListener + implements Serializable, AppListener +{ + + //====================================================================== + // Public Constants + + /** + * The name of the general chat channel: '{@value #GENERAL_CHAT}' + */ + public static final String GENERAL_CHAT = "General"; + + //====================================================================== + // 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( SkycastleServerListener.class.getName() ); + + /** + * Map that associates a session ID with a nickname. + */ + protected final Map<ClientSessionId, String> nicknamesById = + new HashMap<ClientSessionId, String>(); + + //====================================================================== + // Public Methods + + //---------------------------------------------------------------------- + // AppListener Implementation + + /** + * {@inheritDoc} + * <p/> + * Creates the general chat channel. Channels persist across server restarts, so they only need to be + * created here in {@code initialize}. + */ + public void initialize( Properties props ) + { + final ChannelManager channelManager = AppContext.getChannelManager(); + + channelManager.createChannel( GENERAL_CHAT, null, Delivery.RELIABLE ); + } + + + /** + * {@inheritDoc} + * <p/> + * Returns a {@link SkycastleClientSessionListener} for the logged-in session. + */ + public SkycastleClientSessionListener loggedIn( ClientSession session ) + { + // DEBUG: + logger.log( Level.INFO, "User {0} has logged in", session.getName() ); + nicknamesById.put( session.getSessionId(), session.getName() ); + + + return new SkycastleClientSessionListener( session, this ); + } + + /** + * Get a set of nicknames of currently connected users keyed by session ID. + * + * @return Set of nicknames. + */ + public Set<Map.Entry<ClientSessionId, String>> getConnectedUsers() + { + return nicknamesById.entrySet(); + } + + /** + * Remove the user with the specified session ID from the nickname list. + * + * @param sessionId session ID of the user to be removed + */ + public void removeUser( ClientSessionId sessionId ) + { + nicknamesById.remove( sessionId ); + } + +} + + Property changes on: trunk/skycastle/modules/server/src/main/java/org/skycastle/server/hardcoded/SkycastleServerListener.java ___________________________________________________________________ Name: svn:mime-type + text/plain Name: svn:keywords + Id Name: svn:eol-style + native This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.