package network;
import java.util.List;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import main.Core;
import org.apache.mina.core.buffer.CachedBufferAllocator;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.buffer.SimpleBufferAllocator;
import protocol.Message;
import utils.collections.NonBlockingHashMap;
public class UDPServer {
public enum ServerState {
Down,
Up
}
private static int maxBytesPerTick = 30000;
private int port;
private DatagramSocket socket;
private Thread sendThread;
private Thread receiveThread;
private ServerState state = ServerState.Down;
private Map<SocketAddress, Client> clients = new NonBlockingHashMap<SocketAddress, Client>();
private DatagramPacket recvPacket = new DatagramPacket(new byte[496], 496);
private DatagramPacket sendPacket = new DatagramPacket(new byte[496], 496);
private SimpleBufferAllocator bufferPool = new SimpleBufferAllocator();
private NetworkDispatch dispatch;
private int sendQueueDelay;
private String name;
public UDPServer(int port, int sendQueueDelay, String name) {
this.port = port;
this.sendQueueDelay = sendQueueDelay;
this.name = name;
}
public void start() {
try {
state = ServerState.Up;
this.socket = new DatagramSocket(port);
receiveThread = new Thread(() -> {
while(state == ServerState.Up)
receive();
});
receiveThread.setName(name + " recieve");
if(dispatch != null) {
sendThread = new Thread(() -> {
while(state == ServerState.Up) {
try {
if(sendQueueDelay > 0)
Thread.sleep(sendQueueDelay);
} catch (Exception e) {
e.printStackTrace();
}
send();
}
});
sendThread.setName(name + " send");
sendThread.start();
}
receiveThread.start();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("UDP Server listening on port " + port);
}
private void send() {
if(clients.isEmpty())
return;
for(Client client : clients.values()) {
if(client.isOutOfOrder())
continue;
Queue<IoBuffer> packets = client.getPacketQueue();
if(packets.isEmpty())
continue;
if(sendQueueDelay < 0) {
for(IoBuffer packet : packets)
sendPacket(client, packet);
packets.clear();
continue;
}
List<IoBuffer> packetsToEncode = new ArrayList<IoBuffer>();
int bytes = 0;
while(bytes < maxBytesPerTick && !packets.isEmpty()) {
IoBuffer packet = packets.poll();
if(packet == null)
break;
packetsToEncode.add(packet);
bytes += packet.array().length;
}
List<IoBuffer> encoded = dispatch.getHandler().encode(client, packetsToEncode);
if(encoded == null)
continue;
for(IoBuffer buffer : encoded)
sendPacket(client, buffer);
}
}
private void receive() {
try {
socket.receive(recvPacket);
if(recvPacket.getLength() == 0)
return;
IoBuffer buffer = bufferPool.allocate(recvPacket.getLength(), false).put(recvPacket.getData(), 0, recvPacket.getLength());
buffer.position(0);
SocketAddress address = recvPacket.getSocketAddress();
Client client = clients.get(address);
if(client == null) {
client = new Client(address, dispatch);
clients.put(address, client);
}
if(dispatch != null) {
// The SoeProtocolHandler is obviously not always returning something. Do not let the loop suffer </3
List<IoBuffer> decodedPackets = dispatch.getHandler().decode(client, buffer);
if (decodedPackets != null) {
for(IoBuffer packet : decodedPackets)
dispatch.onRecieve(client, packet);
}
}
else
sendPacket(client, buffer);
} catch (IOException e) {
e.printStackTrace();
}
}
public void sendPacket(Client client, IoBuffer packet) {
try {
client.incSent();
sendPacket.setSocketAddress(client.getAddress());
sendPacket.setData(packet.array());
socket.send(sendPacket);
if(!client.getSentPackets().containsValue(packet))
packet.free();
} catch (IOException e) {
e.printStackTrace();
}
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public DatagramSocket getSocket() {
return socket;
}
public void setSocket(DatagramSocket socket) {
this.socket = socket;
}
public Thread getSendThread() {
return sendThread;
}
public void setSendThread(Thread sendThread) {
this.sendThread = sendThread;
}
public Thread getRecieveThread() {
return receiveThread;
}
public void setRecieveThread(Thread receiveThread) {
this.receiveThread = receiveThread;
}
public Map<SocketAddress, Client> getClients() {
return clients;
}
public void setClients(Map<SocketAddress, Client> clients) {
this.clients = clients;
}
public NetworkDispatch getDispatch() {
return dispatch;
}
public void setDispatch(NetworkDispatch dispatch) {
this.dispatch = dispatch;
}
public int getSendQueueDelay() {
return sendQueueDelay;
}
public void setSendQueueDelay(int sendQueueDelay) {
this.sendQueueDelay = sendQueueDelay;
}
public DatagramPacket getSendPacket() {
return sendPacket;
}
public void setSendPacket(DatagramPacket sendPacket) {
this.sendPacket = sendPacket;
}
public void removeClient(Client client) {
clients.remove(client.getAddress());
}
}