OSDN Git Service

Still a work in progress. The new API and base implementations
authorPSpeed42@gmail.com <PSpeed42@gmail.com@75d07b2b-3a1a-0410-a2c5-0572b91ccdca>
Thu, 17 Mar 2011 17:57:07 +0000 (17:57 +0000)
committerPSpeed42@gmail.com <PSpeed42@gmail.com@75d07b2b-3a1a-0410-a2c5-0572b91ccdca>
Thu, 17 Mar 2011 17:57:07 +0000 (17:57 +0000)
of the new interfaces.  Still exists entirely in parallel with
the old code.

git-svn-id: http://jmonkeyengine.googlecode.com/svn/trunk@7010 75d07b2b-3a1a-0410-a2c5-0572b91ccdca

15 files changed:
engine/src/networking/com/jme3/network/Client.java [new file with mode: 0644]
engine/src/networking/com/jme3/network/ClientStateListener.java [new file with mode: 0644]
engine/src/networking/com/jme3/network/ConnectionListener.java [new file with mode: 0644]
engine/src/networking/com/jme3/network/HostedConnection.java [new file with mode: 0644]
engine/src/networking/com/jme3/network/Message.java [new file with mode: 0644]
engine/src/networking/com/jme3/network/MessageConnection.java [new file with mode: 0644]
engine/src/networking/com/jme3/network/MessageListener.java [new file with mode: 0644]
engine/src/networking/com/jme3/network/Network.java [new file with mode: 0644]
engine/src/networking/com/jme3/network/Server.java [new file with mode: 0644]
engine/src/networking/com/jme3/network/base/ConnectorAdapter.java [new file with mode: 0644]
engine/src/networking/com/jme3/network/base/DefaultClient.java [new file with mode: 0644]
engine/src/networking/com/jme3/network/base/DefaultServer.java [new file with mode: 0644]
engine/src/networking/com/jme3/network/base/KernelAdapter.java [new file with mode: 0644]
engine/src/networking/com/jme3/network/base/MessageListenerRegistry.java [new file with mode: 0644]
engine/src/networking/com/jme3/network/message/Message.java

diff --git a/engine/src/networking/com/jme3/network/Client.java b/engine/src/networking/com/jme3/network/Client.java
new file mode 100644 (file)
index 0000000..7e362b4
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+
+/**
+ *  Represents a remote connection to a server that can be used
+ *  for sending and receiving messages.
+ *
+ *  @version   $Revision$
+ *  @author    Paul Speed
+ */
+public interface Client extends MessageConnection
+{
+    /**
+     *  Starts the client allowing it to begin processing incoming
+     *  messages and delivering them to listeners.
+     */
+    public void start();
+
+    /**
+     *  Returns true if this client is fully connected to the
+     *  host.
+     */
+    public boolean isConnected();     
+
+    /**
+     *  Returns a unique ID for this client within the remote
+     *  server or -1 if this client isn't fully connected to the
+     *  server.
+     */
+    public long getId();     
+    /**
+     *  Sends a message to the server.
+     */   
+    public void send( Message message );
+    /**
+     *  Closes this connection to the server.
+     */
+    public void close();         
+
+    /**
+     *  Adds a listener that will be notified about connection
+     *  state changes.
+     */
+    public void addClientStateListener( ClientStateListener listener ); 
+
+    /**
+     *  Removes a previously registered connection listener.
+     */
+    public void removeClientStateListener( ClientStateListener listener ); 
+
+    /**
+     *  Adds a listener that will be notified when any message or object
+     *  is received from the server.
+     */
+    public void addMessageListener( MessageListener<? super Client> listener ); 
+
+    /**
+     *  Adds a listener that will be notified when messages of the specified
+     *  types are received.
+     */
+    public void addMessageListener( MessageListener<? super Client> listener, Class... classes ); 
+
+    /**
+     *  Removes a previously registered wildcard listener.  This does
+     *  not remove this listener from any type-specific registrations.
+     */
+    public void removeMessageListener( MessageListener<? super Client> listener ); 
+
+    /**
+     *  Removes a previously registered type-specific listener from
+     *  the specified types.
+     */
+    public void removeMessageListener( MessageListener<? super Client> listener, Class... classes ); 
+    
+}
+
+
diff --git a/engine/src/networking/com/jme3/network/ClientStateListener.java b/engine/src/networking/com/jme3/network/ClientStateListener.java
new file mode 100644 (file)
index 0000000..271fdfb
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+
+/**
+ *  Listener that is notified about the connection state of
+ *  a Client.
+ *
+ *  @version   $Revision$
+ *  @author    Paul Speed
+ */
+public interface ClientStateListener
+{
+    /**
+     *  Called when the specified client is fully connected to
+     *  the remote server.
+     */
+    public void clientConnected( Client c );
+    /**
+     *  Called when the client has disconnected from the remote
+     *  server.
+     */   
+    public void clientDisconnected( Client c );
+}
diff --git a/engine/src/networking/com/jme3/network/ConnectionListener.java b/engine/src/networking/com/jme3/network/ConnectionListener.java
new file mode 100644 (file)
index 0000000..b7d91ff
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+
+/**
+ *  Listener that is notified about connection arrivals and
+ *  removals within a server.
+ *
+ *  @version   $Revision$
+ *  @author    Paul Speed
+ */
+public interface ConnectionListener
+{
+    /**
+     *  Called when a connection has been added to the specified server and
+     *  is fully setup.
+     */
+    public void connectionAdded( Server server, HostedConnection conn );
+    
+    /**
+     *  Called when a connection has been removed from the specified
+     *  server. 
+     */
+    public void connectionRemoved( Server server, HostedConnection conn );
+}
diff --git a/engine/src/networking/com/jme3/network/HostedConnection.java b/engine/src/networking/com/jme3/network/HostedConnection.java
new file mode 100644 (file)
index 0000000..e0daec4
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+
+/**
+ *  This is the connection back to a client that is being
+ *  hosted in a server instance.  
+ *
+ *  @version   $Revision$
+ *  @author    Paul Speed
+ */
+public interface HostedConnection extends MessageConnection
+{
+    /**
+     *  Returns the server-unique ID for this client.
+     */
+    public long getId();
+    
+    /**
+     *  Closes and removes this connection from the server
+     *  sending the optional reason to the remote client.
+     */
+    public void close( String reason );     
+}
diff --git a/engine/src/networking/com/jme3/network/Message.java b/engine/src/networking/com/jme3/network/Message.java
new file mode 100644 (file)
index 0000000..9f8e4d0
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+import com.jme3.network.serializing.Serializable;
+
+/**
+ *  Interface implemented by all network messages.
+ *
+ *  @version   $Revision$
+ *  @author    Paul Speed
+ */
+@Serializable()
+public interface Message
+{
+    public boolean isReliable();
+}
diff --git a/engine/src/networking/com/jme3/network/MessageConnection.java b/engine/src/networking/com/jme3/network/MessageConnection.java
new file mode 100644 (file)
index 0000000..712d821
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+
+/**
+ *  The source of a received message and the common abstract interface
+ *  of client->server and server->client objects. 
+ *
+ *  @version   $Revision$
+ *  @author    Paul Speed
+ */
+public interface MessageConnection
+{
+    /**
+     *  Sends a message to the other end of the connection.
+     */   
+    public void send( Message message );
+}    
+
diff --git a/engine/src/networking/com/jme3/network/MessageListener.java b/engine/src/networking/com/jme3/network/MessageListener.java
new file mode 100644 (file)
index 0000000..4d3f26a
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+
+/**
+ *  Listener notified about new messages 
+ *
+ *  @version   $Revision$
+ *  @author    Paul Speed
+ */
+public interface MessageListener<S>
+{
+    public void messageReceived( S source, Message m );
+}
diff --git a/engine/src/networking/com/jme3/network/Network.java b/engine/src/networking/com/jme3/network/Network.java
new file mode 100644 (file)
index 0000000..7e9dc02
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+import com.jme3.network.base.DefaultClient;
+import com.jme3.network.base.DefaultServer;
+import com.jme3.network.kernel.tcp.SelectorKernel;
+import com.jme3.network.kernel.tcp.SocketConnector;
+import com.jme3.network.kernel.udp.UdpConnector;
+import com.jme3.network.kernel.udp.UdpKernel;
+
+/**
+ *  The main service provider for conveniently creating
+ *  server and client instances.
+ *
+ *  @version   $Revision$
+ *  @author    Paul Speed
+ */
+public class Network
+{
+    /**
+     *  Creates a Server that will utilize both reliable and fast
+     *  transports to communicate with clients.  The specified port
+     *  will be used for both TCP and UDP communication.
+     */
+    public static Server createServer( int port ) throws IOException
+    {
+        InetAddress local = InetAddress.getLocalHost();
+        
+        UdpKernel fast = new UdpKernel(local, port);
+        SelectorKernel reliable = new SelectorKernel(local,port);
+        return new DefaultServer( reliable, fast );       
+    }
+    
+    /**
+     *  Creates a Client that communicates with the specified host and port
+     *  using both reliable and fast transports.  The localUdpPort specifies the
+     *  local port to use for listening for incoming 'fast' UDP messages.
+     */   
+    public static Client connectToServer( String host, int hostPort, int localUdpPort ) throws IOException
+    {
+        return connectToServer( InetAddress.getByName(host), hostPort, localUdpPort );   
+    }
+    /**
+     *  Creates a Client that communicates with the specified address and port
+     *  using both reliable and fast transports.  The localUdpPort specifies the
+     *  local port to use for listening for incoming 'fast' messages.
+     */   
+    public static Client connectToServer( InetAddress address, int port, int localUdpPort ) throws IOException
+    {
+        InetAddress local = InetAddress.getLocalHost();
+        UdpConnector fast = new UdpConnector( local, localUdpPort, address, port ); 
+        SocketConnector reliable = new SocketConnector( address, port );        
+       
+        return new DefaultClient( reliable, fast );
+    }
+}
diff --git a/engine/src/networking/com/jme3/network/Server.java b/engine/src/networking/com/jme3/network/Server.java
new file mode 100644 (file)
index 0000000..9f36e9a
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network;
+
+import java.util.Collection;
+
+/**
+ *  Represents a host that can send and receive messages to
+ *  a set of remote client connections.
+ *  
+ *  @version   $Revision$
+ *  @author    Paul Speed
+ */
+public interface Server
+{
+    /**
+     *  Sends the specified message to all connected clients.
+     */ 
+    public void broadcast( Message message );
+
+    /**
+     *  Sends the specified message to all connected clients that match
+     *  the filter.
+     */ 
+    public void broadcast( Object filter, Message message );
+
+    /**
+     *  Start the server so that it will began accepting new connections
+     *  and processing messages.
+     */
+    public void start();
+
+    /**
+     *  Returns true if the server has been started.
+     */
+    public boolean isRunning();     
+    /**
+     *  Closes all client connections, stops and running processing threads, and
+     *  closes the host connection.
+     */   
+    public void close();
+    /**
+     *  Retrieves a hosted connection by ID.
+     */
+    public HostedConnection getConnection( long id );     
+    /**
+     *  Retrieves a read-only collection of all currently connected connections.
+     */
+    public Collection<HostedConnection> getConnections(); 
+    /**
+     *  Adds a listener that will be notified when new hosted connections
+     *  arrive.
+     */
+    public void addConnectionListener( ConnectionListener listener );
+    /**
+     *  Removes a previously registered connection listener.
+     */   
+    public void removeConnectionListener( ConnectionListener listener );     
+    
+    /**
+     *  Adds a listener that will be notified when any message or object
+     *  is received from one of the clients.
+     */
+    public void addMessageListener( MessageListener<? super HostedConnection> listener ); 
+
+    /**
+     *  Adds a listener that will be notified when messages of the specified
+     *  types are received from one of the clients.
+     */
+    public void addMessageListener( MessageListener<? super HostedConnection> listener, Class... classes ); 
+
+    /**
+     *  Removes a previously registered wildcard listener.  This does
+     *  not remove this listener from any type-specific registrations.
+     */
+    public void removeMessageListener( MessageListener<? super HostedConnection> listener ); 
+
+    /**
+     *  Removes a previously registered type-specific listener from
+     *  the specified types.
+     */
+    public void removeMessageListener( MessageListener<? super HostedConnection> listener, Class... classes ); 
+    
+     
+}
+
diff --git a/engine/src/networking/com/jme3/network/base/ConnectorAdapter.java b/engine/src/networking/com/jme3/network/base/ConnectorAdapter.java
new file mode 100644 (file)
index 0000000..f3499f9
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.base;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.jme3.network.Message;
+import com.jme3.network.MessageListener;
+import com.jme3.network.kernel.Connector;
+import com.jme3.network.serializing.Serializer;
+
+/**
+ *  Wraps a single Connector and forwards new messages
+ *  to the supplied message dispatcher.  This is used
+ *  by DefaultClient to manage its connector objects.
+ *  This is only responsible for message reading and provides
+ *  no support for buffering writes.
+ *
+ *  <p>This adapter assumes a simple protocol where two
+ *  bytes define a (short) object size with the object data
+ *  to follow.  Note: this limits the size of serialized
+ *  objects to 32676 bytes... even though, for example,
+ *  datagram packets can hold twice that. :P</p>  
+ *
+ *  @version   $Revision$
+ *  @author    Paul Speed
+ */
+public class ConnectorAdapter extends Thread
+{
+    private Connector connector;
+    private MessageListener dispatcher;
+    private AtomicBoolean go = new AtomicBoolean(true);
+    
+    public ConnectorAdapter( Connector connector, MessageListener dispatcher )
+    {
+        super( String.valueOf(connector) );
+        this.connector = connector;
+        this.dispatcher = dispatcher;
+        setDaemon(true);
+    }
+    public void close()
+    {
+        go.set(false);
+        
+        // Kill the connector
+        connector.close();
+    }
+    protected void createAndDispatch( ByteBuffer buffer )
+    {
+        try {
+            Object obj = Serializer.readClassAndObject( buffer );
+            Message m = (Message)obj;
+            dispatcher.messageReceived( null, m );                        
+        } catch( IOException e ) {
+            throw new RuntimeException( "Error deserializing object", e );   
+        }         
+    }
+    public void run()
+    {
+        ByteBuffer current = null;
+        int size = 0;
+    
+        while( go.get() ) {
+            ByteBuffer buffer = connector.read();
+            
+            // push the data from the buffer into as
+            // many messages as we can
+            while( buffer.remaining() > 0 ) {
+                
+                if( current == null ) {
+                    // We are not currently reading an object so
+                    // grab the size.
+                    // Note: this is somewhat limiting... int would
+                    // be better.
+                    size = buffer.getShort();
+                    current = ByteBuffer.allocate(size);
+                }
+
+                if( current.remaining() <= buffer.remaining() ) {
+                    // We have at least one complete object so
+                    // copy what we can into current, create a message,
+                    // and then continue pulling from buffer.
+                    
+                    // Artificially set the limit so we don't overflow
+                    int extra = buffer.remaining() - current.remaining();
+                    buffer.limit( buffer.position() + current.remaining() );
+                    // Now copy the data                   
+                    current.put( buffer );
+                    current.flip();
+                    
+                    // Now set the limit back to a good value
+                    buffer.limit( buffer.position() + extra );
+                    createAndDispatch( current );
+                    current = null;                    
+                } else {
+                
+                    // Not yet a complete object so just copy what we have
+                    current.put( buffer ); 
+                }            
+            }            
+        }
+    }
+        
+}
diff --git a/engine/src/networking/com/jme3/network/base/DefaultClient.java b/engine/src/networking/com/jme3/network/base/DefaultClient.java
new file mode 100644 (file)
index 0000000..3a0dca4
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.base;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.jme3.network.*;
+import com.jme3.network.message.ClientRegistrationMessage; //hopefully temporary
+import com.jme3.network.kernel.Connector;
+import com.jme3.network.serializing.Serializer;
+
+/**
+ *  A default implementation of the Client interface that delegates
+ *  its network connectivity to a kernel.Connector.
+ *
+ *  @version   $Revision$
+ *  @author    Paul Speed
+ */
+public class DefaultClient implements Client
+{
+    static Logger log = Logger.getLogger(DefaultClient.class.getName());
+    
+    private long id = -1;
+    private boolean isRunning = false;
+    private Connector reliable;
+    private Connector fast;
+    private MessageListenerRegistry<Client> messageListeners = new MessageListenerRegistry<Client>();
+    private List<ClientStateListener> stateListeners = new CopyOnWriteArrayList<ClientStateListener>();
+    private Redispatch dispatcher = new Redispatch();
+    private ConnectorAdapter reliableAdapter;    
+    private ConnectorAdapter fastAdapter;    
+    
+    public DefaultClient( Connector reliable, Connector fast )
+    {
+        this.reliable = reliable;
+        this.fast = fast;
+        if( reliable != null ) {
+            reliableAdapter = new ConnectorAdapter(reliable, dispatcher);
+        }
+        if( fast != null ) {
+            fastAdapter = new ConnectorAdapter(fast, dispatcher);
+        }
+    }
+
+    protected void checkRunning()
+    {
+        if( !isRunning )
+            throw new IllegalStateException( "Client is not started." );
+    }
+    
+    public void start()
+    {
+        if( isRunning )
+            throw new IllegalStateException( "Client is already started." );
+            
+        // Start up the threads and stuff
+        if( reliableAdapter != null ) {
+            reliableAdapter.start();
+        }
+        if( fastAdapter != null ) {
+            fastAdapter.start();
+        }
+
+        // Send our connection message with a generated ID until
+        // we get one back from the server.  We'll hash time in
+        // millis and time in nanos.
+        long tempId = System.currentTimeMillis() ^ System.nanoTime();
+
+        // Set it true here so we can send some messages.
+        isRunning = true;        
+                
+        ClientRegistrationMessage reg;
+        if( reliable != null ) {
+            reg = new ClientRegistrationMessage();
+            reg.setId(tempId);
+            reg.setReliable(true);
+            send(reg);            
+        }
+        if( fast != null ) {
+            // We create two different ones to prepare for someday
+            // when there will probably be threaded sending.
+            reg = new ClientRegistrationMessage();
+            reg.setId(tempId);
+            reg.setReliable(false);
+            send(reg); 
+        }        
+    }
+
+    public boolean isConnected()
+    {
+        return id != -1; // for now
+    }     
+
+    public long getId()
+    {   
+        return id;
+    }     
+    protected ByteBuffer messageToBuffer( Message message )
+    {
+        ByteBuffer buffer = ByteBuffer.allocate( 32767 + 2 );
+        
+        try {
+            buffer.position( 2 );
+            Serializer.writeClassAndObject( buffer, message );
+            buffer.flip();
+            short dataLength = (short)(buffer.remaining() - 2);
+            buffer.putShort( dataLength );
+            buffer.position( 0 );
+            
+            return buffer;
+        } catch( IOException e ) {
+            throw new RuntimeException( "Error serializing message", e );
+        }
+    }
+    public void send( Message message )
+    {
+        checkRunning();
+        
+        // For now just send direclty.  We allocate our
+        // own buffer each time because this method might
+        // be called from multiple threads.  If writing
+        // is queued into its own thread then that could
+        // be shared.
+        ByteBuffer buffer = messageToBuffer(message);
+        if( message.isReliable() || fast == null ) {
+            if( reliable == null )
+                throw new RuntimeException( "No reliable connector configured" );
+            reliable.write(buffer);
+        } else {
+            fast.write(buffer); 
+        }
+    }
+    public void close()
+    {
+        checkRunning();
+            
+        // 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?
+        
+        fireDisconnected();
+        
+        isRunning = false;
+    }         
+
+    public void addClientStateListener( ClientStateListener listener )
+    {
+        stateListeners.add( listener );
+    } 
+
+    public void removeClientStateListener( ClientStateListener listener )
+    {
+        stateListeners.remove( listener );
+    } 
+
+    public void addMessageListener( MessageListener<? super Client> listener )
+    {
+        messageListeners.addMessageListener( listener );
+    } 
+
+    public void addMessageListener( MessageListener<? super Client> listener, Class... classes )
+    {
+        messageListeners.addMessageListener( listener, classes );
+    } 
+
+    public void removeMessageListener( MessageListener<? super Client> listener )
+    {
+        messageListeners.removeMessageListener( listener );
+    } 
+
+    public void removeMessageListener( MessageListener<? super Client> listener, Class... classes )
+    {
+        messageListeners.removeMessageListener( listener, classes );
+    } 
+    protected void fireConnected()
+    {
+        for( ClientStateListener l : stateListeners ) {
+            l.clientConnected( this );
+        }            
+    }
+    
+    protected void fireDisconnected()
+    {
+        for( ClientStateListener l : stateListeners ) {
+            l.clientDisconnected( this );
+        }            
+    }
+    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 = ((ClientRegistrationMessage)m).getId();
+            log.log( Level.INFO, "Connection established, id:{0}.", this.id );
+            fireConnected();
+            return;
+        }
+    
+        messageListeners.messageReceived( this, m );
+    }
+    protected class Redispatch implements MessageListener
+    {
+        public void messageReceived( Object source, Message m )
+        {
+            dispatch( m );
+        }
+    }
+}
diff --git a/engine/src/networking/com/jme3/network/base/DefaultServer.java b/engine/src/networking/com/jme3/network/base/DefaultServer.java
new file mode 100644 (file)
index 0000000..78c89b9
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.base;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.jme3.network.*;
+import com.jme3.network.kernel.*;
+import com.jme3.network.message.ClientRegistrationMessage; //hopefully temporary
+import com.jme3.network.serializing.Serializer;
+
+/**
+ *  A default implementation of the Server interface that delegates
+ *  its network connectivity to kernel.Kernel.
+ *
+ *  @version   $Revision$
+ *  @author    Paul Speed
+ */
+public class DefaultServer implements Server
+{
+    private boolean isRunning = false;
+    private AtomicLong nextId = new AtomicLong(0);
+    private Kernel reliable;
+    private KernelAdapter reliableAdapter;
+    private Kernel fast;
+    private KernelAdapter fastAdapter;
+    private Redispatch dispatcher = new Redispatch();
+    private Map<Long,HostedConnection> connections = new ConcurrentHashMap<Long,HostedConnection>();
+    private Map<Endpoint,HostedConnection> endpointConnections 
+                            = new ConcurrentHashMap<Endpoint,HostedConnection>();
+    
+    // Keeps track of clients for whom we've only received the UDP
+    // registration message
+    private Map<Long,Connection> connecting = new ConcurrentHashMap<Long,Connection>();
+    
+    private MessageListenerRegistry<HostedConnection> messageListeners 
+                            = new MessageListenerRegistry<HostedConnection>();                        
+    private List<ConnectionListener> connectionListeners = new CopyOnWriteArrayList<ConnectionListener>();
+    
+    public DefaultServer( Kernel reliable, Kernel fast )
+    {
+        this.reliable = reliable;
+        this.fast = fast;
+        
+        if( reliable != null ) {
+            reliableAdapter = new KernelAdapter( this, reliable, dispatcher );
+        }
+        if( fast != null ) {
+            fastAdapter = new KernelAdapter( this, fast, dispatcher );
+        }
+    }   
+
+    public void start()
+    {
+        if( isRunning )
+            throw new IllegalStateException( "Server is already started." );
+            
+        // Initialize the kernels
+        if( reliable != null ) {
+            reliable.initialize();
+        }
+        if( fast != null ) {
+            fast.initialize();
+        }
+        // Start em up
+        if( reliableAdapter != null ) {
+            reliableAdapter.start();            
+        }
+        if( fastAdapter != null ) {
+            fastAdapter.start();
+        }
+        
+        isRunning = true;             
+    }
+
+    public boolean isRunning()
+    {
+        return isRunning;
+    }     
+    public void close() 
+    {
+        if( !isRunning )
+            throw new IllegalStateException( "Server is not started." );
+        try {
+            // Kill the adpaters, they will kill the kernels
+            if( fastAdapter != null ) {
+                fastAdapter.close();
+            }
+            if( reliableAdapter != null ) {
+                reliableAdapter.close();
+                
+            isRunning = false;            
+            }
+        } catch( InterruptedException e ) {
+            throw new RuntimeException( "Interrupted while closing", e );
+        }                               
+    }
+    protected ByteBuffer messageToBuffer( Message message )
+    {
+        ByteBuffer buffer = ByteBuffer.allocate( 32767 + 2 );
+        
+        try {
+            buffer.position( 2 );
+            Serializer.writeClassAndObject( buffer, message );
+            buffer.flip();
+            short dataLength = (short)(buffer.remaining() - 2);
+            buffer.putShort( dataLength );
+            buffer.position( 0 );
+            
+            return buffer;
+        } catch( IOException e ) {
+            throw new RuntimeException( "Error serializing message", e );
+        }
+    }
+
+    public void broadcast( Message message )
+    {
+        broadcast( null, message );
+    }
+
+    public void broadcast( Object filter, Message message )
+    {
+        ByteBuffer buffer = messageToBuffer(message);
+        
+        // Ignore the filter for the moment
+        if( message.isReliable() || fast == null ) {
+            if( reliable == null )
+                throw new RuntimeException( "No reliable kernel configured" );
+            reliable.broadcast( filter, buffer, true );
+        } else {
+            fast.broadcast( filter, buffer, false );
+        }               
+    }
+
+    public HostedConnection getConnection( long id )
+    {
+        return connections.get(id);
+    }     
+    public Collection<HostedConnection> getConnections()
+    {
+        return Collections.unmodifiableCollection((Collection<HostedConnection>)connections.values());
+    } 
+    public void addConnectionListener( ConnectionListener listener )
+    {
+        connectionListeners.add(listener);   
+    }
+    public void removeConnectionListener( ConnectionListener listener )
+    {
+        connectionListeners.remove(listener);   
+    }     
+    
+    public void addMessageListener( MessageListener<? super HostedConnection> listener )
+    {
+        messageListeners.addMessageListener( listener );
+    } 
+
+    public void addMessageListener( MessageListener<? super HostedConnection> listener, Class... classes )
+    {
+        messageListeners.addMessageListener( listener, classes );
+    } 
+
+    public void removeMessageListener( MessageListener<? super HostedConnection> listener )
+    {
+        messageListeners.removeMessageListener( listener );
+    } 
+
+    public void removeMessageListener( MessageListener<? super HostedConnection> listener, Class... classes )
+    {
+        messageListeners.removeMessageListener( listener, classes );
+    }
+    protected void dispatch( HostedConnection source, Message m )
+    {
+        messageListeners.messageReceived( source, m );
+    }
+
+    protected void fireConnectionAdded( HostedConnection conn )
+    {
+        for( ConnectionListener l : connectionListeners ) {
+            l.connectionAdded( this, conn );
+        }
+    }            
+
+    protected void fireConnectionRemoved( HostedConnection conn )
+    {
+        for( ConnectionListener l : connectionListeners ) {
+            l.connectionRemoved( this, conn );
+        }
+    }            
+
+    protected void registerClient( KernelAdapter ka, Endpoint p, ClientRegistrationMessage m )
+    {
+        Connection addedConnection = null;
+    
+        // generally this will only be called by one thread but it's        
+        // important enough I won't take chances
+        synchronized( this ) {       
+            // Grab the random ID that the client created when creating
+            // its two registration messages
+            long tempId = m.getId();
+            // See if we already have one
+            Connection c = connecting.remove(tempId);
+            if( c == null ) {
+                c = new Connection();
+            }
+            // Fill in what we now know       
+            if( ka == fastAdapter ) {
+                c.fast = p;
+                
+                if( c.reliable == null ) {
+                    // Tuck it away for later
+                    connecting.put(tempId, c);
+                }
+                
+            } else {
+                // It must be the reliable one            
+                c.reliable = p;
+                
+                if( c.fast == null && fastAdapter != null ) {
+                    // Still waiting for the fast connection to
+                    // register
+                    connecting.put(tempId, c);
+                }
+            }
+    
+            if( !connecting.containsKey(tempId) ) {
+    
+                // Then we are fully connected
+                if( connections.put( c.getId(), c ) == null ) {
+                
+                    if( c.fast != null ) {
+                        endpointConnections.put( c.fast, c );
+                    }
+                    endpointConnections.put( c.reliable, c ); 
+                    addedConnection = c;               
+                }
+            }
+        }
+        // Best to do this outside of the synch block to avoid
+        // over synchronizing which is the path to deadlocks
+        if( addedConnection != null ) {       
+            // Nnow we can notify the listeners about the
+            // new connection.
+            fireConnectionAdded( addedConnection );
+                                                    
+            // Send the ID back to the client letting it know it's
+            // fully connected.
+            m = new ClientRegistrationMessage();
+            m.setId( addedConnection.getId() );
+            m.setReliable(true);
+            addedConnection.send(m);
+        }            
+    }
+
+    protected HostedConnection getConnection( Endpoint endpoint )
+    {
+        return endpointConnections.get(endpoint);       
+    } 
+
+    protected void connectionClosed( Endpoint p )
+    {
+        // Try to find the endpoint in all ways that it might
+        // exist.  Note: by this point the channel is closed
+        // already.
+    
+        // Also note: this method will be called twice per
+        // HostedConnection if it has two endpoints.
+        Connection removed = null;
+        synchronized( this ) {             
+            // Just in case the endpoint was still connecting
+            connecting.values().remove(p);
+
+            // And the regular management
+            removed = (Connection)endpointConnections.remove(p);
+            if( removed != null ) {
+                connections.remove( removed.getId() );                
+            }
+        }
+        
+        // Better not to fire events while we hold a lock
+        // so always do this outside the synch block.
+        if( removed != null ) {
+        
+            // Make sure both endpoints are closed.  Note: reliable
+            // should always already be closed through all paths that I
+            // can conceive... but it doesn't hurt to be sure. 
+            if( removed.reliable != null && removed.reliable.isConnected() ) {
+                removed.reliable.close();
+            }
+            if( removed.fast != null && removed.fast.isConnected() ) {
+                removed.fast.close();
+            }
+        
+            fireConnectionRemoved( removed );
+        }
+    }
+
+    protected class Connection implements HostedConnection
+    {
+        private long id;
+        private Endpoint reliable;
+        private Endpoint fast;       
+        
+        public Connection()
+        {
+            id = nextId.getAndIncrement();
+        }
+        
+        public long getId()
+        {
+            return id;
+        }
+        
+        public void send( Message message )
+        {
+            ByteBuffer buffer = messageToBuffer(message);
+            if( message.isReliable() || fast == null ) {
+                reliable.send( buffer );
+            } else {
+                fast.send( buffer );
+            }
+        }
+        
+        public void close( String reason )
+        {
+            // Send a reason
+        
+            // Just close the reliable endpoint
+            // fast will be cleaned up as a side-effect
+            if( reliable != null ) {
+                reliable.close();
+            }
+        }     
+    } 
+
+    protected class Redispatch implements MessageListener<HostedConnection>
+    {
+        public void messageReceived( HostedConnection source, Message m )
+        {
+            dispatch( source, m );
+        }
+    }                                          
+     
+}
diff --git a/engine/src/networking/com/jme3/network/base/KernelAdapter.java b/engine/src/networking/com/jme3/network/base/KernelAdapter.java
new file mode 100644 (file)
index 0000000..6c861c8
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.base;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.jme3.network.*;
+import com.jme3.network.kernel.Endpoint;
+import com.jme3.network.kernel.EndpointEvent;
+import com.jme3.network.kernel.Envelope;
+import com.jme3.network.kernel.Kernel;
+import com.jme3.network.message.ClientRegistrationMessage; //hopefully temporary
+import com.jme3.network.serializing.Serializer;
+
+/**
+ *  Wraps a single Kernel and forwards new messages
+ *  to the supplied message dispatcher and new endpoint
+ *  events to the connection dispatcher.  This is used
+ *  by DefaultServer to manage its kernel objects.
+ *
+ *  <p>This adapter assumes a simple protocol where two
+ *  bytes define a (short) object size with the object data
+ *  to follow.  Note: this limits the size of serialized
+ *  objects to 32676 bytes... even though, for example,
+ *  datagram packets can hold twice that. :P</p>  
+ *
+ *  @version   $Revision$
+ *  @author    Paul Speed
+ */
+public class KernelAdapter extends Thread
+{
+    private DefaultServer server; // this is unfortunate
+    private Kernel kernel;
+    private MessageListener messageDispatcher;
+    private AtomicBoolean go = new AtomicBoolean(true);
+    
+    public KernelAdapter( DefaultServer server, Kernel kernel, MessageListener messageDispatcher )
+    {
+        super( String.valueOf(kernel) );
+        this.server = server;
+        this.kernel = kernel;
+        this.messageDispatcher = messageDispatcher;
+        setDaemon(true);
+    }
+    public void close() throws InterruptedException
+    {
+        go.set(false);
+        
+        // Kill the kernel
+        kernel.terminate();
+    }
+
+    protected HostedConnection getConnection( Endpoint p )
+    {
+        return server.getConnection(p);
+    }
+    protected void connectionClosed( Endpoint p )
+    {
+        server.connectionClosed(p);
+    }
+    protected void createAndDispatch( Endpoint p, ByteBuffer buffer )
+    {
+        try {
+            Object obj = Serializer.readClassAndObject( buffer );
+            Message m = (Message)obj;
+            // Because this class is the only one with the information
+            // to do it... we need to pull of the registration message
+            // here.
+            if( m instanceof ClientRegistrationMessage ) {
+                server.registerClient( this, p, (ClientRegistrationMessage)m );
+                return;           
+            }                
+            
+            HostedConnection source = getConnection(p);
+            messageDispatcher.messageReceived( source, m );
+        } catch( IOException e ) {
+            throw new RuntimeException( "Error deserializing object", e );   
+        }         
+    }
+
+    protected void createAndDispatch( Envelope env )
+    {
+        byte[] data = env.getData();
+        ByteBuffer buffer = ByteBuffer.wrap(data);
+        
+        ByteBuffer current = null;
+        int size = 0;
+        
+        // push the data from the buffer into as
+        // many messages as we can
+        while( buffer.remaining() > 0 ) {
+                
+            if( current == null ) {
+                // We are not currently reading an object so
+                // grab the size.
+                // Note: this is somewhat limiting... int would
+                // be better.
+                size = buffer.getShort();
+                current = ByteBuffer.allocate(size);
+            }
+                
+            if( current.remaining() <= buffer.remaining() ) {
+                // We have at least one complete object so
+                // copy what we can into current, create a message,
+                // and then continue pulling from buffer.
+                    
+                // Artificially set the limit so we don't overflow
+                int extra = buffer.remaining() - current.remaining();
+                buffer.limit( buffer.position() + current.remaining() );
+                // Now copy the data                   
+                current.put( buffer );
+                current.flip();                
+                    
+                // Now set the limit back to a good value
+                buffer.limit( buffer.position() + extra );
+                createAndDispatch( env.getSource(), current );
+                current = null;                    
+            } else {
+                
+                // Not yet a complete object so just copy what we have
+                current.put( buffer ); 
+            }            
+        }            
+            
+    } 
+
+    protected void createAndDispatch( EndpointEvent event )
+    {
+        // Only need to tell the server about disconnects 
+        if( event.getType() == EndpointEvent.Type.REMOVE ) {
+            connectionClosed( event.getEndpoint() );
+        }            
+    }
+    protected void flushEvents()
+    {
+        EndpointEvent event;
+        while( (event = kernel.nextEvent()) != null )
+            {
+            createAndDispatch( event );
+            }
+    }
+    public void run()
+    {
+        while( go.get() ) {
+        
+            try {           
+                // Check for pending events
+                flushEvents();
+                // Grab the next envelope
+                Envelope e = kernel.read();
+                // Check for pending events that might have
+                // come in while we were blocking.  This is usually
+                // when the connection add events come through
+                flushEvents();
+            
+                createAndDispatch( e );
+            } catch( InterruptedException ex ) {
+                if( !go.get() )
+                    return;
+                throw new RuntimeException( "Unexpected interruption", ex );
+            }
+        }
+    }
+        
+}
+
+
diff --git a/engine/src/networking/com/jme3/network/base/MessageListenerRegistry.java b/engine/src/networking/com/jme3/network/base/MessageListenerRegistry.java
new file mode 100644 (file)
index 0000000..fae0cf3
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2011 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.base;
+
+import java.util.*;
+import java.util.concurrent.*;
+
+import com.jme3.network.Message;
+import com.jme3.network.MessageListener;
+
+/**
+ *  Keeps track of message listeners registered to specific
+ *  types or to any type.
+ *
+ *  @version   $Revision$
+ *  @author    Paul Speed
+ */
+public class MessageListenerRegistry<S> implements MessageListener<S>
+{
+    private List<MessageListener<? super S>> listeners = new CopyOnWriteArrayList<MessageListener<? super S>>();
+    private Map<Class,List<MessageListener<? super S>>> typeListeners 
+                    = new ConcurrentHashMap<Class,List<MessageListener<? super S>>>(); 
+
+    public MessageListenerRegistry()
+    {
+    }
+    public void messageReceived( S source, Message m )
+    {
+        for( MessageListener<? super S> l : listeners ) {
+            l.messageReceived( source, m );
+        }
+        
+        for( MessageListener<? super S> l : getListeners(m.getClass(),false) ) {
+            l.messageReceived( source, m );
+        }
+    }
+    protected List<MessageListener<? super S>> getListeners( Class c, boolean create )
+    {
+        List<MessageListener<? super S>> result = typeListeners.get(c);
+        if( result == null && create ) {
+            result = new CopyOnWriteArrayList<MessageListener<? super S>>();
+            typeListeners.put( c, result );
+        }
+        
+        if( result == null ) {
+            result = Collections.emptyList();
+        }
+        return result;   
+    }
+    
+    public void addMessageListener( MessageListener<? super S> listener )
+    {
+        listeners.add(listener);
+    } 
+
+    public void removeMessageListener( MessageListener<? super S> listener )
+    {
+        listeners.remove(listener);
+    } 
+
+    public void addMessageListener( MessageListener<? super S> listener, Class... classes )
+    {
+        for( Class c : classes ) {
+            getListeners(c, true).add(listener);
+        }
+    } 
+
+    public void removeMessageListener( MessageListener<? super S> listener, Class... classes )
+    {
+        for( Class c : classes ) {
+            getListeners(c, false).remove(listener);
+        }
+    }
+}
index 03acc5d..e7dbf68 100644 (file)
@@ -44,7 +44,7 @@ import com.jme3.network.serializing.Serializable;
  * @author Lars Wesselius
  */
 @Serializable()
-public class Message {
+public class Message implements com.jme3.network.Message {
     // The connector this message is meant for.
     private transient Client        connector;
     private transient Connection    connection;