Revision: 434 http://skycastle.svn.sourceforge.net/skycastle/?rev=434&view=rev Author: zzorn Date: 2008-04-03 14:04:36 -0700 (Thu, 03 Apr 2008) Log Message: ----------- The client connection handling part of the server starts to take form. A lot of open questions (and uncompiling code), but the basic message handling is sketched out. Modified Paths: -------------- trunk/skycastle/modules/core/src/main/java/org/skycastle/core/DefaultGameObject.java trunk/skycastle/modules/core/src/main/java/org/skycastle/messaging/MessageListener.java trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/negotiation/ClientSideProtocolNegotiator.java trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/negotiation/ProtocolNegotiator.java trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/negotiation/ServerSideProtocolNegotiator.java trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/registry/ProtocolRegistry.java trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/registry/ProtocolRegistryImpl.java trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleClientSessionHandler.java trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleServer.java Modified: trunk/skycastle/modules/core/src/main/java/org/skycastle/core/DefaultGameObject.java =================================================================== --- trunk/skycastle/modules/core/src/main/java/org/skycastle/core/DefaultGameObject.java 2008-04-03 18:15:38 UTC (rev 433) +++ trunk/skycastle/modules/core/src/main/java/org/skycastle/core/DefaultGameObject.java 2008-04-03 21:04:36 UTC (rev 434) @@ -48,6 +48,16 @@ // Public Methods //---------------------------------------------------------------------- + // Constructors + + public DefaultGameObject() + { + myPropertyFacade = new PropertyFacadeImpl( this, getGameObjectContext() ); + myActionFacade = new ActionFacadeImpl( this, getGameObjectContext() ); + myMessagingFacade = new MessagingFacadeImpl( this, getGameObjectContext() ); + } + + //---------------------------------------------------------------------- // ActionFacade Implementation public void startAction( final InvokeActionMessage invokeActionMessage ) @@ -62,7 +72,9 @@ } - public long startAction( final GameObjectId callerId, final String actionIdentifier, final Object... parameters ) + public long startAction( final GameObjectId callerId, + final String actionIdentifier, + final Object... parameters ) { return myActionFacade.startAction( callerId, actionIdentifier, parameters ); } @@ -200,7 +212,9 @@ } - public void addProperty( final String propertyIdentifier, final Serializable initialValue, final ParameterMetadata propertyMetadata ) + public void addProperty( final String propertyIdentifier, + final Serializable initialValue, + final ParameterMetadata propertyMetadata ) throws ParameterValidationException { myPropertyFacade.addProperty( propertyIdentifier, initialValue, propertyMetadata ); @@ -208,7 +222,9 @@ public void addProperty( final String propertyIdentifier, - final Serializable initialValue, final String description, final ParameterValidator... validators ) + final Serializable initialValue, + final String description, + final ParameterValidator... validators ) throws ParameterValidationException { myPropertyFacade.addProperty( propertyIdentifier, initialValue, description, validators ); @@ -275,17 +291,6 @@ //====================================================================== // Protected Methods - //---------------------------------------------------------------------- - // Protected Constructors - - protected DefaultGameObject() - { - myPropertyFacade = new PropertyFacadeImpl( this, getGameObjectContext() ); - myActionFacade = new ActionFacadeImpl( this, getGameObjectContext() ); - myMessagingFacade = new MessagingFacadeImpl( this, getGameObjectContext() ); - } - - protected final void setId( final GameObjectId id ) { ParameterChecker.checkNotNull( id, "id" ); Modified: trunk/skycastle/modules/core/src/main/java/org/skycastle/messaging/MessageListener.java =================================================================== --- trunk/skycastle/modules/core/src/main/java/org/skycastle/messaging/MessageListener.java 2008-04-03 18:15:38 UTC (rev 433) +++ trunk/skycastle/modules/core/src/main/java/org/skycastle/messaging/MessageListener.java 2008-04-03 21:04:36 UTC (rev 434) @@ -2,14 +2,14 @@ /** - * A listener that recieves {@link AbstractMessage}s. + * A listener that recieves {@link Message}s. * * @author Hans Häggström */ public interface MessageListener { /** - * @param message a new {@link AbstractMessage} to handle. + * @param message a new {@link Message} to handle. */ void onMessage( Message message ); } Modified: trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/negotiation/ClientSideProtocolNegotiator.java =================================================================== --- trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/negotiation/ClientSideProtocolNegotiator.java 2008-04-03 18:15:38 UTC (rev 433) +++ trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/negotiation/ClientSideProtocolNegotiator.java 2008-04-03 21:04:36 UTC (rev 434) @@ -24,9 +24,6 @@ private int myStep = 0; //====================================================================== - // Private Constants - - //====================================================================== // Public Methods //---------------------------------------------------------------------- @@ -37,7 +34,9 @@ * @param clientType the type / name of the client. * @param version the version of the client. */ - public ClientSideProtocolNegotiator( final ProtocolRegistry protocolRegistry, String clientType, String version ) + public ClientSideProtocolNegotiator( final ProtocolRegistry protocolRegistry, + String clientType, + String version ) { super( protocolRegistry ); myClientType = clientType; @@ -45,6 +44,14 @@ } //---------------------------------------------------------------------- + // ProtocolNegotiator Implementation + + public boolean startsNegotiations() + { + return false; + } + + //---------------------------------------------------------------------- // Other Public Methods /** Modified: trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/negotiation/ProtocolNegotiator.java =================================================================== --- trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/negotiation/ProtocolNegotiator.java 2008-04-03 18:15:38 UTC (rev 433) +++ trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/negotiation/ProtocolNegotiator.java 2008-04-03 21:04:36 UTC (rev 434) @@ -33,4 +33,9 @@ */ Protocol getProtocol(); + /** + * @return true if this party starts the protocol negotiation, false if it should wait for a message from + * the other party. + */ + boolean startsNegotiations(); } Modified: trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/negotiation/ServerSideProtocolNegotiator.java =================================================================== --- trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/negotiation/ServerSideProtocolNegotiator.java 2008-04-03 18:15:38 UTC (rev 433) +++ trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/negotiation/ServerSideProtocolNegotiator.java 2008-04-03 21:04:36 UTC (rev 434) @@ -51,6 +51,15 @@ } //---------------------------------------------------------------------- + // ProtocolNegotiator Implementation + + + public boolean startsNegotiations() + { + return true; + } + + //---------------------------------------------------------------------- // Other Public Methods /** @@ -95,7 +104,8 @@ myClientType = parameters.get( CLIENT_KEY ); myClientVersion = parameters.get( VERSION_KEY ); - final String selectedProtocol = findBestCommonSupportedProtocol( parameters.get( KNOWN_PROTOCOLS_KEY ) ); + final String selectedProtocol = findBestCommonSupportedProtocol( parameters.get( + KNOWN_PROTOCOLS_KEY ) ); setProtocol( selectedProtocol ); if ( selectedProtocol.length() > 0 ) { @@ -122,8 +132,9 @@ { if ( spaceSeparatedClientProtocols != null ) { - final Set<String> clientKnownProtocols = new HashSet<String>( Arrays.asList( spaceSeparatedClientProtocols.split( - " " ) ) ); + final Set<String> clientKnownProtocols = new HashSet<String>( Arrays.asList( + spaceSeparatedClientProtocols.split( + " " ) ) ); for ( String supportedProtocol : getAvailableProtocolsIds() ) { if ( clientKnownProtocols.contains( supportedProtocol ) ) Modified: trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/registry/ProtocolRegistry.java =================================================================== --- trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/registry/ProtocolRegistry.java 2008-04-03 18:15:38 UTC (rev 433) +++ trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/registry/ProtocolRegistry.java 2008-04-03 21:04:36 UTC (rev 434) @@ -1,7 +1,9 @@ package org.skycastle.protocol.registry; +import com.sun.sgs.app.ManagedObject; import org.skycastle.protocol.Protocol; +import java.io.Serializable; import java.util.Set; /** @@ -10,6 +12,7 @@ * @author Hans Haggstrom */ public interface ProtocolRegistry + extends ManagedObject, Serializable { /** * @return the id:s for the protocols that are currently available. @@ -17,7 +20,8 @@ Set<String> getAvailableProtocolIds(); /** - * @return the {@link org.skycastle.protocol.Protocol} with the specified protocol id, or null if not found. + * @return the {@link org.skycastle.protocol.Protocol} with the specified protocol id, or null if not + * found. */ Protocol getProtocol( String protocolId ); } Modified: trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/registry/ProtocolRegistryImpl.java =================================================================== --- trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/registry/ProtocolRegistryImpl.java 2008-04-03 18:15:38 UTC (rev 433) +++ trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/registry/ProtocolRegistryImpl.java 2008-04-03 21:04:36 UTC (rev 434) @@ -25,14 +25,19 @@ private final Map<String, Protocol> myProtocols = new LinkedHashMap<String, Protocol>(); //====================================================================== + // Private Constants + + private static final long serialVersionUID = 1L; + + //====================================================================== // Public Methods //---------------------------------------------------------------------- // Constructors /** - * Creates a new {@link ProtocolRegistryImpl} with the known protocols alreay registered, using a default set of - * allowed data types for transfer. + * Creates a new {@link ProtocolRegistryImpl} with the known protocols alreay registered, using a default + * set of allowed data types for transfer. */ public ProtocolRegistryImpl() { @@ -75,8 +80,8 @@ /** - * Removes all registered protocols. Can be used to remove the default protocols, if they are not desired (e.g. in - * unit tests). + * Removes all registered protocols. Can be used to remove the default protocols, if they are not desired + * (e.g. in unit tests). */ public void removeAllProtocols() { Modified: trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleClientSessionHandler.java =================================================================== --- trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleClientSessionHandler.java 2008-04-03 18:15:38 UTC (rev 433) +++ trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleClientSessionHandler.java 2008-04-03 21:04:36 UTC (rev 434) @@ -1,15 +1,20 @@ package org.skycastle.server; import com.sun.sgs.app.*; -import org.skycastle.server.hardcoded.SkycastleServerListener; +import org.skycastle.core.DefaultGameObject; +import org.skycastle.core.GameObject; +import org.skycastle.messaging.Message; +import org.skycastle.messaging.modifications.ModificationMessage; +import org.skycastle.protocol.ProtocolException; +import org.skycastle.protocol.negotiation.ProtocolNegotiator; +import org.skycastle.protocol.negotiation.ServerSideProtocolNegotiator; 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. + * Initializes a client session and listens to messages (commands) and session events from the client. */ public class SkycastleClientSessionHandler implements Serializable, ClientSessionListener @@ -22,8 +27,20 @@ * The session this {@code ClientSessionListener} is listening to. */ private final ClientSession mySession; + private final ProtocolNegotiator myProtocolNegotiator; + private ManagedReference myProtocolRegistryReferenceReference; + private ManagedReference myClientAccountReference; + //====================================================================== + // Non-Private Fields + + /** + * The server listener. This is used for querying connected users etc. + */ + protected ManagedReference serverListener; + + //====================================================================== // Private Constants /** @@ -36,14 +53,9 @@ */ private static final Logger LOGGER = Logger.getLogger( SkycastleClientSessionHandler.class.getName() ); + private static final String SERVER_TYPE = "SkycastleServer"; + private static final String SERVER_VERSION = "0.1"; - - /** - * The server listener. - * This is used for querying connected users etc. - */ - protected ManagedReference serverListener; - //====================================================================== // Public Methods @@ -51,35 +63,104 @@ // Constructors /** - * Creates a new {@code HelloChannelsSessionListener} for the given - * session, and joins it to the given channels. + * 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 + * @param clientSession the session this listener is associated with + * @param protocolRegistryReferenceReference + * */ - public SkycastleClientSessionHandler( ClientSession session, - SkycastleServer initServerListener ) + // TODO: Implement our own generic version of ManagedReference that just wraps a ManagedReference + public SkycastleClientSessionHandler( ClientSession clientSession, + SkycastleServer initServerListener, + final ManagedReference protocolRegistryReferenceReference ) { - mySession = session; + mySession = clientSession; + myProtocolRegistryReferenceReference = protocolRegistryReferenceReference; final DataManager dataManager = AppContext.getDataManager(); serverListener = dataManager.createReference( initServerListener ); + + // TODO: We need to pass in a reference instead of a concrete class, because we store the protocol negotiator in this object.... + myProtocolNegotiator = new ServerSideProtocolNegotiator( myProtocolRegistryReferenceReference, + SERVER_TYPE, + SERVER_VERSION ); + + + if ( myProtocolNegotiator.startsNegotiations() ) + { + final byte[] firstMessage = myProtocolNegotiator.handleMessage( null ); + clientSession.send( firstMessage ); + } + + // Get users account. The account will typically contain actions for resuming some existing avatars + // or creating new avatars, and doing general account management and maybe out-of-game chat. + // TODO: If the ClientSession is already authenticated, we can find the current account for the client, + // otherwise we need to add authentication before or during or after protocol negotiation + GameObject clientAccount = dataManager.getBinding( "account_" + clientSession.getName(), + GameObject.class ); + + // If client doesn't yet have any account, create a new one + if ( clientAccount == null ) + { + clientAccount = new DefaultGameObject(); + } + + myClientAccountReference = dataManager.createReference( clientAccount ); + + // TODO: Notify the users account that the user connected } //---------------------------------------------------------------------- // ClientSessionListener Implementation - /** * {@inheritDoc} * <p/> * Logs when data arrives from the client, and echoes the message back. */ - public void receivedMessage( byte[] message ) + public void receivedMessage( final byte[] message ) { - // DEBUG: - LOGGER.log( Level.INFO, "Direct message from {0}", mySession.getName() ); + if ( myProtocolNegotiator.getStatus().isFinished() ) + { + try + { + // Decode incoming message + final Message decodedMessage = myProtocolNegotiator.getProtocol().decode( message ); + + // TODO: Set the messages sender and such so that the client is not claiming to be someone else... + + // TODO: Should we validate the message here also? + //decodedMessage.validate( ) + + // Send the incoming message to the clients account for processing + final GameObject clientAvatar = myClientAccountReference.get( GameObject.class ); + clientAvatar.onMessage( decodedMessage ); + + // TODO: Or should we send it directly to the target game object?? + // If the message is a ModificationMessage it has a target. + // The client should anyway send only modification messages, update messages don't make that much sense. + ModificationMessage modificationMessage = (ModificationMessage) decodedMessage; + // TODO: Get game object for specific gameObjectId, using e.g. the GameContext - we could build this functionality into the GameObjectId now! Maybe even support generics? + GameObject target = modificationMessage.getTargetId().getGameObjectForModification(); + target.onMessage( modificationMessage ); + } + catch ( ProtocolException e ) + { + // TODO: Log exception with client somehow? Enable fast detection of errorneous or flooding DoS:in clients + e.printStackTrace(); + } + } + else + { + // Continue protocol negotiation + final byte[] reply = myProtocolNegotiator.handleMessage( message ); + + mySession.send( reply ); + } } + /** * {@inheritDoc} * <p/> @@ -87,6 +168,8 @@ */ public void disconnected( boolean graceful ) { + // TODO: Notify the users account that the user disconnected (the avatars can go into off-line mode, etc). + // DEBUG: final String grace = graceful ? "graceful" : "forced"; LOGGER.log( Level.INFO, @@ -95,5 +178,4 @@ ); } - } \ No newline at end of file Modified: trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleServer.java =================================================================== --- trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleServer.java 2008-04-03 18:15:38 UTC (rev 433) +++ trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleServer.java 2008-04-03 21:04:36 UTC (rev 434) @@ -1,19 +1,13 @@ package org.skycastle.server; -import com.sun.sgs.app.AppListener; -import com.sun.sgs.app.ClientSession; -import com.sun.sgs.app.ClientSessionListener; -import com.sun.sgs.app.AppContext; +import com.sun.sgs.app.*; +import org.skycastle.protocol.registry.ProtocolRegistryImpl; import java.io.Serializable; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; -import org.skycastle.protocol.negotiation.ServerSideProtocolNegotiator; -import org.skycastle.protocol.registry.ProtocolRegistry; -import org.skycastle.protocol.registry.ProtocolRegistryImpl; - /** * The Skycastle Server, running inside Darkstar. Recieves events when the server is started the first time, * and when a client logs in. @@ -32,9 +26,8 @@ */ private static final Logger LOGGER = Logger.getLogger( SkycastleServer.class.getName() ); + private ManagedReference myProtocolRegistryReferenceReference; - - //====================================================================== // Public Methods @@ -44,10 +37,7 @@ public void initialize( final Properties properties ) { // Create protocol registry - ProtocolRegistry protocolRegistry = new ProtocolRegistryImpl(); - AppContext.getDataManager().createReference( protocolRegistry ); - - + myProtocolRegistryReferenceReference = AppContext.getDataManager().createReference( new ProtocolRegistryImpl() ); } @@ -56,8 +46,7 @@ // DEBUG: LOGGER.log( Level.INFO, "User {0} has logged in", clientSession.getName() ); - - return new SkycastleClientSessionHandler( clientSession, this ); + return new SkycastleClientSessionHandler( clientSession, this, myProtocolRegistryReferenceReference ); } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.