Initial Commit

This commit is contained in:
Josh Larson
2016-09-09 16:30:59 -05:00
commit fdf4cbecd5
112 changed files with 11443 additions and 0 deletions
@@ -0,0 +1,279 @@
/***********************************************************************************
* Copyright (c) 2015 /// Project SWG /// www.projectswg.com *
* *
* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
* Our goal is to create an emulator which will provide a server for players to *
* continue playing a game similar to the one they used to play. We are basing *
* it on the final publish of the game prior to end-game events. *
* *
* This file is part of Holocore. *
* *
* -------------------------------------------------------------------------------- *
* *
* Holocore is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* Holocore is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with Holocore. If not, see <http://www.gnu.org/licenses/>. *
* *
***********************************************************************************/
package com.projectswg.common.network;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import com.projectswg.common.callback.CallbackManager;
import com.projectswg.common.info.Log;
public class TCPServer {
private final CallbackManager<TCPCallback> callbackManager;
private final Map<SocketAddress, SocketChannel> sockets;
private final InetAddress addr;
private final int port;
private final int bufferSize;
private ServerSocketChannel channel;
private TCPListener listener;
public TCPServer(int port, int bufferSize) {
this(null, port, bufferSize);
}
public TCPServer(InetAddress addr, int port, int bufferSize) {
this.callbackManager = new CallbackManager<>("tcpserver-"+port, 1);
this.sockets = new HashMap<>();
this.addr = addr;
this.port = port;
this.bufferSize = bufferSize;
this.channel = null;
listener = new TCPListener();
}
public void bind() throws IOException {
callbackManager.start();
channel = ServerSocketChannel.open();
channel.socket().bind(new InetSocketAddress(addr, port));
channel.configureBlocking(false);
listener.start();
}
public SocketChannel connectTo(InetAddress addr, int port) throws IOException {
return connectTo(new InetSocketAddress(addr, port));
}
public SocketChannel connectTo(InetSocketAddress sock) throws IOException {
synchronized (sockets) {
SocketChannel sc = sockets.get(sock);
if (sc == null || (!sc.isConnected() && !sc.isConnectionPending())) {
sc = SocketChannel.open(sock);
sc.configureBlocking(false);
sockets.put(sock, sc);
}
return sc;
}
}
public boolean disconnect(SocketAddress sock) {
synchronized (sockets) {
SocketChannel sc = sockets.get(sock);
if (sc == null)
return false;
sockets.remove(sock);
try {
Socket s = sc.socket();
sc.close();
callbackManager.callOnEach((callback) -> callback.onConnectionDisconnect(s, sock));
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
}
private boolean disconnect(SocketChannel sc) {
try {
return disconnect(sc.getRemoteAddress());
} catch (IOException e) {
return false;
}
}
public boolean close() {
callbackManager.stop();
listener.stop();
try {
if (channel != null)
channel.close();
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
public boolean send(InetSocketAddress sock, ByteBuffer data) {
synchronized (sockets) {
SocketChannel sc = sockets.get(sock);
try {
if (sc != null && sc.isConnected()) {
while (data.hasRemaining())
sc.write(data);
return true;
}
} catch (IOException e) {
Log.e("TCPServer", "Terminated connection with %s. Error: %s", sock.toString(), e.getMessage());
disconnect(sc);
}
}
return false;
}
public void setCallback(TCPCallback callback) {
callbackManager.setCallback(callback);
}
public interface TCPCallback {
void onIncomingConnection(Socket s);
void onConnectionDisconnect(Socket s, SocketAddress addr);
void onIncomingData(Socket s, byte [] data);
}
private class TCPListener implements Runnable {
private final ByteBuffer buffer;
private Thread thread;
private boolean running;
public TCPListener() {
buffer = ByteBuffer.allocateDirect(bufferSize);
running = false;
thread = null;
}
public void start() {
running = true;
thread = new Thread(this, "TCPServer port " + channel.socket().getLocalPort());
thread.start();
}
public void stop() {
running = false;
if (thread != null)
thread.interrupt();
thread = null;
}
public void run() {
try (Selector selector = setupSelector()) {
while (running) {
try {
selector.select();
processSelectionKeys(selector);
} catch (Exception e) {
e.printStackTrace();
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
break;
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private Selector setupSelector() throws IOException {
Selector selector = Selector.open();
channel.register(selector, SelectionKey.OP_ACCEPT);
return selector;
}
private void processSelectionKeys(Selector selector) throws ClosedChannelException {
for (SelectionKey key : selector.selectedKeys()) {
if (!key.isValid())
continue;
if (key.isAcceptable()) {
accept(selector);
} else if (key.isReadable()) {
SelectableChannel selectable = key.channel();
if (selectable instanceof SocketChannel) {
boolean canRead = true;
while (canRead)
canRead = read(key, (SocketChannel) selectable);
}
}
}
}
private void accept(Selector selector) {
try {
while (channel.isOpen()) {
SocketChannel sc = channel.accept();
if (sc == null)
break;
SocketChannel old = sockets.get(sc.getRemoteAddress());
if (old != null)
disconnect(old);
sockets.put(sc.getRemoteAddress(), sc);
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
callbackManager.callOnEach((callback) -> callback.onIncomingConnection(sc.socket()));
}
} catch (IOException e) {
e.printStackTrace();
}
}
private boolean read(SelectionKey key, SocketChannel s) {
try {
buffer.position(0);
buffer.limit(bufferSize);
int n = s.read(buffer);
buffer.flip();
if (n < 0) {
key.cancel();
disconnect(s);
} else if (n > 0) {
ByteBuffer smaller = ByteBuffer.allocate(n);
smaller.put(buffer);
callbackManager.callOnEach((callback) -> callback.onIncomingData(s.socket(), smaller.array()));
return true;
}
} catch (IOException e) {
if (e.getMessage() != null && e.getMessage().toLowerCase(Locale.US).contains("connection reset"))
Log.e("TCPServer", "Connection Reset with %s", s.socket().getRemoteSocketAddress());
else if (!(e instanceof ClosedByInterruptException))
Log.e("TCPServer", e);
key.cancel();
disconnect(s);
}
return false;
}
}
}