Removing the deprecated packages and old version of the
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-0572b91ccdca
This commit is contained in:
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);
|
|
||||||
}
|
|
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
package com.jme3.network.message;
|
package com.jme3.network.message;
|
||||||
|
|
||||||
|
import com.jme3.network.AbstractMessage;
|
||||||
import com.jme3.network.serializing.Serializable;
|
import com.jme3.network.serializing.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,7 +45,7 @@ import com.jme3.network.serializing.Serializable;
|
|||||||
* @author Lars Wesselius
|
* @author Lars Wesselius
|
||||||
*/
|
*/
|
||||||
@Serializable()
|
@Serializable()
|
||||||
public class ClientRegistrationMessage extends Message {
|
public class ClientRegistrationMessage extends AbstractMessage {
|
||||||
private long id;
|
private long id;
|
||||||
private String gameName;
|
private String gameName;
|
||||||
private int version;
|
private int version;
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
|
|
||||||
package com.jme3.network.message;
|
package com.jme3.network.message;
|
||||||
|
|
||||||
|
import com.jme3.network.AbstractMessage;
|
||||||
|
import com.jme3.network.Message;
|
||||||
import com.jme3.network.serializing.Serializable;
|
import com.jme3.network.serializing.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,7 +43,7 @@ import com.jme3.network.serializing.Serializable;
|
|||||||
* @author Lars Wesselius
|
* @author Lars Wesselius
|
||||||
*/
|
*/
|
||||||
@Serializable()
|
@Serializable()
|
||||||
public class CompressedMessage extends Message {
|
public class CompressedMessage extends AbstractMessage {
|
||||||
private Message message;
|
private Message message;
|
||||||
|
|
||||||
public CompressedMessage() { }
|
public CompressedMessage() { }
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
package com.jme3.network.message;
|
package com.jme3.network.message;
|
||||||
|
|
||||||
|
import com.jme3.network.AbstractMessage;
|
||||||
import com.jme3.network.serializing.Serializable;
|
import com.jme3.network.serializing.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,7 +41,7 @@ import com.jme3.network.serializing.Serializable;
|
|||||||
* @author Lars Wesselius
|
* @author Lars Wesselius
|
||||||
*/
|
*/
|
||||||
@Serializable()
|
@Serializable()
|
||||||
public class DisconnectMessage extends Message {
|
public class DisconnectMessage extends AbstractMessage {
|
||||||
public static final String KICK = "Kick";
|
public static final String KICK = "Kick";
|
||||||
public static final String USER_REQUESTED = "User requested";
|
public static final String USER_REQUESTED = "User requested";
|
||||||
public static final String ERROR = "Error";
|
public static final String ERROR = "Error";
|
||||||
|
@ -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 {
|
|
||||||
}
|
|
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
package com.jme3.network.message;
|
package com.jme3.network.message;
|
||||||
|
|
||||||
|
import com.jme3.network.Message;
|
||||||
import com.jme3.network.serializing.Serializable;
|
import com.jme3.network.serializing.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
package com.jme3.network.message;
|
package com.jme3.network.message;
|
||||||
|
|
||||||
|
import com.jme3.network.Message;
|
||||||
import com.jme3.network.serializing.Serializable;
|
import com.jme3.network.serializing.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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> {
|
|
||||||
|
|
||||||
}
|
|
@ -123,12 +123,8 @@ public abstract class Serializer {
|
|||||||
registerClass(GZIPCompressedMessage.class, new GZIPSerializer());
|
registerClass(GZIPCompressedMessage.class, new GZIPSerializer());
|
||||||
registerClass(ZIPCompressedMessage.class, new ZIPSerializer());
|
registerClass(ZIPCompressedMessage.class, new ZIPSerializer());
|
||||||
|
|
||||||
registerClass(Message.class);
|
|
||||||
registerClass(DisconnectMessage.class);
|
registerClass(DisconnectMessage.class);
|
||||||
registerClass(ClientRegistrationMessage.class);
|
registerClass(ClientRegistrationMessage.class);
|
||||||
registerClass(DiscoverHostMessage.class);
|
|
||||||
registerClass(StreamDataMessage.class);
|
|
||||||
registerClass(StreamMessage.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -348,11 +344,10 @@ public abstract class Serializer {
|
|||||||
* @param type The class to write.
|
* @param type The class to write.
|
||||||
* @return The SerializerRegistration that's registered to the class.
|
* @return The SerializerRegistration that's registered to the class.
|
||||||
*/
|
*/
|
||||||
public static SerializerRegistration writeClass(ByteBuffer buffer, Class type) {
|
public static SerializerRegistration writeClass(ByteBuffer buffer, Class type) throws IOException {
|
||||||
SerializerRegistration reg = getSerializerRegistration(type);
|
SerializerRegistration reg = getSerializerRegistration(type);
|
||||||
if (reg == null) {
|
if (reg == null) {
|
||||||
reg = classRegistrations.get(Message.class);
|
throw new SerializerException( "Class not registered:" + type );
|
||||||
//registerClassToSerializer(type, FieldSerializer.class);
|
|
||||||
}
|
}
|
||||||
buffer.putShort(reg.getId());
|
buffer.putShort(reg.getId());
|
||||||
return reg;
|
return reg;
|
||||||
|
@ -32,7 +32,6 @@
|
|||||||
|
|
||||||
package com.jme3.network.serializing.serializers;
|
package com.jme3.network.serializing.serializers;
|
||||||
|
|
||||||
import com.jme3.network.message.Message;
|
|
||||||
import com.jme3.network.serializing.Serializer;
|
import com.jme3.network.serializing.Serializer;
|
||||||
import com.jme3.network.serializing.SerializerException;
|
import com.jme3.network.serializing.SerializerException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -68,7 +67,7 @@ public class FieldSerializer extends Serializer {
|
|||||||
List<Field> fields = new ArrayList<Field>();
|
List<Field> fields = new ArrayList<Field>();
|
||||||
|
|
||||||
Class processingClass = clazz;
|
Class processingClass = clazz;
|
||||||
while (processingClass != Object.class && processingClass != Message.class) {
|
while (processingClass != Object.class ) {
|
||||||
Collections.addAll(fields, processingClass.getDeclaredFields());
|
Collections.addAll(fields, processingClass.getDeclaredFields());
|
||||||
processingClass = processingClass.getSuperclass();
|
processingClass = processingClass.getSuperclass();
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
package com.jme3.network.serializing.serializers;
|
package com.jme3.network.serializing.serializers;
|
||||||
|
|
||||||
import com.jme3.network.message.GZIPCompressedMessage;
|
import com.jme3.network.message.GZIPCompressedMessage;
|
||||||
import com.jme3.network.message.Message;
|
import com.jme3.network.Message;
|
||||||
import com.jme3.network.serializing.Serializer;
|
import com.jme3.network.serializing.Serializer;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
package com.jme3.network.serializing.serializers;
|
package com.jme3.network.serializing.serializers;
|
||||||
|
|
||||||
import com.jme3.network.message.Message;
|
import com.jme3.network.Message;
|
||||||
import com.jme3.network.message.ZIPCompressedMessage;
|
import com.jme3.network.message.ZIPCompressedMessage;
|
||||||
import com.jme3.network.serializing.Serializer;
|
import com.jme3.network.serializing.Serializer;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
@ -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…
x
Reference in New Issue
Block a user