networking layer. Note: because some messages were removed from Serializer's auto-registration this version is not binary-compatible with the last. So you must upgrade your client and server at the same time. git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8286 75d07b2b-3a1a-0410-a2c5-0572b91ccdca3.0
parent
878d997a4e
commit
c548987709
@ -1,598 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.connection; |
||||
|
||||
import com.jme3.network.events.ConnectionListener; |
||||
import com.jme3.network.events.MessageListener; |
||||
import com.jme3.network.message.ClientRegistrationMessage; |
||||
import com.jme3.network.message.DisconnectMessage; |
||||
import com.jme3.network.message.DiscoverHostMessage; |
||||
import com.jme3.network.message.Message; |
||||
import com.jme3.network.queue.MessageQueue; |
||||
import com.jme3.network.serializing.Serializer; |
||||
import com.jme3.network.service.ServiceManager; |
||||
import java.io.IOException; |
||||
import java.net.*; |
||||
import java.nio.ByteBuffer; |
||||
import java.nio.channels.CancelledKeyException; |
||||
import java.nio.channels.DatagramChannel; |
||||
import java.nio.channels.SelectionKey; |
||||
import java.nio.channels.SocketChannel; |
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
/** |
||||
* @deprecated Use {@link com.jme3.network.Client} from {@link com.jme3.network.Network} instead. |
||||
*/ |
||||
@Deprecated |
||||
public class Client extends ServiceManager { |
||||
protected Logger log = Logger.getLogger(Client.class.getName()); |
||||
|
||||
protected static int clientIDCounter = 0; |
||||
protected int clientID; |
||||
protected long playerID = -1; |
||||
protected String label; |
||||
|
||||
protected boolean isConnected; |
||||
protected TCPConnection tcp; |
||||
protected UDPConnection udp; |
||||
|
||||
protected ConnectionRunnable |
||||
thread; |
||||
|
||||
protected MessageQueue messageQueue; |
||||
|
||||
// Client (connector) related.
|
||||
protected SocketChannel tcpChannel; |
||||
protected DatagramChannel udpChannel; |
||||
|
||||
protected SocketAddress udpTarget; |
||||
|
||||
protected boolean isConnector; |
||||
|
||||
protected ClientObserver listener = new ClientObserver(); |
||||
|
||||
/** |
||||
* Constructs this client. |
||||
* @deprecated Call createClient() on {@link com.jme3.network.Network} instead. |
||||
*/ |
||||
@Deprecated |
||||
public Client() { |
||||
this(false); |
||||
} |
||||
|
||||
/** |
||||
* Construct this client, either as a server connector, or |
||||
* a real client. Internal method. |
||||
* |
||||
* @param connector Whether this client is a connector or not. |
||||
*/ |
||||
Client(boolean connector) { |
||||
super(ServiceManager.CLIENT); |
||||
clientID = ++clientIDCounter; |
||||
this.label = "Client#" + clientID; |
||||
|
||||
isConnector = connector; |
||||
if (connector) { |
||||
isConnected = true; |
||||
} else { |
||||
if (tcp == null) tcp = new TCPConnection(label); |
||||
if (udp == null) udp = new UDPConnection(label); |
||||
} |
||||
|
||||
messageQueue = new MessageQueue(); |
||||
} |
||||
|
||||
/** |
||||
* Constructor providing custom instances of the clients and its addresses. |
||||
* |
||||
* @param tcp The TCPConnection instance to manage. |
||||
* @param udp The UDPConnection instance to manage. |
||||
* @param tcpAddress The TCP address to connect to. |
||||
* @param udpAddress The UDP address to connect to. |
||||
* @throws java.io.IOException When a connect error has occurred. |
||||
*/ |
||||
public Client(TCPConnection tcp, UDPConnection udp, SocketAddress tcpAddress, SocketAddress udpAddress) throws IOException { |
||||
this(); |
||||
|
||||
this.tcp = tcp; |
||||
tcp.connect(tcpAddress); |
||||
|
||||
this.udp = udp; |
||||
udp.connect(udpAddress); |
||||
isConnected = true; |
||||
|
||||
registerInternalListeners(); |
||||
} |
||||
|
||||
/** |
||||
* Constructor for providing a TCP client instance. UDP will be disabled. |
||||
* |
||||
* @param tcp The TCPConnection instance. |
||||
* @param tcpAddress The address to connect to. |
||||
* @throws IOException When a connection error occurs. |
||||
*/ |
||||
public Client(TCPConnection tcp, SocketAddress tcpAddress) throws IOException { |
||||
this(); |
||||
|
||||
this.tcp = tcp; |
||||
tcp.connect(tcpAddress); |
||||
isConnected = true; |
||||
|
||||
registerInternalListeners(); |
||||
} |
||||
|
||||
/** |
||||
* Constructor for providing a UDP client instance. TCP will be disabled. |
||||
* |
||||
* @param udp The UDP client instance. |
||||
* @param updAddress The address to connect to. |
||||
* @throws IOException When a connection error occurs. |
||||
*/ |
||||
public Client(UDPConnection udp, SocketAddress updAddress) throws IOException { |
||||
this(); |
||||
|
||||
this.udp = udp; |
||||
udp.connect(updAddress); |
||||
isConnected = true; |
||||
|
||||
registerInternalListeners(); |
||||
} |
||||
|
||||
/** |
||||
* Simple constructor for providing TCP port and UDP port. Will bind using on |
||||
* all interfaces, on given ports. |
||||
* |
||||
* @param ip The IP address where the server are located. |
||||
* @param tcpPort The TCP port to use. |
||||
* @param udpPort The UDP port to use. |
||||
* @throws IOException When a connection error occurs. |
||||
* @deprecated Call connectToServer() on {@link com.jme3.network.Network} instead. |
||||
*/ |
||||
@Deprecated |
||||
public Client(String ip, int tcpPort, int udpPort) throws IOException { |
||||
this(); |
||||
|
||||
tcp = new TCPConnection(label); |
||||
tcp.connect(new InetSocketAddress(ip, tcpPort)); |
||||
|
||||
udp = new UDPConnection(label); |
||||
udp.connect(new InetSocketAddress(ip, udpPort)); |
||||
isConnected = true; |
||||
|
||||
registerInternalListeners(); |
||||
} |
||||
|
||||
/** |
||||
* Connect method for when the no arg constructor was used. |
||||
* |
||||
* @param ip The IP address to connect to. |
||||
* @param tcpPort The TCP port to use. To turn off, use -1. |
||||
* @param udpPort The UDP port to use. To turn off, use -1. |
||||
* @throws IllegalArgumentException When an illegal argument was given. |
||||
* @throws java.io.IOException When a connection error occurs. |
||||
*/ |
||||
public void connect(String ip, int tcpPort, int udpPort) throws IllegalArgumentException, IOException { |
||||
if (tcpPort == -1 && udpPort == -1) throw new IllegalArgumentException("No point in connect when you want to turn both the connections off."); |
||||
|
||||
if (tcpPort != -1) { |
||||
tcp.connect(new InetSocketAddress(ip, tcpPort)); |
||||
} |
||||
if (udpPort != -1) { |
||||
udp.connect(new InetSocketAddress(ip, udpPort)); |
||||
} |
||||
registerInternalListeners(); |
||||
isConnected = true; |
||||
} |
||||
|
||||
private void registerInternalListeners() { |
||||
if (tcp != null) { |
||||
tcp.addConnectionListener(listener); |
||||
tcp.socketChannel.keyFor(tcp.selector).attach(this); |
||||
} |
||||
addMessageListener(listener, DisconnectMessage.class); |
||||
} |
||||
|
||||
/** |
||||
* Send a message. Whether it's over TCP or UDP is determined by the message flag. |
||||
* |
||||
* @param message The message to send. |
||||
* @throws IOException When a writing error occurs. |
||||
*/ |
||||
public void send(Message message) throws IOException { |
||||
if (!isConnected) throw new IOException("Not connected yet. Use connect() first."); |
||||
|
||||
try { |
||||
if (message.isReliable()) { |
||||
messageQueue.add(message); |
||||
if (!isConnector) { |
||||
tcp.socketChannel.keyFor(tcp.selector).interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); |
||||
} else { |
||||
tcpChannel.keyFor(tcp.selector).interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); |
||||
} |
||||
} else { |
||||
udp.sendObject(message); |
||||
} |
||||
} catch (CancelledKeyException e) { |
||||
// Client was disconnected.
|
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Disconnect from the server. |
||||
* |
||||
* @param type See DisconnectMessage for the available types. |
||||
* @throws IOException When a disconnection error occurs. |
||||
*/ |
||||
public void disconnect(String type) throws IOException { |
||||
if (isConnector) return; |
||||
// Send a disconnect message to the server.
|
||||
DisconnectMessage msg = new DisconnectMessage(); |
||||
msg.setType(type); |
||||
tcp.sendObject(msg); |
||||
udp.sendObject(msg); |
||||
|
||||
// We can disconnect now.
|
||||
thread.setKeepAlive(false); |
||||
|
||||
// GC it.
|
||||
thread = null; |
||||
|
||||
log.log(Level.INFO, "[{0}][???] Disconnected.", label); |
||||
isConnected = false; |
||||
} |
||||
|
||||
/** |
||||
* Disconnect from the server. |
||||
* |
||||
* @param msg The custom DisconnectMessage to use. |
||||
* @throws IOException When a disconnection error occurs. |
||||
*/ |
||||
public void disconnect(DisconnectMessage msg) throws IOException { |
||||
if (isConnector) return; |
||||
// Send a disconnect message to the server.
|
||||
tcp.sendObject(msg); |
||||
udp.sendObject(msg); |
||||
|
||||
// We can disconnect now.
|
||||
thread.setKeepAlive(false); |
||||
|
||||
// GC it.
|
||||
thread = null; |
||||
|
||||
log.log(Level.INFO, "[{0}][???] Disconnected.", label); |
||||
isConnected = false; |
||||
} |
||||
|
||||
/** |
||||
* Disconnect from the server with the default disconnection type: |
||||
* USER_REQUESTED. |
||||
* |
||||
* @throws IOException When a disconnection error occurs. |
||||
*/ |
||||
public void disconnect() throws IOException { |
||||
disconnect(DisconnectMessage.USER_REQUESTED); |
||||
} |
||||
|
||||
/** |
||||
* Kick this client from the server, with given kick reason. |
||||
* |
||||
* @param reason The reason this client was kicked. |
||||
* @throws IOException When a writing error occurs. |
||||
*/ |
||||
public void kick(String reason) throws IOException { |
||||
if (!isConnector) return; |
||||
DisconnectMessage message = new DisconnectMessage(); |
||||
message.setType(DisconnectMessage.KICK); |
||||
message.setReason(reason); |
||||
message.setReliable(true); |
||||
send(message); |
||||
|
||||
tcp.addToDisconnectionQueue(this); |
||||
|
||||
log.log(Level.INFO, "[Server#?][???] {0} got kicked with reason: {1}.", new Object[]{this, reason}); |
||||
} |
||||
|
||||
/** |
||||
* Kick this client from the server, with given kick reason. |
||||
* |
||||
* @param message The custom disconnect message. |
||||
* @throws IOException When a writing error occurs. |
||||
*/ |
||||
public void kick(DisconnectMessage message) throws IOException { |
||||
if (!isConnector) return; |
||||
message.setReliable(true); |
||||
send(message); |
||||
|
||||
tcp.addToDisconnectionQueue(this); |
||||
|
||||
log.log(Level.INFO, "[Server#?][???] {0} got kicked with reason: {1}.", new Object[]{this, message.getReason()}); |
||||
} |
||||
|
||||
private void disconnectInternal(DisconnectMessage message) throws IOException { |
||||
DisconnectMessage dcMessage = (DisconnectMessage)message; |
||||
String type = dcMessage.getType(); |
||||
String reason = dcMessage.getReason(); |
||||
|
||||
log.log(Level.INFO, "[{0}][???] We got disconnected from the server ({1}: {2}).", new Object[]{ |
||||
label, |
||||
type, |
||||
reason |
||||
}); |
||||
|
||||
// We can disconnect now.
|
||||
thread.setKeepAlive(false); |
||||
|
||||
// GC it.
|
||||
thread = null; |
||||
|
||||
isConnected = false; |
||||
} |
||||
|
||||
public List<InetAddress> discoverHosts(int port, int timeout) throws IOException { |
||||
ArrayList<InetAddress> addresses = new ArrayList<InetAddress>(); |
||||
|
||||
DatagramSocket socket = new DatagramSocket(); |
||||
ByteBuffer buffer = ByteBuffer.allocate(4); |
||||
|
||||
Serializer.writeClass(buffer, DiscoverHostMessage.class); |
||||
|
||||
buffer.flip(); |
||||
byte[] data = new byte[buffer.limit()]; |
||||
buffer.get(data); |
||||
|
||||
for (NetworkInterface iface : Collections.list(NetworkInterface.getNetworkInterfaces())) { |
||||
for (InetAddress address : Collections.list(iface.getInetAddresses())) { |
||||
if (address instanceof Inet6Address || address.isLoopbackAddress()) continue; |
||||
byte[] ip = address.getAddress(); |
||||
ip[3] = -1; |
||||
socket.send(new DatagramPacket(data, data.length, InetAddress.getByAddress(ip), port)); |
||||
ip[2] = -1; |
||||
socket.send(new DatagramPacket(data, data.length, InetAddress.getByAddress(ip), port)); |
||||
} |
||||
} |
||||
log.log(Level.FINE, "[{0}][UDP] Started discovery on port {1}.", new Object[]{label, port}); |
||||
|
||||
long targetTime = System.currentTimeMillis() + timeout; |
||||
|
||||
DatagramPacket packet = new DatagramPacket(new byte[0], 0); |
||||
socket.setSoTimeout(1000); |
||||
while (System.currentTimeMillis() < targetTime) { |
||||
try { |
||||
socket.receive(packet); |
||||
if (addresses.contains(packet.getAddress())) continue; |
||||
addresses.add(packet.getAddress()); |
||||
log.log(Level.FINE, "[{0}][UDP] Discovered server on {1}.", new Object[]{label, packet.getAddress()}); |
||||
} catch (SocketTimeoutException ste) { |
||||
// Nothing to be done here.
|
||||
} |
||||
} |
||||
|
||||
return addresses; |
||||
} |
||||
|
||||
public void setLabel(String label) { |
||||
this.label = label; |
||||
} |
||||
|
||||
///////////////
|
||||
|
||||
// Server client related stuff.
|
||||
|
||||
public void setSocketChannel(SocketChannel channel) { |
||||
tcpChannel = channel; |
||||
} |
||||
|
||||
public SocketChannel getSocketChannel() { |
||||
return tcpChannel; |
||||
} |
||||
|
||||
public void setDatagramChannel(DatagramChannel channel) { |
||||
udpChannel = channel; |
||||
} |
||||
|
||||
public DatagramChannel getDatagramChannel() { |
||||
return udpChannel; |
||||
} |
||||
|
||||
public void setDatagramReceiver(SocketAddress address) { |
||||
udpTarget = address; |
||||
} |
||||
|
||||
public SocketAddress getDatagramReceiver() { |
||||
return udpTarget; |
||||
} |
||||
|
||||
public void setTCPConnection(TCPConnection con) { |
||||
tcp = con; |
||||
} |
||||
|
||||
public void setUDPConnection(UDPConnection con) { |
||||
udp = con; |
||||
} |
||||
|
||||
public TCPConnection getTCPConnection() { |
||||
return tcp; |
||||
} |
||||
|
||||
public UDPConnection getUDPConnection() { |
||||
return udp; |
||||
} |
||||
|
||||
public MessageQueue getMessageQueue() { |
||||
return messageQueue; |
||||
} |
||||
|
||||
///////////////
|
||||
|
||||
/** |
||||
* Start this client. |
||||
*/ |
||||
public void start() |
||||
{ |
||||
new Thread(thread = new ConnectionRunnable(tcp, udp)).start(); |
||||
} |
||||
|
||||
/** |
||||
* Start this client with given sleep time. Higher sleep times may affect the system's response time |
||||
* negatively, whereas lower values may increase CPU load. Use only when you're certain. |
||||
* |
||||
* @param sleep The sleep time. |
||||
*/ |
||||
public void start(int sleep) { |
||||
new Thread(thread = new ConnectionRunnable(tcp, udp, sleep)).start(); |
||||
} |
||||
|
||||
public int getClientID() { |
||||
return clientID; |
||||
} |
||||
|
||||
public long getPlayerID() { |
||||
return playerID; |
||||
} |
||||
|
||||
public void setPlayerID(long id) { |
||||
playerID = id; |
||||
} |
||||
|
||||
public String toString() { |
||||
return label; |
||||
} |
||||
|
||||
public void addConnectionListener(ConnectionListener listener) { |
||||
if (tcp != null) tcp.addConnectionListener(listener); |
||||
if (udp != null) udp.addConnectionListener(listener); |
||||
} |
||||
|
||||
public void removeConnectionListener(ConnectionListener listener) { |
||||
if (tcp != null) tcp.removeConnectionListener(listener); |
||||
if (udp != null) udp.removeConnectionListener(listener); |
||||
} |
||||
|
||||
public void addMessageListener(MessageListener listener) { |
||||
if (tcp != null) tcp.addMessageListener(listener); |
||||
if (udp != null) udp.addMessageListener(listener); |
||||
} |
||||
|
||||
public void addMessageListener(MessageListener listener, Class... classes) { |
||||
for (Class c : classes) { |
||||
if (tcp != null) tcp.addMessageListener(c, listener); |
||||
if (udp != null) udp.addMessageListener(c, listener); |
||||
} |
||||
} |
||||
|
||||
public void removeMessageListener(MessageListener listener) { |
||||
if (tcp != null) tcp.removeMessageListener(listener); |
||||
if (udp != null) udp.removeMessageListener(listener); |
||||
} |
||||
|
||||
public void removeMessageListener(MessageListener listener, Class... classes) { |
||||
for (Class c : classes) { |
||||
if (tcp != null) tcp.removeMessageListener(c, listener); |
||||
if (udp != null) udp.removeMessageListener(c, listener); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* {@inheritDoc} |
||||
*/ |
||||
public boolean equals(Object obj) { |
||||
if (obj == null || !(obj instanceof Client || obj instanceof Integer)) { |
||||
return false; |
||||
} else if (obj instanceof Client){ |
||||
return ((Client)obj).getClientID() == getClientID(); |
||||
} else if (obj instanceof Integer) { |
||||
return ((Integer)obj).intValue() == getClientID(); |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
protected class ClientObserver implements MessageListener, ConnectionListener { |
||||
|
||||
public void messageReceived(Message message) { |
||||
try { |
||||
disconnectInternal((DisconnectMessage)message); |
||||
} catch (IOException e) { |
||||
log.log(Level.WARNING, "[{0}][???] Could not disconnect.", label); |
||||
} |
||||
} |
||||
|
||||
public void messageSent(Message message) { |
||||
|
||||
} |
||||
|
||||
public void objectReceived(Object object) { |
||||
|
||||
} |
||||
|
||||
public void objectSent(Object object) { |
||||
|
||||
} |
||||
|
||||
public void clientConnected(Client client) { |
||||
// We are a client. This means that we succeeded in connecting to the server.
|
||||
if (!isConnected) return; |
||||
long time = System.currentTimeMillis(); |
||||
playerID = time; |
||||
ClientRegistrationMessage message = new ClientRegistrationMessage(); |
||||
message.setId(time); |
||||
try { |
||||
message.setReliable(false); |
||||
send(message); |
||||
message.setReliable(true); |
||||
send(message); |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
log.log(Level.SEVERE, "[{0}][???] Could not sent client registration message. Disconnecting.", label); |
||||
try { |
||||
disconnect(DisconnectMessage.ERROR); |
||||
} catch (IOException ie) {} |
||||
} |
||||
} |
||||
|
||||
public void clientDisconnected(Client client) { |
||||
if (thread != null) { |
||||
// We can disconnect now.
|
||||
thread.setKeepAlive(false); |
||||
|
||||
// GC it.
|
||||
thread = null; |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
@ -1,182 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.connection; |
||||
|
||||
import com.jme3.network.events.ConnectionListener; |
||||
import com.jme3.network.events.MessageAdapter; |
||||
import com.jme3.network.message.ClientRegistrationMessage; |
||||
import com.jme3.network.message.Message; |
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.Hashtable; |
||||
import java.util.List; |
||||
import java.util.logging.Logger; |
||||
|
||||
/** |
||||
* The ClientManager is an internal class that deals with client registrations and disconnects. |
||||
* |
||||
* @author Lars Wesselius |
||||
*/ |
||||
public class ClientManager extends MessageAdapter implements ConnectionListener { |
||||
protected Logger log = Logger.getLogger(ClientManager.class.getName()); |
||||
private ArrayList<Client> clients = new ArrayList<Client>(); |
||||
private Hashtable<Integer, Client> clientsByClientID = new Hashtable<Integer, Client>(); |
||||
|
||||
private ArrayList<ClientRegistrationMessage> pendingMessages = new ArrayList<ClientRegistrationMessage>(); |
||||
|
||||
private ArrayList<ConnectionListener> connectionListeners = new ArrayList<ConnectionListener>(); |
||||
|
||||
private ClientRegistrationMessage findMessage(long playerId) { |
||||
for (ClientRegistrationMessage message : pendingMessages) { |
||||
if (message.getId() == playerId) { |
||||
return message; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public List<Client> getConnectors() { |
||||
return Collections.unmodifiableList(clients); |
||||
} |
||||
|
||||
public Client getClient(long playerId) { |
||||
for (Client client : clients) { |
||||
if (client.getPlayerID() == playerId) return client; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public Client getClientByClientID(int clientID) { |
||||
return clientsByClientID.get(clientID); |
||||
} |
||||
|
||||
public boolean isClientConnected(Client client) { |
||||
return clients.contains(client); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void messageReceived(Message message) { |
||||
ClientRegistrationMessage regMessage = (ClientRegistrationMessage)message; |
||||
ClientRegistrationMessage existingMessage = findMessage(regMessage.getId()); |
||||
|
||||
// Check if message exists, if not add this message to the pending queue.
|
||||
if (existingMessage == null) { |
||||
pendingMessages.add(regMessage); |
||||
return; |
||||
} |
||||
|
||||
// We've got two messages of which we can construct a client.
|
||||
Client client = new Client(true); |
||||
|
||||
Connection conOne = regMessage.getConnection(); |
||||
Connection conTwo = existingMessage.getConnection(); |
||||
|
||||
if (conOne instanceof TCPConnection) { |
||||
fillInTCPInfo(client, regMessage); |
||||
} else if (conOne instanceof UDPConnection) { |
||||
fillInUDPInfo(client, regMessage); |
||||
} |
||||
|
||||
if (conTwo instanceof TCPConnection) { |
||||
fillInTCPInfo(client, existingMessage); |
||||
} else if (conTwo instanceof UDPConnection) { |
||||
fillInUDPInfo(client, existingMessage); |
||||
} |
||||
|
||||
if (client.getUDPConnection() == null || client.getTCPConnection() == null) { |
||||
// Something went wrong in this registration.
|
||||
log.severe("[ClientManager][???] Something went wrong in the client registration process."); |
||||
return; |
||||
} |
||||
|
||||
client.setPlayerID(regMessage.getId()); |
||||
|
||||
// Set other clients to this playerID as well.
|
||||
regMessage.getClient().setPlayerID(regMessage.getId()); |
||||
existingMessage.getClient().setPlayerID(regMessage.getId()); |
||||
|
||||
|
||||
fireClientConnected(client); |
||||
|
||||
// Remove pending message.
|
||||
pendingMessages.remove(existingMessage); |
||||
clients.add(client); |
||||
clientsByClientID.put(client.getClientID(), client); |
||||
} |
||||
|
||||
private void fillInUDPInfo(Client client, ClientRegistrationMessage msg) { |
||||
client.setUDPConnection((UDPConnection)msg.getConnection()); |
||||
client.setDatagramReceiver(msg.getClient().getDatagramReceiver()); |
||||
client.setDatagramChannel(msg.getClient().getDatagramChannel()); |
||||
|
||||
client.getDatagramChannel().keyFor(msg.getConnection().selector).attach(client); |
||||
} |
||||
|
||||
private void fillInTCPInfo(Client client, ClientRegistrationMessage msg) { |
||||
client.setSocketChannel(msg.getClient().getSocketChannel()); |
||||
client.setTCPConnection((TCPConnection)msg.getConnection()); |
||||
|
||||
client.getSocketChannel().keyFor(msg.getConnection().selector).attach(client); |
||||
} |
||||
|
||||
public void addConnectionListener(ConnectionListener listener) { |
||||
connectionListeners.add(listener); |
||||
} |
||||
|
||||
public void removeConnectionListener(ConnectionListener listener) { |
||||
connectionListeners.remove(listener); |
||||
} |
||||
|
||||
public void clientConnected(Client client) { |
||||
} |
||||
|
||||
public void clientDisconnected(Client client) { |
||||
if (clients.contains(client)) { |
||||
clients.remove(client); |
||||
fireClientDisconnected(client); |
||||
} |
||||
} |
||||
|
||||
public void fireClientConnected(Client client) { |
||||
for (ConnectionListener listener : connectionListeners) { |
||||
listener.clientConnected(client); |
||||
} |
||||
} |
||||
|
||||
public void fireClientDisconnected(Client client) { |
||||
for (ConnectionListener listener : connectionListeners) { |
||||
listener.clientDisconnected(client); |
||||
} |
||||
} |
||||
} |
@ -1,395 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.connection; |
||||
|
||||
import com.jme3.network.events.ConnectionListener; |
||||
import com.jme3.network.events.MessageListener; |
||||
import com.jme3.network.message.Message; |
||||
import java.io.IOException; |
||||
import java.net.ConnectException; |
||||
import java.net.InetSocketAddress; |
||||
import java.net.SocketAddress; |
||||
import java.nio.channels.SelectableChannel; |
||||
import java.nio.channels.SelectionKey; |
||||
import java.nio.channels.Selector; |
||||
import java.nio.channels.SocketChannel; |
||||
import java.util.*; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
/** |
||||
* Base class for a connection method. Extend this if you have some other fancy |
||||
* way of dealing with connections. This class provides basic message handling, connection filtering |
||||
* and handles the selector. |
||||
* |
||||
* @author Lars Wesselius |
||||
*/ |
||||
public abstract class Connection implements Runnable { |
||||
protected String label; |
||||
protected Logger log = Logger.getLogger(Connection.class.getName()); |
||||
protected final ArrayList<Client> connections = new ArrayList<Client>(); |
||||
|
||||
protected Selector selector; |
||||
protected boolean alive = false; |
||||
protected ArrayList<ConnectorFilter> connectorFilters = new ArrayList<ConnectorFilter>(); |
||||
|
||||
protected LinkedList<Client> disconnectionQueue = new LinkedList<Client>(); |
||||
|
||||
protected ArrayList<ConnectionListener> connectionListeners = new ArrayList<ConnectionListener>(); |
||||
protected ArrayList<MessageListener> messageListeners = new ArrayList<MessageListener>(); |
||||
protected HashMap<Class, List<MessageListener>> individualMessageListeners = new HashMap<Class, List<MessageListener>>(); |
||||
|
||||
public Connection() { |
||||
try { |
||||
selector = Selector.open(); |
||||
} catch (IOException e) { |
||||
log.log(Level.SEVERE, "Could not open selector.", e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Add a connector filter for this connection. |
||||
* |
||||
* @param filter The filter to add. |
||||
*/ |
||||
public void addConnectorFilter(ConnectorFilter filter) { |
||||
connectorFilters.add(filter); |
||||
} |
||||
|
||||
/** |
||||
* Remove a connector filter for this connection. |
||||
* @param filter The filter to remove. |
||||
*/ |
||||
public void removeConnectorFilter(ConnectorFilter filter) { |
||||
connectorFilters.remove(filter); |
||||
} |
||||
|
||||
/** |
||||
* Determine whether this connection should be filtered. |
||||
* |
||||
* @param address The address that should be checked. |
||||
* @return The reason if it should be filtered. |
||||
*/ |
||||
public String shouldFilterConnector(InetSocketAddress address) { |
||||
for (ConnectorFilter filter : connectorFilters) { |
||||
String str = filter.filterConnector(address); |
||||
if (str != null) return str; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public void run() { |
||||
if (!alive) alive = true; |
||||
try { |
||||
if (selector.selectNow() > 0) { |
||||
// We've received some keys.
|
||||
Set<SelectionKey> keys = selector.selectedKeys(); |
||||
|
||||
for (Iterator<SelectionKey> it = keys.iterator(); it.hasNext();) { |
||||
SelectionKey key = it.next(); |
||||
it.remove(); |
||||
|
||||
|
||||
if (key.isValid() && key.isReadable()) { |
||||
read(key.channel()); |
||||
} |
||||
if (key.isValid() && key.isAcceptable()) { |
||||
accept(key.channel()); |
||||
} |
||||
if (key.isValid() && key.isWritable()) { |
||||
write(key.channel()); |
||||
} |
||||
if (key.isValid() && key.isConnectable()) { |
||||
connect(key.channel()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Process queue's.
|
||||
Client dcClient = disconnectionQueue.poll(); |
||||
while (dcClient != null) { |
||||
disconnect(dcClient); |
||||
dcClient = disconnectionQueue.poll(); |
||||
} |
||||
} catch (ConnectException ce) { |
||||
log.log(Level.WARNING, "[{0}][???] Connection refused.", label); |
||||
fireClientDisconnected(null); |
||||
} catch (IOException e) { |
||||
log.log(Level.SEVERE, "[{0}][???] Error while selecting. Message: {1}", new Object[]{label, e.getMessage()}); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Get all the connectors. |
||||
* |
||||
* @return A unmodifiable list with the connectors. |
||||
*/ |
||||
public List<Client> getLocalConnectors() { |
||||
return Collections.unmodifiableList(connections); |
||||
} |
||||
|
||||
/** |
||||
* Get the combined connectors, meaning TCP and UDP are combined into one client. |
||||
* |
||||
* @return A unmodifiable list with the connectors. |
||||
*/ |
||||
public List<Client> getConnectors() { |
||||
return Collections.unmodifiableList(connections); |
||||
} |
||||
|
||||
/** |
||||
* Return whether this connection is still alive. |
||||
* |
||||
* @return True if so, false if not. |
||||
*/ |
||||
public boolean isAlive() { return alive; } |
||||
|
||||
/** |
||||
* Accept an incoming connection. |
||||
* |
||||
* @param channel The channel. |
||||
* @throws IOException When a problem occurs. |
||||
*/ |
||||
public abstract void accept(SelectableChannel channel) throws IOException; |
||||
|
||||
/** |
||||
* Finish the connection. |
||||
* |
||||
* @param channel The channel. |
||||
* @throws IOException When a problem occurs. |
||||
*/ |
||||
public abstract void connect(SelectableChannel channel) throws IOException; |
||||
|
||||
/** |
||||
* Read from the channel. |
||||
* |
||||
* @param channel The channel. |
||||
* @throws IOException When a problem occurs. |
||||
*/ |
||||
public abstract void read(SelectableChannel channel) throws IOException; |
||||
|
||||
/** |
||||
* Write to a channel. |
||||
* |
||||
* @param channel The channel to write to. |
||||
* @throws IOException When a problem occurs. |
||||
*/ |
||||
public abstract void write(SelectableChannel channel) throws IOException; |
||||
|
||||
/** |
||||
* Connect to a server using this overload. |
||||
* |
||||
* @param address The address to connect to. |
||||
* @throws IOException When a problem occurs. |
||||
*/ |
||||
public abstract void connect(SocketAddress address) throws IOException; |
||||
|
||||
/** |
||||
* Bind to an address. |
||||
* |
||||
* @param address The address to bind to. |
||||
* @throws IOException When a problem occurs. |
||||
*/ |
||||
public abstract void bind(SocketAddress address) throws IOException; |
||||
|
||||
/** |
||||
* Send an object to the server. If this is a server, it will be |
||||
* broadcast to all clients. |
||||
* |
||||
* @param object The object to send. |
||||
* @throws IOException When a writing error occurs. |
||||
*/ |
||||
public abstract void sendObject(Object object) throws IOException; |
||||
|
||||
/** |
||||
* Send an object to the connector. Server method. |
||||
* |
||||
* @param connector The connector to send to. |
||||
* @param object The object to send. |
||||
* @throws IOException When a writing error occurs. |
||||
*/ |
||||
public abstract void sendObject(Client connector, Object object) throws IOException; |
||||
|
||||
/** |
||||
* Called when the connection implementation should clean up. |
||||
* |
||||
* @throws IOException When a problem occurs. |
||||
*/ |
||||
public abstract void cleanup() throws IOException; |
||||
|
||||
/////////////////////////////// Connection management //////////////////////////
|
||||
|
||||
public void addToDisconnectionQueue(Client client) { |
||||
disconnectionQueue.add(client); |
||||
} |
||||
|
||||
/** |
||||
* Disconnect a client. |
||||
* |
||||
* @param client The client to disconnect. |
||||
* @throws IOException When closing the client's channel has failed. |
||||
*/ |
||||
private void disconnect(Client client) throws IOException { |
||||
if (client == null) return; |
||||
|
||||
// Find the correct client.
|
||||
|
||||
Client localClient = null; |
||||
synchronized (connections){ |
||||
for (Client locClient : connections) { |
||||
if (locClient.getPlayerID() == client.getPlayerID()) { |
||||
localClient = locClient; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (localClient == null) localClient = client; |
||||
|
||||
SocketChannel chan = localClient.getSocketChannel(); |
||||
if (chan != null) { |
||||
SelectionKey key = chan.keyFor(selector); |
||||
if (key != null) key.cancel(); |
||||
chan.close(); |
||||
} |
||||
|
||||
synchronized (connections){ |
||||
connections.remove(localClient); |
||||
} |
||||
fireClientDisconnected(client); |
||||
} |
||||
|
||||
|
||||
/////////////////////////////// Listener related ///////////////////////////////
|
||||
|
||||
public void addConnectionListener(ConnectionListener listener) { |
||||
connectionListeners.add(listener); |
||||
} |
||||
|
||||
public void removeConnectionListener(ConnectionListener listener) { |
||||
connectionListeners.remove(listener); |
||||
} |
||||
|
||||
public void addMessageListener(MessageListener listener) { |
||||
messageListeners.add(listener); |
||||
} |
||||
|
||||
public void removeMessageListener(MessageListener listener) { |
||||
messageListeners.remove(listener); |
||||
} |
||||
|
||||
public void addMessageListener(Class messageClass, MessageListener listener) { |
||||
if (individualMessageListeners.containsKey(messageClass)) { |
||||
individualMessageListeners.get(messageClass).add(listener); |
||||
} else { |
||||
List<MessageListener> list = new ArrayList<MessageListener>(); |
||||
list.add(listener); |
||||
individualMessageListeners.put(messageClass, list); |
||||
} |
||||
} |
||||
|
||||
public void removeMessageListener(Class messageClass, MessageListener listener) { |
||||
if (individualMessageListeners.containsKey(messageClass)) { |
||||
individualMessageListeners.get(messageClass).remove(listener); |
||||
} |
||||
} |
||||
|
||||
protected void fireMessageReceived(Message message) { |
||||
// Pass to listeners.
|
||||
for (MessageListener listener : messageListeners) { |
||||
listener.messageReceived(message); |
||||
} |
||||
|
||||
List<MessageListener> list = individualMessageListeners.get(message.getClass()); |
||||
if (list == null) return; |
||||
|
||||
for (MessageListener listener : list) { |
||||
listener.messageReceived(message); |
||||
} |
||||
} |
||||
|
||||
protected void fireMessageSent(Message message) { |
||||
for (MessageListener listener : messageListeners) { |
||||
listener.messageSent(message); |
||||
} |
||||
|
||||
List<MessageListener> list = individualMessageListeners.get(message.getClass()); |
||||
if (list == null) return; |
||||
|
||||
for (MessageListener listener : list) { |
||||
listener.messageSent(message); |
||||
} |
||||
} |
||||
|
||||
protected void fireObjectReceived(Object data) { |
||||
for (MessageListener listener : messageListeners) { |
||||
listener.objectReceived(data); |
||||
} |
||||
|
||||
if (data == null) return; |
||||
|
||||
List<MessageListener> list = individualMessageListeners.get(data.getClass()); |
||||
if (list == null) return; |
||||
|
||||
for (MessageListener listener : list) { |
||||
listener.objectReceived(data); |
||||
} |
||||
} |
||||
|
||||
protected void fireObjectSent(Object data) { |
||||
for (MessageListener listener : messageListeners) { |
||||
listener.objectSent(data); |
||||
} |
||||
|
||||
List<MessageListener> list = individualMessageListeners.get(data.getClass()); |
||||
if (list == null) return; |
||||
|
||||
for (MessageListener listener : list) { |
||||
listener.objectSent(data); |
||||
} |
||||
} |
||||
|
||||
protected void fireClientConnected(Client client) { |
||||
for (ConnectionListener listener : connectionListeners) { |
||||
listener.clientConnected(client); |
||||
} |
||||
} |
||||
|
||||
protected void fireClientDisconnected(Client client) { |
||||
for (ConnectionListener listener : connectionListeners) { |
||||
listener.clientDisconnected(client); |
||||
} |
||||
} |
||||
|
||||
|
||||
} |
@ -1,108 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.connection; |
||||
|
||||
import com.jme3.system.JmeSystem; |
||||
import java.io.IOException; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
/** |
||||
* The connection runnable takes the UDP and TCP connections |
||||
* and updates them accordingly. |
||||
* |
||||
* @author Lars Wesselius |
||||
*/ |
||||
public class ConnectionRunnable implements Runnable { |
||||
protected Logger log = Logger.getLogger(Server.class.getName()); |
||||
|
||||
private TCPConnection tcp; |
||||
private UDPConnection udp; |
||||
private int delay = 2; |
||||
private boolean keepAlive = true; |
||||
private boolean alive = true; |
||||
|
||||
public ConnectionRunnable(TCPConnection tcp, UDPConnection udp, int delay) { |
||||
this.tcp = tcp; |
||||
this.udp = udp; |
||||
this.delay = delay; |
||||
} |
||||
|
||||
public ConnectionRunnable(TCPConnection tcp, UDPConnection udp) { |
||||
this.tcp = tcp; |
||||
this.udp = udp; |
||||
} |
||||
|
||||
public void setKeepAlive(boolean keepAlive) { |
||||
this.keepAlive = keepAlive; |
||||
} |
||||
|
||||
public boolean isRunning() { |
||||
return alive; |
||||
} |
||||
|
||||
public void run() { |
||||
if (!JmeSystem.isLowPermissions()){ |
||||
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { |
||||
public void uncaughtException(Thread thread, Throwable thrown) { |
||||
log.log(Level.SEVERE, "Uncaught exception thrown in "+thread.toString(), thrown); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
while (keepAlive) |
||||
{ |
||||
// Run while one of the connections is still live.
|
||||
tcp.run(); |
||||
udp.run(); |
||||
|
||||
if (delay > 0) { |
||||
try { |
||||
Thread.sleep(delay); |
||||
} catch (InterruptedException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} else { Thread.yield(); } |
||||
} |
||||
try |
||||
{ |
||||
tcp.cleanup(); |
||||
udp.cleanup(); |
||||
} catch (IOException e) { |
||||
log.log(Level.WARNING, "[???][???] Could not clean up the connection.", e); |
||||
return; |
||||
} |
||||
alive = false; |
||||
log.log(Level.FINE, "[???][???] Cleaned up TCP/UDP."); |
||||
} |
||||
} |
@ -1,54 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.connection; |
||||
|
||||
import java.net.InetSocketAddress; |
||||
|
||||
/** |
||||
* A connection filter that can be used by the dev to filter |
||||
* connections based on <code>InetAddress</code>es. |
||||
* |
||||
* @author Lars Wesselius |
||||
*/ |
||||
public interface ConnectorFilter { |
||||
|
||||
/** |
||||
* Filter a connection based on <code>InetAddress</code>. This is called |
||||
* every time a client, or <code>Client</code>, connects to the server. |
||||
* |
||||
* @param address The address. |
||||
* @return A null string if the connection should be accepted without problems. |
||||
* A non null value indicates the reason of why the client should be dropped. |
||||
*/ |
||||
public String filterConnector(InetSocketAddress address); |
||||
} |
@ -1,336 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.connection; |
||||
|
||||
import javax.net.ssl.*; |
||||
import java.io.File; |
||||
import java.io.FileInputStream; |
||||
import java.io.IOException; |
||||
import java.net.SocketAddress; |
||||
import java.nio.ByteBuffer; |
||||
import java.nio.channels.SelectableChannel; |
||||
import java.nio.channels.SelectionKey; |
||||
import java.nio.channels.SocketChannel; |
||||
import java.security.KeyStore; |
||||
import java.util.ArrayList; |
||||
import java.util.logging.Level; |
||||
|
||||
/** |
||||
* The SSLTCPConnection. Handles all SSL traffic for both client |
||||
* and server. Please do not use this class, as it does not work. |
||||
* Replacement is custom encryption over TCP or UDP, without using SSL. |
||||
* |
||||
* @author Lars Wesselius |
||||
*/ |
||||
public class SSLTCPConnection extends TCPConnection { |
||||
|
||||
// Incoming data. Encrypted.
|
||||
protected ByteBuffer incDataEncrypted; |
||||
|
||||
// Incoming data. Decrypted.
|
||||
protected ByteBuffer incDataDecrypted; |
||||
|
||||
// Outgoing data. Encrypted.
|
||||
protected ByteBuffer outDataEncrypted; |
||||
|
||||
// Used for operations that don't consume any data.
|
||||
protected ByteBuffer dummy; |
||||
|
||||
protected SSLEngine sslEngine; |
||||
protected boolean initialHandshake; |
||||
protected SSLEngineResult.HandshakeStatus |
||||
handshakeStatus; |
||||
protected SSLEngineResult.Status |
||||
status; |
||||
|
||||
protected ArrayList<Client> |
||||
handshakingConnectors = new ArrayList<Client>(); |
||||
|
||||
public SSLTCPConnection(String name) { |
||||
label = name; |
||||
createSSLEngine(); |
||||
|
||||
|
||||
SSLSession session = sslEngine.getSession(); |
||||
|
||||
incDataDecrypted = ByteBuffer.allocateDirect(session.getApplicationBufferSize()); |
||||
incDataEncrypted = ByteBuffer.allocateDirect(session.getPacketBufferSize()); |
||||
outDataEncrypted = ByteBuffer.allocateDirect(session.getPacketBufferSize()); |
||||
|
||||
incDataEncrypted.position(incDataEncrypted.limit()); |
||||
outDataEncrypted.position(outDataEncrypted.limit()); |
||||
dummy = ByteBuffer.allocate(0); |
||||
} |
||||
|
||||
private void createSSLEngine() { |
||||
try |
||||
{ |
||||
KeyStore ks = KeyStore.getInstance("JKS"); |
||||
File kf = new File("keystore"); |
||||
ks.load(new FileInputStream(kf), "lollercopter".toCharArray()); |
||||
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); |
||||
kmf.init(ks, "lollercopter".toCharArray()); |
||||
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); |
||||
tmf.init(ks); |
||||
|
||||
TrustManager[] trustAllCerts = new TrustManager[] |
||||
{ |
||||
new X509TrustManager() { |
||||
public java.security.cert.X509Certificate[] getAcceptedIssuers() { |
||||
return null; |
||||
} |
||||
|
||||
public void checkClientTrusted( |
||||
java.security.cert.X509Certificate[] certs, String authType) { |
||||
} |
||||
|
||||
public void checkServerTrusted( |
||||
java.security.cert.X509Certificate[] certs, String authType) { |
||||
} |
||||
} |
||||
}; |
||||
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS"); |
||||
sslContext.init(kmf.getKeyManagers(), trustAllCerts, null); |
||||
|
||||
sslEngine = sslContext.createSSLEngine(); |
||||
} catch (Exception e) { |
||||
log.log(Level.SEVERE, "[{0}][TCP] Could not create SSL engine: {1}", new Object[]{label, e.getMessage()}); |
||||
} |
||||
} |
||||
|
||||
private void doHandshake(SocketChannel channel) throws IOException { |
||||
while (true) { |
||||
SSLEngineResult result; |
||||
log.log(Level.FINEST, "[{0}][TCP] Handshake Status is now {1}.", new Object[]{label, handshakeStatus}); |
||||
switch (handshakeStatus) { |
||||
case NOT_HANDSHAKING: |
||||
log.log(Level.SEVERE, "[{0}][TCP] We're doing a handshake while we're not handshaking.", label); |
||||
break; |
||||
|
||||
case FINISHED: |
||||
initialHandshake = false; |
||||
channel.keyFor(selector).interestOps(SelectionKey.OP_READ); |
||||
|
||||
return; |
||||
|
||||
case NEED_TASK: |
||||
// TODO: Run this task in another thread or something.
|
||||
Runnable task; |
||||
while ((task = sslEngine.getDelegatedTask()) != null) { |
||||
task.run(); |
||||
} |
||||
handshakeStatus = sslEngine.getHandshakeStatus(); |
||||
break; |
||||
|
||||
case NEED_UNWRAP: |
||||
readAndUnwrap(channel); |
||||
|
||||
if (initialHandshake && status == SSLEngineResult.Status.BUFFER_UNDERFLOW) { |
||||
channel.keyFor(selector).interestOps(SelectionKey.OP_READ); |
||||
return; |
||||
} |
||||
|
||||
break; |
||||
|
||||
case NEED_WRAP: |
||||
|
||||
if (outDataEncrypted.hasRemaining()) { |
||||
log.log(Level.FINE, "[{0}][TCP] We found data that should be written out.", label); |
||||
return; |
||||
} |
||||
|
||||
// Prepare to write
|
||||
outDataEncrypted.clear(); |
||||
result = sslEngine.wrap(dummy, outDataEncrypted); |
||||
log.log(Level.FINEST, "[{0}][TCP] Wrapping result: {1}.", new Object[]{label, result}); |
||||
|
||||
if (result.bytesProduced() == 0) log.log(Level.SEVERE, "[{0}][TCP] No net data produced during wrap.", label); |
||||
if (result.bytesConsumed() != 0) log.log(Level.SEVERE, "[{0}][TCP] App data consumed during handshake wrap.", label); |
||||
handshakeStatus = result.getHandshakeStatus(); |
||||
outDataEncrypted.flip(); |
||||
|
||||
// Now send the data and come back here only when
|
||||
// the data is all sent
|
||||
System.out.println("WRITING TO: " + channel + " : " + channel.socket()); |
||||
if (!flushData(channel)) { |
||||
// There is data left to be send. Wait for it
|
||||
return; |
||||
} |
||||
break; |
||||
|
||||
} |
||||
} |
||||
} |
||||
|
||||
public void connect(SocketAddress address) throws IOException { |
||||
super.connect(address); |
||||
} |
||||
|
||||
public void bind(SocketAddress address) throws IOException { |
||||
super.bind(address); |
||||
|
||||
sslEngine.setUseClientMode(false); |
||||
sslEngine.setNeedClientAuth(false); |
||||
} |
||||
|
||||
public void connect(SelectableChannel channel) throws IOException { |
||||
super.connect(channel); |
||||
initialHandshake = true; |
||||
sslEngine.setUseClientMode(true); |
||||
sslEngine.beginHandshake(); |
||||
socketChannel.keyFor(selector).interestOps(SelectionKey.OP_WRITE); |
||||
handshakeStatus = sslEngine.getHandshakeStatus(); |
||||
doHandshake(socketChannel); |
||||
} |
||||
|
||||
public void accept(SelectableChannel channel) throws IOException { |
||||
super.accept(channel); |
||||
|
||||
Client con = connections.get(connections.size() - 1); |
||||
handshakingConnectors.add(con); |
||||
//con.getChannel().keyFor(selector).interestOps(SelectionKey.OP_WRITE);
|
||||
|
||||
initialHandshake = true; |
||||
sslEngine.beginHandshake(); |
||||
handshakeStatus = sslEngine.getHandshakeStatus(); |
||||
doHandshake(con.getSocketChannel()); |
||||
} |
||||
|
||||
public void read(SelectableChannel channel) throws IOException { |
||||
if (initialHandshake) { |
||||
doHandshake((SocketChannel)channel); |
||||
return; |
||||
} |
||||
super.read(channel); |
||||
} |
||||
|
||||
public void readAndUnwrap(SocketChannel channel) throws IOException { |
||||
incDataEncrypted.flip(); |
||||
int bytesRead = channel.read(incDataEncrypted); |
||||
|
||||
if (bytesRead == 0) { |
||||
System.out.println("BUFFER INFO: " + incDataEncrypted); |
||||
} |
||||
if (bytesRead == -1) { |
||||
log.log(Level.FINE, "[{0}][TCP] -1 bytes read, closing stream.", new Object[]{label, bytesRead}); |
||||
return; |
||||
} |
||||
log.log(Level.FINE, "[{0}][TCP] Read {1} bytes.", new Object[]{label, bytesRead}); |
||||
|
||||
incDataDecrypted.clear(); |
||||
incDataEncrypted.flip(); |
||||
|
||||
SSLEngineResult result; |
||||
do { |
||||
result = sslEngine.unwrap(incDataEncrypted, incDataDecrypted); |
||||
log.log(Level.FINE, "[{0}][TCP] Unwrap result: {1}.", new Object[]{label, result}); |
||||
} while (result.getStatus() == SSLEngineResult.Status.OK && |
||||
result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP && |
||||
result.bytesProduced() == 0); |
||||
|
||||
// We could have finished the handshake.
|
||||
if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED) { |
||||
initialHandshake = false; |
||||
channel.keyFor(selector).interestOps(SelectionKey.OP_READ); |
||||
} |
||||
|
||||
// Check if we unwrapped everything there is to unwrap.
|
||||
if (incDataDecrypted.position() == 0 && |
||||
result.getStatus() == SSLEngineResult.Status.OK && incDataEncrypted.hasRemaining()) { |
||||
result = sslEngine.unwrap(incDataEncrypted, incDataDecrypted); |
||||
log.log(Level.FINE, "[{0}][TCP] Unwrap result: {1}.", new Object[]{label, result}); |
||||
} |
||||
|
||||
// Update statuses
|
||||
status = result.getStatus(); |
||||
handshakeStatus = result.getHandshakeStatus(); |
||||
|
||||
incDataEncrypted.compact(); |
||||
incDataDecrypted.flip(); |
||||
|
||||
if (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_TASK || |
||||
handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP || |
||||
handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED) |
||||
{ |
||||
log.log(Level.FINE, "[{0}][TCP] Rehandshaking..", label); |
||||
doHandshake(channel); |
||||
} |
||||
|
||||
} |
||||
|
||||
public void send(Object object) throws IOException { |
||||
super.sendObject(object); |
||||
} |
||||
|
||||
public void send(SocketChannel channel, Object object) throws IOException { |
||||
super.send(channel, object); |
||||
} |
||||
|
||||
public void write(SelectableChannel channel) throws IOException { |
||||
//super.write(channel);
|
||||
SocketChannel socketChannel = (SocketChannel)channel; |
||||
|
||||
if (flushData(socketChannel)) { |
||||
if (initialHandshake) { |
||||
doHandshake(socketChannel); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private boolean flushData(SocketChannel channel) throws IOException { |
||||
int written = 0; |
||||
try { |
||||
///
|
||||
while (outDataEncrypted.hasRemaining()) { |
||||
written += channel.write(outDataEncrypted); |
||||
} |
||||
} catch (IOException ioe) { |
||||
outDataEncrypted.position(outDataEncrypted.limit()); |
||||
throw ioe; |
||||
} |
||||
|
||||
log.log(Level.FINE, "[{0}][TCP] Wrote {1} bytes to {2}.", new Object[]{label, written, channel.socket().getRemoteSocketAddress()}); |
||||
if (outDataEncrypted.hasRemaining()) { |
||||
SelectionKey key = channel.keyFor(selector); |
||||
key.interestOps(SelectionKey.OP_WRITE); |
||||
return false; |
||||
} else { |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
|
||||
} |
@ -1,462 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.connection; |
||||
|
||||
import com.jme3.network.events.ConnectionListener; |
||||
import com.jme3.network.events.MessageListener; |
||||
import com.jme3.network.message.ClientRegistrationMessage; |
||||
import com.jme3.network.message.DisconnectMessage; |
||||
import com.jme3.network.message.Message; |
||||
import com.jme3.network.service.ServiceManager; |
||||
|
||||
import java.io.IOException; |
||||
import java.net.InetSocketAddress; |
||||
import java.net.SocketAddress; |
||||
import java.util.List; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
/** |
||||
* The class where your SpiderMonkey adventures start. The server class
|
||||
* manages the TCP and UDP servers. |
||||
* |
||||
* Using the constructors where you either provide ports or the instances, |
||||
* they will bind automatically. If you do not want this to happen, use the |
||||
* no arg constructor, and then call bind later on. |
||||
* |
||||
* @author Lars Wesselius |
||||
* @deprecated Use {@link com.jme3.network.Server} from {@link com.jme3.network.Network} instead. |
||||
*/ |
||||
@Deprecated |
||||
public class Server extends ServiceManager { |
||||
protected Logger log = Logger.getLogger(Server.class.getName()); |
||||
|
||||
protected static int serverIDCounter = 0; |
||||
protected TCPConnection tcp = null; |
||||
protected UDPConnection udp = null; |
||||
|
||||
protected String label; |
||||
protected int serverID; |
||||
protected boolean isBound = false; |
||||
|
||||
protected ConnectionRunnable |
||||
thread; |
||||
|
||||
protected SocketAddress lastUDPAddress; |
||||
protected SocketAddress lastTCPAddress; |
||||
|
||||
protected ClientManager clientManager = new ClientManager(); |
||||
|
||||
protected MessageListener listener = new ServerMessageObserver(); |
||||
|
||||
/** |
||||
* Default constructor. Sets the label to |
||||
* <code>Server#[serverID]</code> |
||||
*/ |
||||
public Server() { |
||||
super(ServiceManager.SERVER); |
||||
serverID = ++serverIDCounter; |
||||
this.label = "Server#" + serverID; |
||||
} |
||||
|
||||
/** |
||||
* Constructor providing custom instances of the servers and its addresses. |
||||
* |
||||
* @param tcp The TCPConnection instance to manage. |
||||
* @param udp The UDPConnection instance to manage. |
||||
* @param tcpAddress The TCP address to bind to. |
||||
* @param udpAddress The UDP address to bind to. |
||||
* @throws IOException When a bind error has occurred. |
||||
*/ |
||||
public Server(TCPConnection tcp, UDPConnection udp, SocketAddress tcpAddress, SocketAddress udpAddress) throws IOException { |
||||
this(); |
||||
|
||||
this.tcp = tcp; |
||||
tcp.bind(tcpAddress); |
||||
lastTCPAddress = tcpAddress; |
||||
|
||||
this.udp = udp; |
||||
udp.bind(udpAddress); |
||||
lastUDPAddress = udpAddress; |
||||
isBound = true; |
||||
|
||||
registerInternalListeners(); |
||||
} |
||||
|
||||
/** |
||||
* Constructor for providing a TCP server instance. UDP will be disabled. |
||||
* |
||||
* @param tcp The TCPConnection instance. |
||||
* @param tcpAddress The address to bind to. |
||||
* @throws IOException When a binding error occurs. |
||||
*/ |
||||
public Server(TCPConnection tcp, SocketAddress tcpAddress) throws IOException { |
||||
this(); |
||||
|
||||
this.tcp = tcp; |
||||
tcp.bind(tcpAddress); |
||||
lastTCPAddress = tcpAddress; |
||||
isBound = true; |
||||
|
||||
registerInternalListeners(); |
||||
} |
||||
|
||||
/** |
||||
* Constructor for providing a UDP server instance. TCP will be disabled. |
||||
* |
||||
* @param udp The UDP server instance. |
||||
* @param udpAddress The address to bind to. |
||||
* @throws IOException When a binding error occurs. |
||||
*/ |
||||
public Server(UDPConnection udp, SocketAddress udpAddress) throws IOException { |
||||
this(); |
||||
|
||||
this.udp = udp; |
||||
udp.bind(udpAddress); |
||||
lastUDPAddress = udpAddress; |
||||
isBound = true; |
||||
|
||||
registerInternalListeners(); |
||||
} |
||||
|
||||
/** |
||||
* Simple constructor for providing TCP port and UDP port. Will bind using on |
||||
* all interfaces, on given ports. |
||||
* |
||||
* @param tcpPort The TCP port to use. |
||||
* @param udpPort The UDP port to use. |
||||
* @throws IOException When a binding error occurs. |
||||
* @deprecated Call createServer() on {@link com.jme3.network.Network} instead. |
||||
*/ |
||||
@Deprecated |
||||
public Server(int tcpPort, int udpPort) throws IOException { |
||||
this(); |
||||
|
||||
tcp = new TCPConnection(label); |
||||
|
||||
lastTCPAddress = new InetSocketAddress(tcpPort); |
||||
tcp.bind(lastTCPAddress); |
||||
|
||||
lastUDPAddress = new InetSocketAddress(udpPort); |
||||
udp = new UDPConnection(label); |
||||
udp.bind(lastUDPAddress); |
||||
isBound = true; |
||||
|
||||
registerInternalListeners(); |
||||
} |
||||
|
||||
private void registerInternalListeners() { |
||||
if (tcp != null) { |
||||
tcp.addMessageListener(DisconnectMessage.class, listener); |
||||
tcp.addMessageListener(ClientRegistrationMessage.class, clientManager); |
||||
tcp.addConnectionListener(clientManager); |
||||
} |
||||
if (udp != null) { |
||||
udp.addMessageListener(DisconnectMessage.class, listener); |
||||
udp.addMessageListener(ClientRegistrationMessage.class, clientManager); |
||||
udp.addConnectionListener(clientManager); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Bind method for when the no arg constructor was used. |
||||
* |
||||
* @param tcpPort The TCP port to use. To turn off, use -1. |
||||
* @param udpPort The UDP port to use. To turn off, use -1. |
||||
* @throws IllegalArgumentException When an illegal argument was given. |
||||
* @throws java.io.IOException When a binding error occurs. |
||||
*/ |
||||
public void bind(int tcpPort, int udpPort) throws IllegalArgumentException, IOException { |
||||
if (tcpPort == -1 && udpPort == -1) throw new IllegalArgumentException("No point in binding when you want to turn both the connections off."); |
||||
|
||||
if (tcpPort != -1) { |
||||
lastTCPAddress = new InetSocketAddress(tcpPort); |
||||
tcp.bind(lastTCPAddress); |
||||
} |
||||
if (udpPort != -1) { |
||||
lastUDPAddress = new InetSocketAddress(udpPort); |
||||
udp.bind(lastUDPAddress); |
||||
} |
||||
registerInternalListeners(); |
||||
isBound = true; |
||||
} |
||||
|
||||
/** |
||||
* Broadcast a message. |
||||
* |
||||
* @param message The message to broadcast. |
||||
* @throws IOException When a writing error occurs. |
||||
*/ |
||||
public void broadcast(Message message) throws IOException { |
||||
if (!isBound) throw new IOException("Not bound yet. Use bind() first."); |
||||
if (message.isReliable()) { |
||||
if (tcp == null) throw new IOException("No TCP server."); |
||||
tcp.sendObject(message); |
||||
} else { |
||||
if (udp == null) throw new IOException("No UDP server."); |
||||
udp.sendObject(message); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Broadcast a message, except to the given client. |
||||
* |
||||
* @param except The client to refrain from sending the message to. |
||||
* @param message The message to send. |
||||
* @throws IOException When a writing error occurs. |
||||
*/ |
||||
public void broadcastExcept(Client except, Message message) throws IOException { |
||||
if (!isBound) throw new IOException("Not bound yet. Use bind() first."); |
||||
|
||||
// We don't have to check for reliable or not here, since client.send does that.
|
||||
for (Client con : clientManager.getConnectors()) { |
||||
if (con == except) continue; |
||||
con.send(message); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Start this server. |
||||
* |
||||
* @throws IOException When an error occurs. |
||||
*/ |
||||
public void start() throws IOException { |
||||
if (!isBound) { |
||||
tcp.bind(lastTCPAddress); |
||||
udp.bind(lastUDPAddress); |
||||
} |
||||
new Thread(thread = new ConnectionRunnable(tcp, udp)).start(); |
||||
log.log(Level.INFO, "[{0}][???] Started server.", label); |
||||
} |
||||
|
||||
/** |
||||
* Start this server with given sleep time. Higher sleep times may affect the system's response time |
||||
* negatively, whereas lower values may increase CPU load. Use only when you're certain. |
||||
* |
||||
* @param sleep The sleep time. |
||||
* @throws IOException When an error occurs. |
||||
*/ |
||||
public void start(int sleep) throws IOException { |
||||
if (!isBound) { |
||||
tcp.bind(lastTCPAddress); |
||||
udp.bind(lastUDPAddress); |
||||
} |
||||
new Thread(thread = new ConnectionRunnable(tcp, udp, sleep)).start(); |
||||
log.log(Level.INFO, "[{0}][???] Started server.", label); |
||||
} |
||||
|
||||
/** |
||||
* Stop this server. Note that it kicks all clients so that they can |
||||
* gracefully quit. |
||||
* |
||||
* @throws IOException When a writing error occurs. |
||||
*/ |
||||
public void stop() throws IOException { |
||||
stop(new DisconnectMessage()); |
||||
} |
||||
|
||||
/** |
||||
* Stops the server with custom message. |
||||
* |
||||
* @throws IOException When a writing error occurs. |
||||
*/ |
||||
public void stop(DisconnectMessage message) throws IOException { |
||||
log.log(Level.INFO, "[{0}][???] Server is shutting down..", label); |
||||
if (message.getReason() == null) { |
||||
message.setReason("Server shut down."); |
||||
} |
||||
if (message.getType() == null) { |
||||
message.setType(DisconnectMessage.KICK); |
||||
} |
||||
message.setReliable(true); |
||||
|
||||
broadcast(message); |
||||
|
||||
for (Client client : getConnectors()) { |
||||
tcp.addToDisconnectionQueue(client); |
||||
} |
||||
|
||||
tcp.selector.wakeup(); |
||||
log.log(Level.FINE, "[{0}][???] Sent disconnection messages to all clients.", label); |
||||
|
||||
thread.setKeepAlive(false); |
||||
thread = null; |
||||
log.log(Level.INFO, "[{0}][???] Server shut down.", label); |
||||
isBound = false; |
||||
} |
||||
|
||||
public boolean isRunning() { |
||||
return thread != null && thread.isRunning(); |
||||
} |
||||
|
||||
public int getServerID() { |
||||
return serverID; |
||||
} |
||||
|
||||
public void setLabel(String label) { |
||||
this.label = label; |
||||
} |
||||
|
||||
public String toString() { |
||||
return label; |
||||
} |
||||
|
||||
/////////////////////////////// Connection management //////////////////////////
|
||||
|
||||
/** |
||||
* Get all the connectors for the TCP connection. |
||||
* |
||||
* @return A unmodifiable list with the connectors. |
||||
*/ |
||||
public List<Client> getTCPConnectors() { |
||||
if (tcp != null) return tcp.getConnectors(); |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* Get all the connectors for the UDP connection. |
||||
* |
||||
* @return A unmodifiable list with the connectors. |
||||
*/ |
||||
public List<Client> getUDPConnectors() { |
||||
if (udp != null) return udp.getConnectors(); |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* Get the combined connectors, meaning TCP and UDP are combined into one client. You should |
||||
* generally use this for clients. |
||||
* |
||||
* @return A unmodifiable list with the connectors. |
||||
*/ |
||||
public List<Client> getConnectors() { |
||||
return clientManager.getConnectors(); |
||||
} |
||||
|
||||
/** |
||||
* Get a specific client based on the provided clientID. |
||||
* @param clientID The clientID identifying the client requested. |
||||
* @return The located client or null if the client was not on the list. |
||||
*/ |
||||
public Client getClientByID(int clientID) { |
||||
Client c = clientManager.getClientByClientID(clientID); |
||||
return c; |
||||
} |
||||
|
||||
/////////////////////////////// Connector filters //////////////////////////////
|
||||
|
||||
public void addConnectorFilter(ConnectorFilter filter) { |
||||
if (tcp != null) tcp.addConnectorFilter(filter); |
||||
if (udp != null) udp.addConnectorFilter(filter); |
||||
} |
||||
|
||||
public void removeConnectorFilter(ConnectorFilter filter) { |
||||
if (tcp != null) tcp.removeConnectorFilter(filter); |
||||
if (udp != null) udp.removeConnectorFilter(filter); |
||||
} |
||||
|
||||
/////////////////////////////// Listener related ///////////////////////////////
|
||||
|
||||
public void addLocalConnectionListener(ConnectionListener listener) { |
||||
if (tcp != null) tcp.addConnectionListener(listener); |
||||
if (udp != null) udp.addConnectionListener(listener); |
||||
} |
||||
|
||||
public void removeLocalConnectionListener(ConnectionListener listener) { |
||||
if (tcp != null) tcp.removeConnectionListener(listener); |
||||
if (udp != null) udp.removeConnectionListener(listener); |
||||
} |
||||
|
||||
public void addConnectionListener(ConnectionListener listener) { |
||||
clientManager.addConnectionListener(listener); |
||||
} |
||||
|
||||
public void removeConnectionListener(ConnectionListener listener) { |
||||
clientManager.removeConnectionListener(listener); |
||||
} |
||||
|
||||
public void addMessageListener(MessageListener listener) { |
||||
if (tcp != null) tcp.addMessageListener(listener); |
||||
if (udp != null) udp.addMessageListener(listener); |
||||
} |
||||
|
||||
public void addMessageListener(MessageListener listener, Class... classes) { |
||||
for (Class c : classes) { |
||||
if (tcp != null) tcp.addMessageListener(c, listener); |
||||
if (udp != null) udp.addMessageListener(c, listener); |
||||
} |
||||
} |
||||
|
||||
public void removeMessageListener(MessageListener listener) { |
||||
if (tcp != null) tcp.removeMessageListener(listener); |
||||
if (udp != null) udp.removeMessageListener(listener); |
||||
} |
||||
|
||||
public void removeMessageListener(MessageListener listener, Class... classes) { |
||||
for (Class c : classes) { |
||||
if (tcp != null) tcp.removeMessageListener(c, listener); |
||||
if (udp != null) udp.removeMessageListener(c, listener); |
||||
} |
||||
} |
||||
|
||||
protected class ServerMessageObserver implements MessageListener { |
||||
|
||||
public void messageReceived(Message message) { |
||||
// Right now, this is definitely a DisconnectMessage.
|
||||
DisconnectMessage dcMessage = (DisconnectMessage)message; |
||||
Client client = dcMessage.getClient(); |
||||
|
||||
if (clientManager.isClientConnected(client)) { |
||||
log.log(Level.INFO, "[{0}][???] Client {1} disconnected ({2}: {3}).", new Object[]{ |
||||
label, |
||||
client, |
||||
dcMessage.getType(), |
||||
(dcMessage.getReason() != null) ? dcMessage.getReason() : "No description" |
||||
}); |
||||
} |
||||
dcMessage.getConnection().addToDisconnectionQueue(client); |
||||
} |
||||
|
||||
public void messageSent(Message message) { |
||||
|
||||
} |
||||
|
||||
public void objectReceived(Object object) { |
||||
|
||||
} |
||||
|
||||
public void objectSent(Object object) { |
||||
|
||||
} |
||||
} |
||||
} |
@ -1,398 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.connection; |
||||
|
||||
import com.jme3.network.message.Message; |
||||
import com.jme3.network.queue.MessageQueue; |
||||
import com.jme3.network.serializing.Serializer; |
||||
import java.io.IOException; |
||||
import java.net.InetSocketAddress; |
||||
import java.net.SocketAddress; |
||||
import java.nio.BufferOverflowException; |
||||
import java.nio.BufferUnderflowException; |
||||
import java.nio.ByteBuffer; |
||||
import java.nio.channels.SelectableChannel; |
||||
import java.nio.channels.SelectionKey; |
||||
import java.nio.channels.ServerSocketChannel; |
||||
import java.nio.channels.SocketChannel; |
||||
import java.util.Iterator; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.Map; |
||||
import java.util.logging.Level; |
||||
|
||||
/** |
||||
* The <code>TCPConnection</code> handles all traffic regarding TCP client and server. |
||||
* |
||||
* @author Lars Wesselius |
||||
* @see Connection |
||||
*/ |
||||
public class TCPConnection extends Connection { |
||||
protected SocketChannel socketChannel; |
||||
protected ServerSocketChannel serverSocketChannel; |
||||
|
||||
protected ByteBuffer readBuffer; |
||||
protected ByteBuffer writeBuffer; |
||||
protected ByteBuffer tempWriteBuffer; |
||||
|
||||
protected final Object writeLock = new Object(); |
||||
|
||||
private int objectLength = 0; |
||||
|
||||
public TCPConnection(String name) |
||||
{ |
||||
label = name; |
||||
|
||||
readBuffer = ByteBuffer.allocateDirect(16228); |
||||
writeBuffer = ByteBuffer.allocateDirect(16228); |
||||
tempWriteBuffer = ByteBuffer.allocateDirect(16228); |
||||
} |
||||
|
||||
public TCPConnection() { } |
||||
|
||||
public void connect(SocketAddress address) throws IOException { |
||||
socketChannel = SocketChannel.open(); |
||||
socketChannel.socket().setTcpNoDelay(true); |
||||
|
||||
socketChannel.configureBlocking(false); |
||||
socketChannel.connect(address); |
||||
socketChannel.register(selector, SelectionKey.OP_CONNECT).attach(this); |
||||
log.log(Level.INFO, "[{1}][TCP] Connecting to {0}", new Object[]{address, label}); |
||||
} |
||||
|
||||
public void bind(SocketAddress address) throws IOException { |
||||
serverSocketChannel = selector.provider().openServerSocketChannel(); |
||||
serverSocketChannel.socket().bind(address); |
||||
serverSocketChannel.configureBlocking(false); |
||||
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); |
||||
log.log(Level.INFO, "[{1}][TCP] Bound to {0}", new Object[]{address, label}); |
||||
} |
||||
|
||||
public void connect(SelectableChannel channel) throws IOException { |
||||
((SocketChannel)channel).finishConnect(); |
||||
socketChannel.keyFor(selector).interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); |
||||
fireClientConnected(null); |
||||
log.log(Level.INFO, "[{0}][TCP] Connection succeeded.", label); |
||||
} |
||||
|
||||
public void accept(SelectableChannel channel) throws IOException { |
||||
SocketChannel socketChannel = ((ServerSocketChannel)channel).accept(); |
||||
|
||||
String reason = shouldFilterConnector((InetSocketAddress)socketChannel.socket().getRemoteSocketAddress()); |
||||
if (reason != null) { |
||||
log.log(Level.INFO, "[{2}][TCP] Client with address {0} got filtered with reason: {1}", new Object[]{(InetSocketAddress)socketChannel.socket().getRemoteSocketAddress(), reason, label}); |
||||
socketChannel.close(); |
||||
return; |
||||
} |
||||
|
||||
socketChannel.configureBlocking(false); |
||||
socketChannel.socket().setTcpNoDelay(true); |
||||
|
||||
Client con = new Client(true); |
||||
con.setTCPConnection(this); |
||||
con.setSocketChannel(socketChannel); |
||||
|
||||
socketChannel.register(selector, SelectionKey.OP_READ, con); |
||||
|
||||
connections.add(con); |
||||
|
||||
log.log(Level.INFO, "[{1}][TCP] A client connected with address {0}", new Object[]{socketChannel.socket().getInetAddress(), label}); |
||||
} |
||||
|
||||
public void read(SelectableChannel channel) throws IOException { |
||||
SocketChannel socketChannel = (SocketChannel)channel; |
||||
|
||||
if (socketChannel == null) { |
||||
log.log(Level.WARNING, "[{0}][TCP] Connection was closed before we could read.", label); |
||||
return; |
||||
} |
||||
|
||||
int read = -1; |
||||
readBuffer.compact(); |
||||
try { |
||||
read = socketChannel.read(readBuffer); |
||||
} catch (IOException ioe) { |
||||
// Probably a 'remote host closed connection'.
|
||||
socketChannel.keyFor(selector).cancel(); |
||||
|
||||
if (serverSocketChannel != null) { |
||||
log.log(Level.WARNING, "[{0}][TCP] Connection was forcibly closed before we could read. Disconnected client.", label); |
||||
addToDisconnectionQueue((Client)socketChannel.keyFor(selector).attachment()); |
||||
} else { |
||||
log.log(Level.WARNING, "[{0}][TCP] Server forcibly closed connection. Disconnected.", label); |
||||
fireClientDisconnected(null); |
||||
} |
||||
} |
||||
|
||||
if (read != -1) { |
||||
log.log(Level.FINE, "[{1}][TCP] Read {0} bytes.", new Object[]{read, label}); |
||||
} |
||||
|
||||
readBuffer.flip(); |
||||
if (read == -1) { |
||||
socketChannel.keyFor(selector).cancel(); |
||||
if (serverSocketChannel != null) { |
||||
log.log(Level.WARNING, "[{0}][TCP] Connection was closed before we could read. Disconnected client.", label); |
||||
addToDisconnectionQueue((Client)socketChannel.keyFor(selector).attachment()); |
||||
} else { |
||||
log.log(Level.WARNING, "[{0}][TCP] Server closed connection. Disconnected.", label); |
||||
fireClientDisconnected(null); |
||||
} |
||||
return; |
||||
} |
||||
|
||||
// Okay, see if we can read the data length.
|
||||
while (true) { |
||||
try { |
||||
|
||||
// If we're currently not already reading an object, retrieve the length
|
||||
// of the next one.
|
||||
if (objectLength == 0) { |
||||
objectLength = readBuffer.getShort(); |
||||
} |
||||
|
||||
int pos = readBuffer.position(); |
||||
int oldLimit = readBuffer.limit(); |
||||
|
||||
int dataLength = objectLength; |
||||
if (dataLength > 0 && readBuffer.remaining() >= dataLength) { |
||||
// We can read a full object.
|
||||
if (pos + dataLength + 2 > readBuffer.capacity()) { |
||||
readBuffer.limit(readBuffer.capacity()); |
||||
} else { |
||||
readBuffer.limit(pos + dataLength + 2); |
||||
} |
||||
Object obj = Serializer.readClassAndObject(readBuffer); |
||||
readBuffer.limit(oldLimit); |
||||
objectLength = 0; |
||||
if (obj != null) { |
||||
if (obj instanceof Message) { |
||||
Message message = (Message)obj; |
||||
|
||||
Object attachment = socketChannel.keyFor(selector).attachment(); |
||||
if (attachment instanceof Client) message.setClient((Client)attachment); |
||||
message.setConnection(this); |
||||
this.fireMessageReceived(message); |
||||
} else { |
||||
this.fireObjectReceived(obj); |
||||
} |
||||
log.log(Level.FINEST, "[{0}][TCP] Read full object: {1}", new Object[]{label, obj}); |
||||
} |
||||
} else if (dataLength > readBuffer.remaining()) { |
||||
readBuffer.compact(); |
||||
int bytesRead = socketChannel.read(readBuffer); |
||||
log.log(Level.FINEST, "[{0}][TCP] Object won't fit in buffer, so read {1} more bytes in a compacted buffer.", new Object[]{label, bytesRead}); |
||||
readBuffer.flip(); |
||||
} else { |
||||
objectLength = dataLength; |
||||
} |
||||
} catch (BufferUnderflowException someEx) { |
||||
log.log(Level.FINEST, "[{0}][TCP] Done reading messages.", new Object[]{label}); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
public void sendObject(Object object) throws IOException { |
||||
if (serverSocketChannel == null) { |
||||
send(socketChannel, object) ; |
||||
} else { |
||||
for (Client connector : connections) { |
||||
send(connector.getSocketChannel(), object); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void sendObject(Client con, Object object) throws IOException { |
||||
if (object instanceof Message) ((Message)object).setClient(con); |
||||
send(con.getSocketChannel(), object); |
||||
} |
||||
|
||||
public void cleanup() throws IOException { |
||||
if (serverSocketChannel != null) { |
||||
serverSocketChannel.close(); |
||||
connections.clear(); |
||||
} else { |
||||
socketChannel.close(); |
||||
} |
||||
} |
||||
|
||||
protected void send(SocketChannel channel, Object object) throws IOException { |
||||
try { |
||||
synchronized (writeLock) { |
||||
tempWriteBuffer.clear(); |
||||
tempWriteBuffer.position(4); |
||||
Serializer.writeClassAndObject(tempWriteBuffer, object); |
||||
tempWriteBuffer.flip(); |
||||
|
||||
int dataLength = tempWriteBuffer.limit() - 4; |
||||
tempWriteBuffer.position(0); |
||||
tempWriteBuffer.putInt(dataLength); |
||||
tempWriteBuffer.position(0); |
||||
|
||||
if (dataLength > writeBuffer.capacity()) { |
||||
log.log(Level.WARNING, "[{0}][TCP] Message too big for buffer. Discarded.", label); |
||||
return; |
||||
} |
||||
|
||||
if (writeBuffer.position() > 0) { |
||||
try { |
||||
writeBuffer.put(tempWriteBuffer); |
||||
} catch (BufferOverflowException boe) { |
||||
log.log(Level.WARNING, "[{0}][TCP] Buffer overflow occurred while appending data to be sent later. " + |
||||
"Cleared the buffer, so some data may be lost.", label); |
||||
|
||||
// TODO The fix here is all wrong.
|
||||
writeBuffer.clear(); |
||||
while (tempWriteBuffer.hasRemaining()) { |
||||
if (channel.write(tempWriteBuffer) == 0) break; |
||||
|
||||
} |
||||
} |
||||
} else { |
||||
int writeLength = 0; |
||||
while (tempWriteBuffer.hasRemaining()) { |
||||
int wrote = channel.write(tempWriteBuffer); |
||||
writeLength += wrote; |
||||
if (wrote == 0) { |
||||
break; |
||||
} |
||||
} |
||||
|
||||
log.log(Level.FINE, "[{1}][TCP] Wrote {0} bytes.", new Object[]{writeLength, label}); |
||||
|
||||
try |
||||
{ |
||||
if (writeBuffer.hasRemaining()) { |
||||
writeBuffer.put(tempWriteBuffer); |
||||
channel.keyFor(selector).interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); |
||||
} else { |
||||
if (object instanceof Message) { |
||||
this.fireMessageSent((Message)object); |
||||
} else { |
||||
this.fireObjectSent(object); |
||||
} |
||||
} |
||||
} catch (BufferOverflowException boe) { |
||||
log.log(Level.WARNING, "[{0}][TCP] Buffer overflow occurred while queuing data to be sent later. " + |
||||
"Cleared the buffer, so some data may be lost. Please note that this exception occurs rarely, " + |
||||
"so if this is shown often, please check your message sizes or contact the developer.", label); |
||||
writeBuffer.clear(); |
||||
} |
||||
} |
||||
} |
||||
} catch (IOException ioe) { |
||||
// We're doing some additional handling here, since a client could be reset.
|
||||
Client client; |
||||
if (socketChannel == null) { |
||||
client = ((Message)object).getClient(); |
||||
} else { |
||||
client = (Client)socketChannel.keyFor(selector).attachment(); |
||||
} |
||||
if (client != null) { |
||||
addToDisconnectionQueue(client); |
||||
|
||||
log.log(Level.WARNING, "[{0}][TCP] Disconnected {1} because an error occurred: {2}.", new Object[]{label, client, ioe.getMessage()}); |
||||
return; |
||||
} |
||||
throw ioe; |
||||
} |
||||
} |
||||
|
||||
public synchronized void write(SelectableChannel channel) throws IOException { |
||||
SocketChannel socketChannel = (SocketChannel)channel; |
||||
Client client = (Client)socketChannel.keyFor(selector).attachment(); |
||||
MessageQueue queue = client.getMessageQueue(); |
||||
|
||||
Map<Message, Short> sizeMap = new LinkedHashMap<Message, Short>(); |
||||
for (Iterator<Message> it = queue.iterator(); it.hasNext();) { |
||||
Message message = it.next(); |
||||
if (!message.isReliable()) continue; |
||||
|
||||
int pos = writeBuffer.position(); |
||||
try { |
||||
writeBuffer.position(pos + 2); |
||||
Serializer.writeClassAndObject(writeBuffer, message); |
||||
|
||||
|
||||
short dataLength = (short)(writeBuffer.position() - pos - 2); |
||||
|
||||
writeBuffer.position(pos); |
||||
writeBuffer.putShort(dataLength); |
||||
writeBuffer.position(pos + dataLength + 2); |
||||
|
||||
sizeMap.put(message, dataLength); |
||||
|
||||
it.remove(); |
||||
} catch (Exception bfe) { |
||||
// No problem, just write the buffer and be done with it.
|
||||
writeBuffer.position(pos); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
writeBuffer.flip(); |
||||
|
||||
int written = 0; |
||||
while (writeBuffer.hasRemaining()) { |
||||
int wrote = socketChannel.write(writeBuffer); |
||||
written += wrote; |
||||
|
||||
if (wrote == 0) { |
||||
break; |
||||
} |
||||
} |
||||
|
||||
log.log(Level.FINE, "[{1}][TCP] Wrote {0} bytes.", new Object[]{written, label}); |
||||
|
||||
// Check which messages were NOT sent.
|
||||
if (writeBuffer.hasRemaining()) { |
||||
for (Iterator<Map.Entry<Message, Short>> it = sizeMap.entrySet().iterator(); it.hasNext();) { |
||||
Map.Entry<Message, Short> entry = it.next(); |
||||
|
||||
written -= entry.getValue(); |
||||
if (written > 0) { |
||||
it.remove(); |
||||
} else { |
||||
// Re add to queue.
|
||||
client.getMessageQueue().add(entry.getKey()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (queue.isEmpty()) { |
||||
channel.keyFor(selector).interestOps(SelectionKey.OP_READ); |
||||
} |
||||
writeBuffer.clear(); |
||||
} |
||||
} |
@ -1,214 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.connection; |
||||
|
||||
import com.jme3.network.message.DiscoverHostMessage; |
||||
import com.jme3.network.message.Message; |
||||
import com.jme3.network.serializing.Serializer; |
||||
import java.io.IOException; |
||||
import java.net.InetSocketAddress; |
||||
import java.net.SocketAddress; |
||||
import java.nio.ByteBuffer; |
||||
import java.nio.channels.ClosedChannelException; |
||||
import java.nio.channels.DatagramChannel; |
||||
import java.nio.channels.SelectableChannel; |
||||
import java.nio.channels.SelectionKey; |
||||
import java.util.logging.Level; |
||||
|
||||
/** |
||||
* The <code>UDPConnection</code> handles all UDP traffic. |
||||
* |
||||
* @author Lars Wesselius |
||||
*/ |
||||
public class UDPConnection extends Connection { |
||||
protected DatagramChannel datagramChannel; |
||||
|
||||
protected ByteBuffer writeBuffer; |
||||
protected ByteBuffer readBuffer; |
||||
|
||||
protected SocketAddress target = null; |
||||
|
||||
public UDPConnection(String label) { |
||||
this.label = label; |
||||
|
||||
readBuffer = ByteBuffer.allocateDirect(8192); |
||||
writeBuffer = ByteBuffer.allocateDirect(8192); |
||||
} |
||||
|
||||
public void connect(SocketAddress address) throws IOException { |
||||
datagramChannel = selector.provider().openDatagramChannel(); |
||||
datagramChannel.socket().bind(null); |
||||
datagramChannel.socket().connect(address); |
||||
datagramChannel.configureBlocking(false); |
||||
|
||||
datagramChannel.register(selector, SelectionKey.OP_READ); |
||||
log.log(Level.INFO, "[{1}][UDP] Set target to {0}", new Object[]{address, label}); |
||||
target = address; |
||||
} |
||||
|
||||
public void bind(SocketAddress address) throws IOException { |
||||
datagramChannel = selector.provider().openDatagramChannel(); |
||||
datagramChannel.socket().bind(address); |
||||
datagramChannel.configureBlocking(false); |
||||
|
||||
datagramChannel.register(selector, SelectionKey.OP_READ); |
||||
|
||||
log.log(Level.INFO, "[{1}][UDP] Bound to {0}", new Object[]{address, label}); |
||||
} |
||||
|
||||
public void connect(SelectableChannel channel) throws IOException { |
||||
// UDP is connectionless.
|
||||
} |
||||
|
||||
public void accept(SelectableChannel channel) throws IOException { |
||||
// UDP is connectionless.
|
||||
} |
||||
|
||||
public void read(SelectableChannel channel) throws IOException { |
||||
DatagramChannel socketChannel = (DatagramChannel)channel; |
||||
|
||||
InetSocketAddress address = (InetSocketAddress)datagramChannel.receive(readBuffer); |
||||
if (address == null){ |
||||
//System.out.println("Address is NULL!");
|
||||
//TODO: Fix disconnection issue
|
||||
socketChannel.close(); |
||||
|
||||
return; |
||||
} |
||||
|
||||
String reason = shouldFilterConnector(address); |
||||
if (reason != null) { |
||||
log.log(Level.INFO, "[Server][UDP] Client with address {0} got filtered with reason: {1}", new Object[]{address, reason}); |
||||
socketChannel.close(); |
||||
return; |
||||
} |
||||
|
||||
SelectionKey key = socketChannel.keyFor(selector); |
||||
if ((key.attachment() == null || ((Client)key.attachment()).getDatagramReceiver() != address) && target == null) { |
||||
Client client = new Client(true); |
||||
client.setDatagramReceiver(address); |
||||
client.setUDPConnection(this); |
||||
client.setDatagramChannel(socketChannel); |
||||
synchronized (connections){ |
||||
connections.add(client); |
||||
} |
||||
|
||||
key.attach(client); |
||||
} |
||||
|
||||
readBuffer.flip(); |
||||
|
||||
|
||||
Object object = Serializer.readClassAndObject(readBuffer); |
||||
|
||||
log.log(Level.FINE, "[{0}][UDP] Read full object: {1}", new Object[]{label, object}); |
||||
|
||||
if (object instanceof Message) { |
||||
Message message = (Message)object; |
||||
|
||||
if (message instanceof DiscoverHostMessage) { |
||||
synchronized (connections){ |
||||
connections.remove( (Client) key.attachment() ); |
||||
} |
||||
log.log(Level.FINE, "[{0}][UDP] Responded to a discover host message by {1}.", new Object[]{label, address}); |
||||
send(address, message); |
||||
return; |
||||
} |
||||
|
||||
Object attachment = socketChannel.keyFor(selector).attachment(); |
||||
if (attachment instanceof Client) message.setClient((Client)attachment); |
||||
message.setConnection(this); |
||||
this.fireMessageReceived(message); |
||||
} else { |
||||
this.fireObjectReceived(object); |
||||
} |
||||
|
||||
readBuffer.clear(); |
||||
} |
||||
|
||||
protected synchronized void send(SocketAddress dest, Object object) { |
||||
try { |
||||
Serializer.writeClassAndObject(writeBuffer, object); |
||||
writeBuffer.flip(); |
||||
|
||||
if (dest == null) |
||||
throw new NullPointerException(); |
||||
|
||||
int bytes = datagramChannel.send(writeBuffer, dest); |
||||
|
||||
if (object instanceof Message) { |
||||
this.fireMessageSent((Message)object); |
||||
} else { |
||||
this.fireObjectSent(object); |
||||
} |
||||
|
||||
log.log(Level.FINE, "[{0}][UDP] Wrote {1} bytes to {2}.", new Object[]{label, bytes, dest}); |
||||
writeBuffer.clear(); |
||||
} catch (ClosedChannelException e) { |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
|
||||
public void sendObject(Object object) throws IOException { |
||||
if (target == null) { |
||||
// This is a UDP server.
|
||||
synchronized (connections){ |
||||
for (Client connector : connections) { |
||||
send(connector.getDatagramReceiver(), object); |
||||
} |
||||
} |
||||
} else { |
||||
send(target, object); |
||||
} |
||||
} |
||||
|
||||
public void sendObject(Client client, Object object) throws IOException { |
||||
if (object instanceof Message) ((Message)object).setClient(client); |
||||
send(client.getDatagramReceiver(), object); |
||||
} |
||||
|
||||
public void cleanup() throws IOException { |
||||
datagramChannel.close(); |
||||
|
||||
if (target == null) { |
||||
synchronized (connections){ |
||||
connections.clear(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void write(SelectableChannel channel) throws IOException { |
||||
// UDP is (almost) always ready for data, so send() will do.
|
||||
} |
||||
} |
@ -1,45 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.events; |
||||
|
||||
import com.jme3.network.connection.Client; |
||||
|
||||
/** |
||||
* Server adapter for making it easier to listen for server events. |
||||
* |
||||
* @author Lars Wesselius |
||||
*/ |
||||
public class ConnectionAdapter implements ConnectionListener { |
||||
public void clientConnected(Client client) {} |
||||
public void clientDisconnected(Client client) {} |
||||
} |
@ -1,47 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.events; |
||||
|
||||
import com.jme3.network.connection.Client; |
||||
|
||||
/** |
||||
* Listener for server events. |
||||
* |
||||
* @author Lars Wesselius |
||||
* @deprecated Use {@link com.jme3.network.ConnectionListener} instead. |
||||
*/ |
||||
@Deprecated |
||||
public interface ConnectionListener { |
||||
public void clientConnected(Client client); |
||||
public void clientDisconnected(Client client); |
||||
} |
@ -1,47 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.events; |
||||
|
||||
import com.jme3.network.message.Message; |
||||
|
||||
/** |
||||
* Message adapter to make it easier to listen to message vents. |
||||
* |
||||
* @author Lars Wesselius |
||||
*/ |
||||
public class MessageAdapter implements MessageListener { |
||||
public void messageReceived(Message message) {} |
||||
public void messageSent(Message message) {} |
||||
public void objectReceived(Object object) {} |
||||
public void objectSent(Object object) {} |
||||
} |
@ -1,50 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.events; |
||||
|
||||
import com.jme3.network.message.Message; |
||||
|
||||
/** |
||||
* Listener for messages. |
||||
* |
||||
* @author Lars Wesselius |
||||
* @deprecated Use {@link com.jme3.network.MessageListener} instead. |
||||
*/ |
||||
@Deprecated |
||||
public interface MessageListener { |
||||
public void messageReceived(Message message); |
||||
public void messageSent(Message message); |
||||
|
||||
public void objectReceived(Object object); |
||||
public void objectSent(Object object); |
||||
} |
@ -1,44 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.message; |
||||
|
||||
import com.jme3.network.serializing.Serializable; |
||||
|
||||
/** |
||||
* The discover host message. |
||||
* |
||||
* @author Lars Wesselius |
||||
*/ |
||||
@Serializable() |
||||
public class DiscoverHostMessage extends Message { |
||||
} |
@ -1,96 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.message; |
||||
|
||||
import com.jme3.network.connection.Client; |
||||
import com.jme3.network.connection.Connection; |
||||
import com.jme3.network.serializing.Serializable; |
||||
|
||||
/** |
||||
* Message represents data being sent to the other side. This can be anything, |
||||
* and it will be serialized field by field. Extend this class if you wish to |
||||
* provide objects with common fields to the other side. |
||||
* |
||||
* @author Lars Wesselius |
||||
* @deprecated Message implementations should extend {@linke com.jme3.network.AbstractMessage} |
||||
* instead or use the {@linke com.jme3.network.Message} for referencing. |
||||
*/ |
||||
@Deprecated |
||||
@Serializable() |
||||
public class Message extends com.jme3.network.AbstractMessage { |
||||
// The connector this message is meant for.
|
||||
private transient Client connector; |
||||
private transient Connection connection; |
||||
private transient boolean reliable = true; |
||||
|
||||
public Message() {} |
||||
|
||||
public Message(boolean reliable) { |
||||
this.reliable = reliable; |
||||
} |
||||
|
||||
public boolean isReliable() { |
||||
return reliable; |
||||
} |
||||
|
||||
public Message setReliable(boolean reliable) { |
||||
this.reliable = reliable; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* @deprecated This method always returns null in the new API. |
||||
*/ |
||||
@Deprecated |
||||
public Client getClient() { |
||||
return connector; |
||||
} |
||||
|
||||
@Deprecated |
||||
public void setClient(Client connector) { |
||||
this.connector = connector; |
||||
} |
||||
|
||||
/** |
||||
* @deprecated This method always returns null in the new API. |
||||
*/ |
||||
@Deprecated |
||||
public Connection getConnection() { |
||||
return connection; |
||||
} |
||||
|
||||
@Deprecated |
||||
public void setConnection(Connection connection) { |
||||
this.connection = connection; |
||||
} |
||||
} |
@ -1,58 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.message; |
||||
|
||||
import com.jme3.network.serializing.Serializable; |
||||
|
||||
/** |
||||
* Stream message contains the data for the stream. |
||||
* |
||||
* @author Lars Wesselius |
||||
*/ |
||||
@Serializable() |
||||
public class StreamDataMessage extends StreamMessage { |
||||
private byte[] data; |
||||
|
||||
public StreamDataMessage() { } |
||||
|
||||
public StreamDataMessage(short id) { |
||||
super(id); |
||||
} |
||||
|
||||
public void setData(byte[] data) { |
||||
this.data = data; |
||||
} |
||||
|
||||
public byte[] getData() { return data; } |
||||
|
||||
} |
@ -1,54 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.message; |
||||
|
||||
import com.jme3.network.serializing.Serializable; |
||||
|
||||
@Serializable() |
||||
public class StreamMessage extends Message { |
||||
private short streamID; |
||||
|
||||
public StreamMessage(short id) { |
||||
streamID = id; |
||||
} |
||||
|
||||
public StreamMessage() {} |
||||
|
||||
public short getStreamID() { |
||||
return streamID; |
||||
} |
||||
|
||||
public void setStreamID(short streamID) { |
||||
this.streamID = streamID; |
||||
} |
||||
} |
@ -1,8 +0,0 @@ |
||||
package com.jme3.network.queue; |
||||
|
||||
import com.jme3.network.message.Message; |
||||
import java.util.concurrent.ConcurrentLinkedQueue; |
||||
|
||||
public class MessageQueue extends ConcurrentLinkedQueue<Message> { |
||||
|
||||
} |
@ -1,41 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.service; |
||||
|
||||
/** |
||||
* The Service interface. All services should implement this class, to provide a common way to manage service. |
||||
* |
||||
* @author Lars Wesselius |
||||
*/ |
||||
public interface Service { |
||||
} |
@ -1,96 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.service; |
||||
|
||||
import com.jme3.network.connection.Client; |
||||
import com.jme3.network.connection.Server; |
||||
import java.lang.reflect.Constructor; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
/** |
||||
* A class can extend Service Manager to support services. |
||||
* |
||||
* @author Lars Wesselius |
||||
*/ |
||||
public class ServiceManager { |
||||
private Logger log = Logger.getLogger(ServiceManager.class.getName()); |
||||
private final List<Service> services = new ArrayList<Service>(); |
||||
|
||||
private boolean client; |
||||
|
||||
public static final boolean CLIENT = true; |
||||
public static final boolean SERVER = false; |
||||
|
||||
public ServiceManager(boolean client) { |
||||
this.client = client; |
||||
} |
||||
|
||||
public <T> T getService(Class cls) { |
||||
for (Service service : services) { |
||||
if (service.getClass() == cls) return (T)service; |
||||
} |
||||
|
||||
try { |
||||
if (!Service.class.isAssignableFrom(cls)) |
||||
return null; |
||||
|
||||
Constructor ctor; |
||||
if (client) { |
||||
try { |
||||
ctor = cls.getConstructor(new Class[]{Client.class}); |
||||
} catch (NoSuchMethodException nsme) { |
||||
log.log(Level.WARNING, "[ServiceManager][???] The service {0} does not support client mode.", cls); |
||||
return null; |
||||
} |
||||
} else { |
||||
try { |
||||
ctor = cls.getConstructor(new Class[]{Server.class}); |
||||
} catch (NoSuchMethodException nsme) { |
||||
log.log(Level.WARNING, "[ServiceManager][???] The service {0} does not support server mode.", cls); |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
T inst = (T)ctor.newInstance(this); |
||||
|
||||
services.add((Service)inst); |
||||
return inst; |
||||
} catch (Exception e) { |
||||
log.log(Level.SEVERE, "[ServiceManager][???] Instantiaton of service failed.", e); |
||||
} |
||||
return null; |
||||
} |
||||
} |
@ -1,133 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.streaming; |
||||
|
||||
import com.jme3.network.connection.Client; |
||||
import com.jme3.network.events.MessageAdapter; |
||||
import com.jme3.network.message.Message; |
||||
import com.jme3.network.message.StreamDataMessage; |
||||
import com.jme3.network.message.StreamMessage; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.ArrayList; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
public class ClientStreamingService extends MessageAdapter { |
||||
private static Logger log = Logger.getLogger(StreamingService.class.getName()); |
||||
|
||||
protected ArrayList<StreamListener> |
||||
streamListeners; |
||||
|
||||
protected ArrayList<Stream> streams; |
||||
|
||||
private Client client; |
||||
|
||||
|
||||
public ClientStreamingService(Client client) { |
||||
this.client = client; |
||||
streams = new ArrayList<Stream>(); |
||||
streamListeners = new ArrayList<StreamListener>(); |
||||
client.addMessageListener(this, StreamDataMessage.class, |
||||
StreamMessage.class); |
||||
} |
||||
|
||||
// Client classes/methods //////////////////////////////////////////////////////////////
|
||||
|
||||
public void addStreamListener(StreamListener listener) { |
||||
streamListeners.add(listener); |
||||
} |
||||
|
||||
public void removeStreamListener(StreamListener listener) { |
||||
streamListeners.remove(listener); |
||||
} |
||||
|
||||
public void messageReceived(Message message) { |
||||
if (message instanceof StreamMessage && !(message instanceof StreamDataMessage)) { |
||||
// A stream was offered.
|
||||
StreamMessage msg = (StreamMessage)message; |
||||
Stream stream = getStream(msg.getStreamID()); |
||||
|
||||
if (stream != null) { |
||||
// This is a completion message.
|
||||
for (StreamListener listener : stream.getDataListeners()) { |
||||
listener.streamCompleted(msg); |
||||
} |
||||
} else { |
||||
stream = new Stream(); |
||||
stream.setMessage(msg); |
||||
boolean accept = fireStreamOffered(stream, msg); |
||||
|
||||
streams.add(stream); |
||||
if (accept) { |
||||
try { |
||||
client.send(msg); |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
} |
||||
} else if (message instanceof StreamDataMessage) { |
||||
StreamDataMessage dataMessage = (StreamDataMessage)message; |
||||
Stream stream = getStream(dataMessage.getStreamID()); |
||||
if (stream == null) { |
||||
log.log(Level.WARNING, "[StreamClient][TCP] We've received a data message even though we didn't register to the stream."); |
||||
return; |
||||
} |
||||
|
||||
for (StreamListener listener : stream.getDataListeners()) { |
||||
listener.streamDataReceived(dataMessage); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
private Stream getStream(short id) { |
||||
for (Stream stream : streams) { |
||||
if (stream.getMessage().getStreamID() == id) return stream; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
private boolean fireStreamOffered(Stream stream, StreamMessage message) { |
||||
boolean accept = false; |
||||
for (StreamListener listener : streamListeners) { |
||||
if (listener.streamOffered(message)) { |
||||
accept = true; |
||||
|
||||
stream.addDataListener(listener); |
||||
} |
||||
} |
||||
return accept; |
||||
} |
||||
} |
@ -1,133 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.streaming; |
||||
|
||||
import com.jme3.network.connection.Client; |
||||
import com.jme3.network.connection.Server; |
||||
import com.jme3.network.events.MessageAdapter; |
||||
import com.jme3.network.message.Message; |
||||
import com.jme3.network.message.StreamDataMessage; |
||||
import com.jme3.network.message.StreamMessage; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.util.ArrayList; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
public class ServerStreamingService extends MessageAdapter { |
||||
private static Logger log = Logger.getLogger(StreamingService.class.getName()); |
||||
|
||||
protected ArrayList<Stream> streams; |
||||
|
||||
private short nextStreamID = Short.MIN_VALUE; |
||||
|
||||
public ServerStreamingService(Server server) { |
||||
streams = new ArrayList<Stream>(); |
||||
server.addMessageListener(this, StreamMessage.class); |
||||
} |
||||
|
||||
public void offerStream(Client client, StreamMessage msg, InputStream data) { |
||||
short streamID = ++nextStreamID; |
||||
msg.setStreamID(streamID); |
||||
msg.setReliable(true); |
||||
|
||||
Stream stream = new Stream(); |
||||
stream.setData(data); |
||||
stream.setMessage(msg); |
||||
stream.setReceiver(client); |
||||
streams.add(stream); |
||||
|
||||
try { |
||||
client.send(msg); |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
|
||||
private void startStream(Stream stream) { |
||||
Client receiver = stream.getReceiver(); |
||||
try |
||||
{ |
||||
InputStream data = stream.getData(); |
||||
|
||||
byte[] buffer = new byte[1024]; |
||||
int length; |
||||
|
||||
StreamDataMessage msg = new StreamDataMessage(stream.getMessage().getStreamID()); |
||||
msg.setReliable(true); |
||||
|
||||
while ((length = data.read(buffer)) != -1) { |
||||
byte[] newBuffer = new byte[length]; |
||||
|
||||
for (int i = 0; i != length; ++i) { |
||||
newBuffer[i] = buffer[i]; |
||||
} |
||||
msg.setData(newBuffer); |
||||
receiver.send(msg); |
||||
} |
||||
data.close(); |
||||
|
||||
receiver.send(stream.getMessage()); |
||||
} catch (Exception ex) { |
||||
ex.printStackTrace(); |
||||
log.log(Level.WARNING, "[StreamSender][TCP] Could not send stream with message {0} to {1}. Reason: {2}.", new Object[]{stream, receiver, ex.getMessage()}); |
||||
} |
||||
} |
||||
|
||||
public void messageReceived(Message message) { |
||||
if (message instanceof StreamMessage && !(message instanceof StreamDataMessage)) { |
||||
// A stream was accepted.
|
||||
StreamMessage streamMessage = (StreamMessage)message; |
||||
Stream stream = getStream(streamMessage); |
||||
|
||||
if (stream == null) return; |
||||
stream.setAccepted(true); |
||||
startStream(stream); |
||||
} |
||||
} |
||||
|
||||
private Stream getStream(short id) { |
||||
for (Stream stream : streams) { |
||||
if (stream.getMessage().getStreamID() == id) return stream; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
private Stream getStream(StreamMessage msg) { |
||||
for (Stream stream : streams) { |
||||
if (stream.getMessage().getStreamID() == msg.getStreamID()) return stream; |
||||
} |
||||
return null; |
||||
} |
||||
} |
@ -1,94 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.streaming; |
||||
|
||||
import com.jme3.network.connection.Client; |
||||
import com.jme3.network.message.StreamMessage; |
||||
|
||||
import java.io.InputStream; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
public class Stream { |
||||
private StreamMessage message; |
||||
private InputStream data; |
||||
private Client receiver; |
||||
|
||||
private ArrayList<StreamListener> listeners = new ArrayList<StreamListener>(); |
||||
|
||||
private boolean accepted = false; |
||||
|
||||
public StreamMessage getMessage() { |
||||
return message; |
||||
} |
||||
|
||||
public void setMessage(StreamMessage message) { |
||||
this.message = message; |
||||
} |
||||
|
||||
public InputStream getData() { |
||||
return data; |
||||
} |
||||
|
||||
public void setData(InputStream data) { |
||||
this.data = data; |
||||
} |
||||
|
||||
public boolean isAccepted() { |
||||
return accepted; |
||||
} |
||||
|
||||
public void setAccepted(boolean accepted) { |
||||
this.accepted = accepted; |
||||
} |
||||
|
||||
public Client getReceiver() { |
||||
return receiver; |
||||
} |
||||
|
||||
public void setReceiver(Client receiver) { |
||||
this.receiver = receiver; |
||||
} |
||||
|
||||
public void addDataListener(StreamListener listener) { |
||||
listeners.add(listener); |
||||
} |
||||
|
||||
public void removeDataListener(StreamListener listener) { |
||||
listeners.remove(listener); |
||||
} |
||||
|
||||
public List<StreamListener> getDataListeners() { |
||||
return listeners; |
||||
} |
||||
} |
@ -1,47 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.streaming; |
||||
|
||||
import com.jme3.network.message.StreamDataMessage; |
||||
import com.jme3.network.message.StreamMessage; |
||||
|
||||
/** |
||||
* Used for stream sending/receiving. |
||||
* |
||||
* @author Lars Wesselius |
||||
*/ |
||||
public interface StreamListener { |
||||
public boolean streamOffered(StreamMessage message); |
||||
public void streamDataReceived(StreamDataMessage message); |
||||
public void streamCompleted(StreamMessage message); |
||||
} |
@ -1,76 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.streaming; |
||||
|
||||
import com.jme3.network.connection.Client; |
||||
import com.jme3.network.connection.Server; |
||||
import com.jme3.network.events.MessageAdapter; |
||||
import com.jme3.network.message.StreamMessage; |
||||
import com.jme3.network.service.Service; |
||||
|
||||
import java.io.InputStream; |
||||
|
||||
/** |
||||
* Streaming service handles all kinds of streaming to clients. It can be instantiated by |
||||
* both the client and server, where server will work as sender, and client as receiver. |
||||
* |
||||
* @author Lars Wesselius |
||||
*/ |
||||
public class StreamingService extends MessageAdapter implements Service { |
||||
|
||||
private ClientStreamingService clientService; |
||||
private ServerStreamingService serverService; |
||||
|
||||
public StreamingService(Client client) { |
||||
clientService = new ClientStreamingService(client); |
||||
} |
||||
|
||||
public StreamingService(Server server) { |
||||
serverService = new ServerStreamingService(server); |
||||
} |
||||
|
||||
public void offerStream(Client client, StreamMessage msg, InputStream data) { |
||||
if (serverService == null) return; |
||||
serverService.offerStream(client, msg, data); |
||||
} |
||||
|
||||
public void addStreamListener(StreamListener listener) { |
||||
if (clientService == null) return; |
||||
clientService.addStreamListener(listener); |
||||
} |
||||
|
||||
public void removeStreamListener(StreamListener listener) { |
||||
if (clientService == null) return; |
||||
clientService.removeStreamListener(listener); |
||||
} |
||||
} |
@ -1,264 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.sync; |
||||
|
||||
import com.jme3.network.connection.Client; |
||||
import com.jme3.network.events.MessageAdapter; |
||||
import com.jme3.network.message.Message; |
||||
import com.jme3.network.service.Service; |
||||
import com.jme3.util.IntMap; |
||||
import com.jme3.util.IntMap.Entry; |
||||
import java.nio.ByteBuffer; |
||||
|
||||
@Deprecated |
||||
public class ClientSyncService extends MessageAdapter implements Service { |
||||
|
||||
private static final ByteBuffer BUFFER = ByteBuffer.wrap(new byte[10000]); |
||||
|
||||
private static class ClientEntityInfo { |
||||
SyncEntity entity; |
||||
|
||||
EntitySyncInfo lastSyncInfo; |
||||
EntitySyncInfo lastCreateInfo; |
||||
|
||||
long lastUpdate = 0; |
||||
long lastExtrapolate = -1; |
||||
long lastUpdateRate; |
||||
long lastLatencyDelta = Long.MAX_VALUE; |
||||
} |
||||
|
||||
// private final ArrayList<EntitySyncInfo> syncQueue =
|
||||
// new ArrayList<EntitySyncInfo>();
|
||||
|
||||
private final IntMap<ClientEntityInfo> entities = |
||||
new IntMap<ClientEntityInfo>(); |
||||
|
||||
private EntityFactory factory; |
||||
private SyncSerializer serializer = new SyncSerializer(); |
||||
|
||||
private long lastSyncMsgTime = 0; |
||||
private int lastHeartbeat; |
||||
private MovingAverage averageLatency = new MovingAverage(20); |
||||
|
||||
public void update(float tpf2){ |
||||
long time = System.currentTimeMillis(); |
||||
synchronized (entities){ |
||||
for (Entry<ClientEntityInfo> entry : entities){ |
||||
ClientEntityInfo info = entry.getValue(); |
||||
|
||||
if (info.lastSyncInfo != null){ |
||||
if (!inLoopApplySyncInfo(entry.getKey(), info)) |
||||
continue; // entity was deleted due to this command
|
||||
} |
||||
|
||||
long timeSinceUpdate = time - info.lastUpdate; |
||||
if (timeSinceUpdate >= info.lastUpdateRate){ |
||||
if (info.lastExtrapolate == -1){ |
||||
info.entity.interpolate(1); |
||||
info.lastExtrapolate = info.lastUpdate + info.lastUpdateRate; |
||||
} |
||||
|
||||
long timeSinceExtrapolate = time - info.lastExtrapolate; |
||||
info.lastExtrapolate = time; |
||||
float tpf = timeSinceExtrapolate / 1000f; |
||||
info.entity.extrapolate(tpf); |
||||
}else{ |
||||
float blendAmount = (float) timeSinceUpdate / (float)info.lastUpdateRate; |
||||
info.entity.interpolate(blendAmount); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
public ClientSyncService(Client client){ |
||||
client.addMessageListener(this /*, SyncMessage.class*/ ); |
||||
} |
||||
|
||||
public void setEntityFactory(EntityFactory factory){ |
||||
this.factory = factory; |
||||
} |
||||
|
||||
public SyncEntity getEntity(int id){ |
||||
return entities.get(id).entity; |
||||
} |
||||
|
||||
private void inLoopCreateEntity(int entityId, ClientEntityInfo clientInfo){ |
||||
EntitySyncInfo initInfo = clientInfo.lastSyncInfo; |
||||
|
||||
Class<? extends SyncEntity> clazz; |
||||
try { |
||||
clazz = (Class<? extends SyncEntity>) Class.forName(initInfo.className); |
||||
} catch (ClassNotFoundException ex) { |
||||
throw new RuntimeException("Cannot find entity class: " + initInfo.className, ex); |
||||
} |
||||
|
||||
SyncEntity entity; |
||||
if (factory != null){ |
||||
entity = factory.createEntity(clazz); |
||||
}else{ |
||||
try { |
||||
entity = clazz.newInstance(); |
||||
} catch (InstantiationException ex) { |
||||
throw new RuntimeException("Entity class is missing empty constructor", ex); |
||||
} catch (IllegalAccessException ex) { |
||||
throw new RuntimeException(ex); |
||||
} |
||||
} |
||||
|
||||
clientInfo.entity = entity; |
||||
entity.onRemoteCreate(); |
||||
|
||||
serializer.read(entity, ByteBuffer.wrap(initInfo.data), true); |
||||
|
||||
clientInfo.lastSyncInfo = null; |
||||
} |
||||
|
||||
private void inLoopSyncEntity(int entityId, ClientEntityInfo entityInfo){ |
||||
serializer.read(entityInfo.entity, ByteBuffer.wrap(entityInfo.lastSyncInfo.data), false); |
||||
entityInfo.entity.onRemoteUpdate( entityInfo.lastLatencyDelta / 1000f ); |
||||
|
||||
// clear so its not called again
|
||||
entityInfo.lastSyncInfo = null; |
||||
entityInfo.lastLatencyDelta = Long.MAX_VALUE; |
||||
} |
||||
|
||||
private void inLoopDeleteEntity(int entityId, ClientEntityInfo clientInfo){ |
||||
SyncEntity entity = clientInfo.entity; |
||||
entity.onRemoteDelete(); |
||||
entities.remove(entityId); |
||||
} |
||||
|
||||
private boolean inLoopApplySyncInfo(int entityId, ClientEntityInfo clientInfo){ |
||||
switch (clientInfo.lastSyncInfo.type){ |
||||
case EntitySyncInfo.TYPE_NEW: |
||||
inLoopCreateEntity(entityId, clientInfo); |
||||
return true; |
||||
case EntitySyncInfo.TYPE_SYNC: |
||||
inLoopSyncEntity(entityId, clientInfo); |
||||
return true; |
||||
case EntitySyncInfo.TYPE_DELETE: |
||||
inLoopDeleteEntity(entityId, clientInfo); |
||||
return false; |
||||
default: |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
} |
||||
|
||||
private void createEntity(EntitySyncInfo info){ |
||||
ClientEntityInfo entityInfo = new ClientEntityInfo(); |
||||
entityInfo.lastUpdate = System.currentTimeMillis(); |
||||
|
||||
// forces inLoopCreateEntity to be called later
|
||||
entityInfo.lastSyncInfo = info; |
||||
|
||||
entities.put(info.id, entityInfo); |
||||
} |
||||
|
||||
private void syncEntity(EntitySyncInfo info, int latencyDelta){ |
||||
ClientEntityInfo entityInfo = entities.get(info.id); |
||||
if (entityInfo == null || entityInfo.entity == null) |
||||
return; // didn't receive init yet.
|
||||
|
||||
long time = System.currentTimeMillis(); |
||||
entityInfo.lastUpdateRate = time - entityInfo.lastUpdate; |
||||
entityInfo.lastUpdate = time; |
||||
entityInfo.lastExtrapolate = -1; |
||||
|
||||
// forces inLoopSyncEntity to be called later
|
||||
entityInfo.lastSyncInfo = info; |
||||
entityInfo.lastLatencyDelta = latencyDelta; |
||||
} |
||||
|
||||
void deleteEntity(EntitySyncInfo info){ |
||||
ClientEntityInfo clientInfo = entities.get(info.id); |
||||
clientInfo.lastSyncInfo = info; |
||||
} |
||||
|
||||
private void applySyncInfo(EntitySyncInfo info, int latencyDelta){ |
||||
switch (info.type) { |
||||
case EntitySyncInfo.TYPE_NEW: |
||||
createEntity(info); |
||||
break; |
||||
case EntitySyncInfo.TYPE_SYNC: |
||||
syncEntity(info, latencyDelta); |
||||
break; |
||||
case EntitySyncInfo.TYPE_DELETE: |
||||
deleteEntity(info); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void messageReceived(Message msg) { |
||||
if (!(msg instanceof SyncMessage)) { |
||||
return; |
||||
} |
||||
|
||||
int latencyDelta = 0; |
||||
if (lastSyncMsgTime == 0) { |
||||
// this is the first syncmessage
|
||||
lastSyncMsgTime = System.currentTimeMillis(); |
||||
} else { |
||||
long time = System.currentTimeMillis(); |
||||
long delta = time - lastSyncMsgTime; |
||||
averageLatency.add(delta); |
||||
lastSyncMsgTime = time; |
||||
latencyDelta = (int) (delta - averageLatency.getAverage()); |
||||
} |
||||
|
||||
SyncMessage sync = (SyncMessage) msg; |
||||
|
||||
boolean isOldMessage = false; |
||||
int newHeartbeat = sync.heartbeat; |
||||
if (lastHeartbeat > newHeartbeat){ |
||||
// check if at the end of heartbeat indices
|
||||
// within 1000 heartbeats
|
||||
if (lastHeartbeat > Integer.MAX_VALUE - 1000 && newHeartbeat < 1000){ |
||||
lastHeartbeat = newHeartbeat; |
||||
}else{ |
||||
isOldMessage = true; |
||||
} |
||||
}else{ |
||||
lastHeartbeat = newHeartbeat; |
||||
} |
||||
|
||||
for (EntitySyncInfo info : sync.infos) { |
||||
if (info.type == EntitySyncInfo.TYPE_SYNC && isOldMessage) |
||||
continue; // old sync message, ignore.
|
||||
|
||||
applySyncInfo(info, latencyDelta); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
@ -1,38 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.sync; |
||||
|
||||
@Deprecated |
||||
public interface EntityFactory { |
||||
public SyncEntity createEntity(Class<? extends SyncEntity> entityType); |
||||
} |
@ -1,27 +0,0 @@ |
||||
package com.jme3.network.sync; |
||||
|
||||
@Deprecated |
||||
public final class EntitySyncInfo { |
||||
|
||||
public static final byte TYPE_NEW = 0x1, |
||||
TYPE_SYNC = 0x2, |
||||
TYPE_DELETE = 0x3; |
||||
|
||||
/** |
||||
* NEW, SYNC, or DELETE |
||||
*/ |
||||
public byte type; |
||||
/** |
||||
* Entity ID |
||||
*/ |
||||
public int id; |
||||
/** |
||||
* Entity Class Name |
||||
*/ |
||||
public String className; |
||||
|
||||
/** |
||||
* Vars |
||||
*/ |
||||
public byte[] data; |
||||
} |
@ -1,64 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.sync; |
||||
|
||||
@Deprecated |
||||
public class MovingAverage { |
||||
|
||||
private long[] samples; |
||||
private long sum; |
||||
private int count, index; |
||||
|
||||
public MovingAverage(int numSamples){ |
||||
samples = new long[numSamples]; |
||||
} |
||||
|
||||
public void add(long sample){ |
||||
sum = sum - samples[index] + sample; |
||||
samples[index++] = sample; |
||||
if (index > count){ |
||||
count = index; |
||||
} |
||||
if (index >= samples.length){ |
||||
index = 0; |
||||
} |
||||
} |
||||
|
||||
public long getAverage(){ |
||||
if (count == 0) |
||||
return 0; |
||||
else |
||||
return (long) ((float) sum / (float) count); |
||||
} |
||||
|
||||
} |
@ -1,257 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.sync; |
||||
|
||||
import com.jme3.math.FastMath; |
||||
import com.jme3.network.connection.Client; |
||||
import com.jme3.network.connection.Server; |
||||
import com.jme3.network.events.ConnectionAdapter; |
||||
import com.jme3.network.service.Service; |
||||
import java.io.IOException; |
||||
import java.nio.ByteBuffer; |
||||
import java.util.ArrayList; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
@Deprecated |
||||
public class ServerSyncService extends ConnectionAdapter implements Service { |
||||
|
||||
private static final ByteBuffer BUFFER = ByteBuffer.wrap(new byte[10000]); |
||||
|
||||
private float updateRate = 0.1f; |
||||
private float packetDropRate = 0; |
||||
private long latency = 0; |
||||
private HashMap<Long, SyncMessage> latencyQueue; |
||||
|
||||
private final Server server; |
||||
private final SyncSerializer serializer = new SyncSerializer(); |
||||
// private final ArrayList<Client> connectedClients = new ArrayList<Client>();
|
||||
private final ArrayList<SyncEntity> npcs = new ArrayList<SyncEntity>(); |
||||
private final HashMap<SyncEntity, Integer> npcToId |
||||
= new HashMap<SyncEntity, Integer>(); |
||||
|
||||
private static int nextId = 0; |
||||
|
||||
private int heartbeat = 0; |
||||
private float time = 0; |
||||
|
||||
public ServerSyncService(Server server){ |
||||
this.server = server; |
||||
server.addConnectionListener(this); |
||||
} |
||||
|
||||
public void setNetworkSimulationParams(float packetDropRate, long latency){ |
||||
if (latencyQueue == null) |
||||
latencyQueue = new HashMap<Long, SyncMessage>(); |
||||
|
||||
this.packetDropRate = packetDropRate; |
||||
this.latency = latency; |
||||
} |
||||
|
||||
private EntitySyncInfo generateInitInfo(SyncEntity entity, boolean newId){ |
||||
EntitySyncInfo info = new EntitySyncInfo(); |
||||
info.className = entity.getClass().getName(); |
||||
info.id = newId ? nextId ++ : npcToId.get(entity); |
||||
info.type = EntitySyncInfo.TYPE_NEW; |
||||
|
||||
BUFFER.clear(); |
||||
serializer.write(entity, BUFFER, true); |
||||
BUFFER.flip(); |
||||
info.data = new byte[BUFFER.limit()]; |
||||
BUFFER.get(info.data); |
||||
return info; |
||||
} |
||||
|
||||
private EntitySyncInfo generateSyncInfo(SyncEntity entity){ |
||||
EntitySyncInfo info = new EntitySyncInfo(); |
||||
info.className = null; |
||||
info.id = npcToId.get(entity); |
||||
info.type = EntitySyncInfo.TYPE_SYNC; |
||||
|
||||
BUFFER.clear(); |
||||
serializer.write(entity, BUFFER, false); |
||||
BUFFER.flip(); |
||||
info.data = new byte[BUFFER.limit()]; |
||||
BUFFER.get(info.data); |
||||
return info; |
||||
} |
||||
|
||||
private EntitySyncInfo generateDeleteInfo(SyncEntity entity){ |
||||
EntitySyncInfo info = new EntitySyncInfo(); |
||||
info.className = null; |
||||
info.id = npcToId.get(entity); |
||||
info.type = EntitySyncInfo.TYPE_DELETE; |
||||
return info; |
||||
} |
||||
|
||||
public void addNpc(SyncEntity entity){ |
||||
EntitySyncInfo info = generateInitInfo(entity, true); |
||||
SyncMessage syncMsg = new SyncMessage(); |
||||
syncMsg.setReliable(true); |
||||
syncMsg.heartbeat = heartbeat; |
||||
syncMsg.infos = new EntitySyncInfo[]{ info }; |
||||
|
||||
try { |
||||
server.broadcast(syncMsg); |
||||
} catch (IOException ex) { |
||||
ex.printStackTrace(); |
||||
} |
||||
|
||||
synchronized (npcs){ |
||||
npcs.add(entity); |
||||
npcToId.put(entity, info.id); |
||||
} |
||||
} |
||||
|
||||
public void removeNpc(SyncEntity entity){ |
||||
EntitySyncInfo info = generateDeleteInfo(entity); |
||||
|
||||
SyncMessage syncMsg = new SyncMessage(); |
||||
syncMsg.setReliable(true); |
||||
syncMsg.heartbeat = heartbeat; |
||||
syncMsg.infos = new EntitySyncInfo[]{ info }; |
||||
|
||||
try { |
||||
server.broadcast(syncMsg); |
||||
} catch (IOException ex) { |
||||
ex.printStackTrace(); |
||||
} |
||||
|
||||
synchronized (npcs){ |
||||
npcs.remove(entity); |
||||
npcToId.remove(entity); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void clientConnected(Client client){ |
||||
System.out.println("Server: Client connected: " + client); |
||||
SyncMessage msg = new SyncMessage(); |
||||
msg.setReliable(true); // sending INIT information, has to be reliable.
|
||||
|
||||
msg.heartbeat = heartbeat; |
||||
EntitySyncInfo[] infos = new EntitySyncInfo[npcs.size()]; |
||||
msg.infos = infos; |
||||
synchronized (npcs){ |
||||
for (int i = 0; i < npcs.size(); i++){ |
||||
SyncEntity entity = npcs.get(i); |
||||
EntitySyncInfo info = generateInitInfo(entity, false); |
||||
infos[i] = info; |
||||
} |
||||
|
||||
try { |
||||
client.send(msg); |
||||
} catch (IOException ex) { |
||||
ex.printStackTrace(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void clientDisconnected(Client client){ |
||||
System.out.println("Server: Client disconnected: " + client); |
||||
} |
||||
|
||||
private void sendDelayedMessages(){ |
||||
ArrayList<Long> removeList = new ArrayList<Long>(); |
||||
for (Map.Entry<Long, SyncMessage> entry : latencyQueue.entrySet()){ |
||||
if (entry.getKey() > System.currentTimeMillis()) |
||||
continue; |
||||
|
||||
removeList.add(entry.getKey()); |
||||
if (packetDropRate > FastMath.nextRandomFloat()) |
||||
continue; |
||||
|
||||
for (Client client : server.getConnectors()){ |
||||
try { |
||||
client.send(entry.getValue()); |
||||
} catch (IOException ex) { |
||||
ex.printStackTrace(); |
||||
} |
||||
} |
||||
} |
||||
for (Long removeEntry : removeList) |
||||
latencyQueue.remove(removeEntry); |
||||
} |
||||
|
||||
public void update(float tpf){ |
||||
if (latencyQueue != null) |
||||
sendDelayedMessages(); |
||||
|
||||
if (npcs.size() == 0) |
||||
return; |
||||
|
||||
time += tpf; |
||||
if (time < updateRate){ |
||||
return; |
||||
}else{ |
||||
time = 0; |
||||
} |
||||
|
||||
SyncMessage msg = new SyncMessage(); |
||||
msg.setReliable(false); // Purely SYNC message, reliability not needed
|
||||
|
||||
msg.heartbeat = heartbeat; |
||||
synchronized (npcs){ |
||||
EntitySyncInfo[] infos = new EntitySyncInfo[npcs.size()]; |
||||
msg.infos = infos; |
||||
for (int i = 0; i < npcs.size(); i++){ |
||||
SyncEntity entity = npcs.get(i); |
||||
EntitySyncInfo info = generateSyncInfo(entity); |
||||
entity.onLocalUpdate(); |
||||
infos[i] = info; |
||||
} |
||||
} |
||||
|
||||
if (latencyQueue != null){ |
||||
long latencyTime = (long) (latency + (FastMath.nextRandomFloat()-0.5f) * latency); |
||||
long timeToSend = System.currentTimeMillis() + latencyTime; |
||||
latencyQueue.put(timeToSend, msg); |
||||
}else{ |
||||
for (Client client : server.getConnectors()){ |
||||
try { |
||||
client.send(msg); // unreliable
|
||||
} catch (IOException ex) { |
||||
ex.printStackTrace(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
heartbeat++; |
||||
if (heartbeat < 0){ |
||||
// overflow detected
|
||||
heartbeat = 0; |
||||
} |
||||
} |
||||
|
||||
} |
@ -1,49 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.sync; |
||||
|
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
@Deprecated |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target(ElementType.FIELD) |
||||
public @interface Sync { |
||||
|
||||
public enum SyncType { InitOnly, Init, Sync } |
||||
|
||||
boolean smooth() default false; |
||||
SyncType value() default SyncType.Sync; |
||||
} |
@ -1,46 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.sync; |
||||
|
||||
@Deprecated |
||||
public interface SyncEntity { |
||||
|
||||
public void onRemoteCreate(); |
||||
public void onRemoteUpdate(float latencyDelta); |
||||
public void onRemoteDelete(); |
||||
public void onLocalUpdate(); |
||||
|
||||
public void interpolate(float blendAmount); |
||||
public void extrapolate(float tpf); |
||||
|
||||
} |
@ -1,43 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.sync; |
||||
|
||||
import com.jme3.network.message.Message; |
||||
import com.jme3.network.serializing.Serializable; |
||||
|
||||
@Deprecated |
||||
@Serializable |
||||
public class SyncMessage extends Message { |
||||
public int heartbeat; |
||||
public EntitySyncInfo[] infos; |
||||
} |
@ -1,124 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2010 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.sync; |
||||
|
||||
import com.jme3.network.serializing.Serializer; |
||||
import com.jme3.network.sync.Sync.SyncType; |
||||
import java.io.ObjectInput; |
||||
import java.io.ObjectOutput; |
||||
import java.lang.reflect.Field; |
||||
import java.nio.ByteBuffer; |
||||
import java.util.ArrayList; |
||||
import java.util.HashMap; |
||||
|
||||
@Deprecated |
||||
class SyncSerializer { |
||||
|
||||
static class SyncFieldInfo { |
||||
private Field field; |
||||
private boolean init,sync,smooth; |
||||
} |
||||
|
||||
static class FieldTable extends ArrayList<SyncFieldInfo> { |
||||
} |
||||
|
||||
private HashMap<Class<?>, FieldTable> classFieldTables |
||||
= new HashMap<Class<?>, FieldTable>(); |
||||
|
||||
private FieldTable generateFieldTable(Class<?> clazz){ |
||||
FieldTable table = new FieldTable(); |
||||
while (clazz != null){ |
||||
Field[] fields = clazz.getDeclaredFields(); |
||||
for (Field field : fields){ |
||||
Sync syncAnnot = field.getAnnotation(Sync.class); |
||||
if (syncAnnot == null) |
||||
continue; |
||||
|
||||
SyncFieldInfo info = new SyncFieldInfo(); |
||||
field.setAccessible(true); |
||||
info.field = field; |
||||
info.init = syncAnnot.value() == SyncType.Init |
||||
|| syncAnnot.value() == SyncType.InitOnly; |
||||
info.sync = syncAnnot.value() != SyncType.InitOnly; |
||||
info.smooth = syncAnnot.smooth(); |
||||
table.add(info); |
||||
} |
||||
|
||||
clazz = clazz.getSuperclass(); |
||||
} |
||||
|
||||
return table; |
||||
} |
||||
|
||||
private FieldTable getTable(Class<?> clazz){ |
||||
FieldTable table = classFieldTables.get(clazz); |
||||
if (table == null){ |
||||
table = generateFieldTable(clazz); |
||||
classFieldTables.put(clazz, table); |
||||
} |
||||
return table; |
||||
} |
||||
|
||||
public void read(Object entity, ByteBuffer in, boolean init){ |
||||
FieldTable table = getTable(entity.getClass()); |
||||
for (SyncFieldInfo fieldInfo : table){ |
||||
if ( (init && !fieldInfo.init) |
||||
|| (!init && !fieldInfo.sync) ) |
||||
continue; |
||||
|
||||
Field field = fieldInfo.field; |
||||
try { |
||||
Object obj = Serializer.readClassAndObject(in); |
||||
field.set(entity, obj); |
||||
} catch (Exception ex){ |
||||
ex.printStackTrace(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void write(Object entity, ByteBuffer out, boolean init){ |
||||
FieldTable table = getTable(entity.getClass()); |
||||
for (SyncFieldInfo fieldInfo : table){ |
||||
if ( (init && !fieldInfo.init) |
||||
|| (!init && !fieldInfo.sync) ) |
||||
continue; |
||||
|
||||
Field field = fieldInfo.field; |
||||
try { |
||||
Serializer.writeClassAndObject(out, field.get(entity)); |
||||
} catch (Exception ex){ |
||||
ex.printStackTrace(); |
||||
} |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue