Revision: 444 http://skycastle.svn.sourceforge.net/skycastle/?rev=444&view=rev Author: zzorn Date: 2008-04-05 15:09:23 -0700 (Sat, 05 Apr 2008) Log Message: ----------- Implemented protocol negotiation to the client too. Now the only major missing thing on the client side is the UI. Modified Paths: -------------- trunk/skycastle/modules/client/src/main/java/org/skycastle/client/SkycastleClient.java trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/ProtocolCommunicator.java trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleClientSessionHandler.java Modified: trunk/skycastle/modules/client/src/main/java/org/skycastle/client/SkycastleClient.java =================================================================== --- trunk/skycastle/modules/client/src/main/java/org/skycastle/client/SkycastleClient.java 2008-04-05 21:40:12 UTC (rev 443) +++ trunk/skycastle/modules/client/src/main/java/org/skycastle/client/SkycastleClient.java 2008-04-05 22:09:23 UTC (rev 444) @@ -6,15 +6,19 @@ import com.sun.sgs.client.simple.SimpleClientListener; import org.skycastle.core.GameContext; import org.skycastle.messaging.Message; +import org.skycastle.messaging.modifications.ModificationMessage; import org.skycastle.messaging.updates.UpdateMessage; +import org.skycastle.protocol.ProtocolCommunicator; import org.skycastle.protocol.ProtocolException; import org.skycastle.protocol.negotiation.ClientSideProtocolNegotiator; -import org.skycastle.protocol.negotiation.ProtocolNegotiator; +import org.skycastle.protocol.negotiation.NegotiationStatus; import org.skycastle.protocol.registry.ProtocolRegistryImpl; +import javax.swing.*; import java.io.IOException; import java.net.PasswordAuthentication; import java.util.Properties; +import java.util.logging.Level; import java.util.logging.Logger; /** @@ -43,20 +47,26 @@ // --> 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() +// So client could do something like getAccountObject().getUiRoot().createJComponent() + +// Supress warning about missing serialVersionUID, +// as this implementation of ProtocolCommunicator is not intended to be serizliaed. +@SuppressWarnings( { "serial" } ) public class SkycastleClient + extends ProtocolCommunicator implements SimpleClientListener { //====================================================================== // Private Fields - private final SimpleClient myDarkstarClient; - private final ProtocolNegotiator myProtocolNegotiator; + @SuppressWarnings( { "NonSerializableFieldInSerializableClass" } ) + private final SimpleClient mySimpleClient; //====================================================================== // Private Constants + private static final int PROTOCOL_NEGOTIATION_TIMEOUT_MS = 10000; /** * The name of the host property. */ @@ -80,10 +90,9 @@ private static final String TEST_USER_NAME = "TestUser"; /** - * The {@link java.util.logging.Logger} for this class. + * The {@link Logger} for this class. */ - private static final Logger LOGGER = - Logger.getLogger( SkycastleClient.class.getName() ); + 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(); @@ -99,14 +108,15 @@ */ public SkycastleClient() { - myDarkstarClient = new SimpleClient( this ); + super( new ClientSideProtocolNegotiator( PROTOCOL_REGISTRY, + CLIENT_TYPE, + CLIENT_VERSION ), + PROTOCOL_NEGOTIATION_TIMEOUT_MS ); + mySimpleClient = new SimpleClient( this ); + // DEBUG: For now, just try directly to log in. login(); - - myProtocolNegotiator = new ClientSideProtocolNegotiator( PROTOCOL_REGISTRY, - CLIENT_TYPE, - CLIENT_VERSION ); } //---------------------------------------------------------------------- @@ -114,7 +124,7 @@ public ClientChannelListener joinedChannel( final ClientChannel clientChannel ) { - LOGGER.fine( "Joined channel '" + clientChannel + "'." ); + LOGGER.info( "Joined channel '" + clientChannel + "'." ); return null; } @@ -122,59 +132,32 @@ public void receivedMessage( final byte[] serverMessage ) { - if ( myProtocolNegotiator.getStatus().isFinished() ) + try { - 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( GameContext.getGameObjectContext() ); - } - catch ( ProtocolException e ) - { - // TODO: Notify user, reset server connection? - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. - } + handleReceivedEncodedMessage( serverMessage ); } - else + catch ( ProtocolException e ) { - 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. - } + LOGGER.warning( "Problem when decoding incoming message from server: " + e.getMessage() ); } } public void reconnecting() { - LOGGER.fine( "Reconnecting" ); + LOGGER.info( "Reconnecting" ); } public void reconnected() { - LOGGER.fine( "Reconnected" ); + LOGGER.info( "Reconnected" ); } public void disconnected( final boolean b, final String s ) { - LOGGER.fine( "Disconnected. Graceful: '" + b + "'. Message: '" + s + "'." ); + LOGGER.info( "Disconnected. Graceful: '" + b + "'. Message: '" + s + "'." ); } //---------------------------------------------------------------------- @@ -191,28 +174,105 @@ public void loggedIn() { - LOGGER.fine( "Logged In" ); + LOGGER.info( "Logged In. Now negotiationg protocol..." ); - if ( myProtocolNegotiator.startsNegotiations() ) + startProtocolNegotiations(); + } + + + public void loginFailed( final String s ) + { + LOGGER.info( "Login Failed. Reason: '" + s + "'." ); + } + + //====================================================================== + // Protected Methods + + @Override + protected void scheduleTimeoutCallback( final long timeout_ms ) + { + final Thread timeoutTimer = new Thread( new Runnable() { - try + + public void run() { - myDarkstarClient.send( myProtocolNegotiator.handleMessage( null ) ); + try + { + Thread.sleep( timeout_ms ); + } + catch ( InterruptedException e ) + { + // We were interrupted for some reason. Stop waiting, and call the timeout. + } + + // TODO: Assumes that the main game loop will run in the Swing event thread. Check this. + SwingUtilities.invokeLater( new Runnable() + { + + public void run() + { + onTimeoutCallback(); + } + + } ); } - catch ( IOException e ) - { - // TODO: Notify user about failure, reset server connection? - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. - } + + } ); + + timeoutTimer.setDaemon( true ); // If the program exists, don't stay to wait for the timeout. + + timeoutTimer.start(); + } + + + @Override + protected void sendEncodedMessage( final byte[] encodedMessage ) + { + try + { + mySimpleClient.send( encodedMessage ); } + catch ( IOException e ) + { + LOGGER.log( Level.WARNING, "Problem when sending an encoded message to the server.", e ); + } } - public void loginFailed( final String s ) + @Override + protected void onProtocolNegotiationFailed( final NegotiationStatus status ) { - LOGGER.fine( "Login Failed. Reason: '" + s + "'." ); + LOGGER.severe( "Could not negotiate a common protocol with the server: " + status.toString() ); } + + @Override + protected void onProtocolNegotiationSucceeded( final String protocolId ) + { + LOGGER.info( "Protocol negotiated." ); + } + + + @Override + protected void onMessage( final Message message ) throws ProtocolException + { + if ( message instanceof UpdateMessage ) + { + final UpdateMessage updateMessage = (UpdateMessage) message; + + updateMessage.applyStateChangeToModel( GameContext.getGameObjectContext() ); + } + else if ( message instanceof ModificationMessage ) + { + throw new ProtocolException( + "The client does not accept any ModificationMessages, only UpdateMessages, but the incoming message was: " + message ); + } + else + { + throw new ProtocolException( "Unknown message type: " + message.getClass() ); + } + } + //====================================================================== // Private Methods @@ -220,6 +280,7 @@ * 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 + @SuppressWarnings( { "AccessOfSystemProperties" } ) private void login() { final String host = System.getProperty( HOST_PROPERTY, DEFAULT_HOST ); @@ -230,12 +291,12 @@ final Properties connectProps = new Properties(); connectProps.setProperty( "host", host ); connectProps.setProperty( "port", port ); - myDarkstarClient.login( connectProps ); + mySimpleClient.login( connectProps ); } catch ( Exception e ) { - // TODO: Report error to user - e.printStackTrace(); + LOGGER.log( Level.SEVERE, "Problem when connecting to the server.", e ); + disconnected( false, e.getMessage() ); } } Modified: trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/ProtocolCommunicator.java =================================================================== --- trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/ProtocolCommunicator.java 2008-04-05 21:40:12 UTC (rev 443) +++ trunk/skycastle/modules/core/src/main/java/org/skycastle/protocol/ProtocolCommunicator.java 2008-04-05 22:09:23 UTC (rev 444) @@ -70,7 +70,7 @@ * Starts the negotiation timeout, and sends opening message to the other party (depending on the {@link * ProtocolNegotiator} type). */ - public final void startNegotiations() + public final void startProtocolNegotiations() { if ( myNegotiationsStarted ) { @@ -97,7 +97,7 @@ * Should be called after the time specified amount of time after scheduleTimeoutCallback is called. Used * to check if the protocol negotiation timed out. */ - public final void timeoutCallback() + public final void onTimeoutCallback() { if ( !myProtocolNegotiator.getStatus().isFinished() ) { 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-05 21:40:12 UTC (rev 443) +++ trunk/skycastle/modules/server/src/main/java/org/skycastle/server/SkycastleClientSessionHandler.java 2008-04-05 22:09:23 UTC (rev 444) @@ -80,7 +80,7 @@ myClientSession = clientSession; - startNegotiations(); + startProtocolNegotiations(); } //---------------------------------------------------------------------- @@ -127,7 +127,7 @@ public void run() throws Exception { - timeoutCallback(); + onTimeoutCallback(); } //====================================================================== This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.