/** * Creates a Client that communicates with the specified host and and separate TCP and UDP ports * using both reliable and fast transports. * * @param gameName This is the name that identifies the game. This must match * the target server's name or this client will be turned away. * @param version This is a game-specific verison that helps detect when out-of-date * clients have connected to an incompatible server. This must match * the server's version of this client will be turned away. * @param hostPort The remote TCP port on the server to which this client should * send reliable messages. * @param remoteUdpPort The remote UDP port on the server to which this client should * send 'fast'/unreliable messages. Set to -1 if 'fast' traffic should * go over TCP. This will completely disable UDP traffic for this * client. */ public static Client connectToServer( String gameName, int version, String host, int hostPort, int remoteUdpPort ) throws IOException { InetAddress remoteAddress = InetAddress.getByName(host); UdpConnector fast = remoteUdpPort == -1 ? null : new UdpConnector( remoteAddress, remoteUdpPort ); SocketConnector reliable = new SocketConnector( remoteAddress, hostPort ); return new DefaultClient( gameName, version, reliable, fast, new TcpConnectorFactory(remoteAddress) ); }
@Override public void close() { checkRunning(); closeConnections( null ); }
@Override public void messageReceived( Object source, Message m ) { dispatch( m ); }
protected void send( int channel, Message message, boolean waitForConnected ) { checkRunning(); if( waitForConnected ) { // Make sure we aren't still connecting waitForConnected(); } ByteBuffer buffer = dataBuffer.get(); if( buffer == null ) { buffer = ByteBuffer.allocate( 65536 + 2 ); dataBuffer.set(buffer); } buffer.clear(); // Convert the message to bytes buffer = MessageProtocol.messageToBuffer(message, buffer); // Since we share the buffer between invocations, we will need to // copy this message's part out of it. This is because we actually // do the send on a background thread. byte[] temp = new byte[buffer.remaining()]; System.arraycopy(buffer.array(), buffer.position(), temp, 0, buffer.remaining()); buffer = ByteBuffer.wrap(temp); channels.get(channel).write(buffer); }
startServices(); fireConnected(); configureChannels( ((ChannelInfoMessage)m).getId(), ((ChannelInfoMessage)m).getPorts() ); return; } else if( m instanceof DisconnectMessage ) { DisconnectInfo info = new DisconnectInfo(); info.reason = reason; closeConnections(info);
reg = new ClientRegistrationMessage(); reg.setId(tempId); reg.setGameName(getGameName()); reg.setVersion(getVersion()); reg.setReliable(true); send(CH_RELIABLE, reg, false); if( channels.get(ch) == null ) continue; send(ch, reg, false);
protected void dispatch( Message m ) { // Pull off the connection management messages we're // interested in and then pass on the rest. if( m instanceof ClientRegistrationMessage ) { // Then we've gotten our real id this.id = (int)((ClientRegistrationMessage)m).getId(); log.log( Level.INFO, "Connection established, id:{0}.", this.id ); connecting.countDown(); fireConnected(); return; } if( m instanceof DisconnectMessage ) { // Can't do too much else yet String reason = ((DisconnectMessage)m).getReason(); log.log( Level.SEVERE, "Connection terminated, reason:{0}.", reason ); DisconnectInfo info = new DisconnectInfo(); info.reason = reason; closeConnections(info); } // Make sure client MessageListeners are called single-threaded // since it could receive messages from the TCP and UDP // thread simultaneously. synchronized( this ) { messageListeners.messageReceived( this, m ); } }
@Override public void handleError( Object source, Throwable t ) { // Only doing the DefaultClient.this to make the code // checker happy... it compiles fine without it but I // don't like red lines in my editor. :P DefaultClient.this.handleError( t ); } }
protected void waitForConnected() { if( isConnected() ) return; try { connecting.await(); } catch( InterruptedException e ) { throw new RuntimeException( "Interrupted waiting for connect", e ); } }
/** * Either calls the ErrorListener or closes the connection * if there are no listeners. */ protected void handleError( Throwable t ) { // If there are no listeners then close the connection with // a reason if( errorListeners.isEmpty() ) { log.log( Level.SEVERE, "Termining connection due to unhandled error", t ); DisconnectInfo info = new DisconnectInfo(); info.reason = "Connection Error"; info.error = t; closeConnections(info); return; } for( ErrorListener l : errorListeners ) { l.handleError( this, t ); } }
reg = new ClientRegistrationMessage(); reg.setId(tempId); reg.setGameName(getGameName()); reg.setVersion(getVersion()); reg.setReliable(true); send(CH_RELIABLE, reg, false); if( channels.get(ch) == null ) continue; send(ch, reg, false);
startServices(); fireConnected(); configureChannels( ((ChannelInfoMessage)m).getId(), ((ChannelInfoMessage)m).getPorts() ); return; } else if( m instanceof DisconnectMessage ) { DisconnectInfo info = new DisconnectInfo(); info.reason = reason; closeConnections(info);
protected void send( int channel, Message message, boolean waitForConnected ) { checkRunning(); if( waitForConnected ) { // Make sure we aren't still connecting waitForConnected(); } ByteBuffer buffer = dataBuffer.get(); if( buffer == null ) { buffer = ByteBuffer.allocate( 65536 + 2 ); dataBuffer.set(buffer); } buffer.clear(); // Convert the message to bytes buffer = MessageProtocol.messageToBuffer(message, buffer); // Since we share the buffer between invocations, we will need to // copy this message's part out of it. This is because we actually // do the send on a background thread. byte[] temp = new byte[buffer.remaining()]; System.arraycopy(buffer.array(), buffer.position(), temp, 0, buffer.remaining()); buffer = ByteBuffer.wrap(temp); channels.get(channel).write(buffer); }
public void handleError( Object source, Throwable t ) { // Only doing the DefaultClient.this to make the code // checker happy... it compiles fine without it but I // don't like red lines in my editor. :P DefaultClient.this.handleError( t ); } }
protected void waitForConnected() { if( isConnected() ) return; try { connecting.await(); } catch( InterruptedException e ) { throw new RuntimeException( "Interrupted waiting for connect", e ); } }
/** * Either calls the ErrorListener or closes the connection * if there are no listeners. */ protected void handleError( Throwable t ) { // If there are no listeners then close the connection with // a reason if( errorListeners.isEmpty() ) { log.log( Level.SEVERE, "Termining connection due to unhandled error", t ); DisconnectInfo info = new DisconnectInfo(); info.reason = "Connection Error"; info.error = t; closeConnections(info); return; } for( ErrorListener l : errorListeners ) { l.handleError( this, t ); } }
protected void closeConnections( DisconnectInfo info ) { if( !isRunning ) return; // Send a close message // Tell the thread it's ok to die if( fastAdapter != null ) { fastAdapter.close(); } if( reliableAdapter != null ) { reliableAdapter.close(); } // Wait for the threads? // Just in case we never fully connected connecting.countDown(); fireDisconnected(info); isRunning = false; }
reg = new ClientRegistrationMessage(); reg.setId(tempId); reg.setGameName(getGameName()); reg.setVersion(getVersion()); reg.setReliable(true); send(reg, false); reg.setId(tempId); reg.setReliable(false); send(reg, false);
@Override public void close() { checkRunning(); closeConnections( null ); }