diff --git a/core/src/com/riiablo/net/bnls/BNLS.fbs b/core/src/com/riiablo/net/bnls/BNLS.fbs index 6f8503dc..e16fa36a 100644 --- a/core/src/com/riiablo/net/bnls/BNLS.fbs +++ b/core/src/com/riiablo/net/bnls/BNLS.fbs @@ -1,8 +1,10 @@ +include "ConnectionClosed.fbs"; include "QueryRealms.fbs"; namespace com.riiablo.net.packet.bnls; union BNLSData { + ConnectionClosed, QueryRealms, } diff --git a/core/src/com/riiablo/net/bnls/ConnectionClosed.fbs b/core/src/com/riiablo/net/bnls/ConnectionClosed.fbs new file mode 100644 index 00000000..0b4acfba --- /dev/null +++ b/core/src/com/riiablo/net/bnls/ConnectionClosed.fbs @@ -0,0 +1,5 @@ +namespace com.riiablo.net.packet.bnls; + +table ConnectionClosed { + reason:string; +} \ No newline at end of file diff --git a/server/src/com/riiablo/server/BNLS.java b/server/src/com/riiablo/server/BNLS.java index 94b27e9f..e6a634da 100644 --- a/server/src/com/riiablo/server/BNLS.java +++ b/server/src/com/riiablo/server/BNLS.java @@ -7,10 +7,12 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Net; import com.badlogic.gdx.backends.headless.HeadlessApplication; import com.badlogic.gdx.backends.headless.HeadlessApplicationConfiguration; +import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.net.ServerSocket; import com.badlogic.gdx.net.Socket; import com.badlogic.gdx.utils.BufferUtils; import com.riiablo.net.packet.bnls.BNLSData; +import com.riiablo.net.packet.bnls.ConnectionClosed; import com.riiablo.net.packet.bnls.QueryRealms; import com.riiablo.net.packet.bnls.Realm; @@ -26,12 +28,14 @@ import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.text.DateFormat; import java.util.Calendar; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; public class BNLS extends ApplicationAdapter { private static final String TAG = "BNLS"; private static final int PORT = 6110; + private static final int MAX_CLIENTS = 32; public static void main(String[] args) { HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration(); @@ -40,9 +44,11 @@ public class BNLS extends ApplicationAdapter { ServerSocket server; ByteBuffer buffer; - Thread thread; + Thread main; AtomicBoolean kill; Thread cli; + ThreadGroup clientThreads; + CopyOnWriteArrayList CLIENTS = new CopyOnWriteArrayList<>(); private static final String[][] REALMS = new String[][] { {"localhost", "U.S. West"}, @@ -65,42 +71,43 @@ public class BNLS extends ApplicationAdapter { Gdx.app.error(TAG, e.getMessage(), e); } + clientThreads = new ThreadGroup("BNLSClients"); + Gdx.app.log(TAG, "Starting server..."); server = Gdx.net.newServerSocket(Net.Protocol.TCP, PORT, null); buffer = BufferUtils.newByteBuffer(4096); kill = new AtomicBoolean(false); - thread = new Thread(new Runnable() { + main = new Thread(new Runnable() { @Override public void run() { while (!kill.get()) { - Socket socket = null; - try { - Gdx.app.log(TAG, "waiting..."); - socket = server.accept(null); - Gdx.app.log(TAG, "connection from " + socket.getRemoteAddress()); + Gdx.app.log(TAG, "waiting..."); + Socket socket = server.accept(null); + Gdx.app.log(TAG, "connection from " + socket.getRemoteAddress()); + if (CLIENTS.size() >= MAX_CLIENTS) { + try { + ConnectionDenied(socket, "Server is Full"); + } catch (Throwable ignored) { + } finally { + socket.dispose(); + } + } else { + new Client(socket).start(); + } + } - buffer.mark(); - ReadableByteChannel in = Channels.newChannel(socket.getInputStream()); - in.read(buffer); - buffer.limit(buffer.position()); - buffer.reset(); - - com.riiablo.net.packet.bnls.BNLS packet = com.riiablo.net.packet.bnls.BNLS.getRootAsBNLS(buffer); - Gdx.app.log(TAG, "packet type " + BNLSData.name(packet.dataType())); - process(socket, packet); - } catch (Throwable t) { - Gdx.app.log(TAG, t.getMessage()); - } finally { - Gdx.app.log(TAG, "closing socket..."); - if (socket != null) socket.dispose(); + Gdx.app.log(TAG, "killing child threads..."); + for (Client client : CLIENTS) { + if (client != null) { + client.kill.set(true); } } Gdx.app.log(TAG, "killing thread..."); } }); - thread.setName("BNLS"); - thread.start(); + main.setName("BNLS"); + main.start(); cli = new Thread(new Runnable() { @Override @@ -134,7 +141,7 @@ public class BNLS extends ApplicationAdapter { kill.set(true); server.dispose(); try { - thread.join(); + main.join(); } catch (Throwable ignored) {} } @@ -148,7 +155,21 @@ public class BNLS extends ApplicationAdapter { } } - private void QueryRealms(Socket socket) throws IOException { + private boolean ConnectionDenied(Socket socket, String reason) throws IOException { + FlatBufferBuilder builder = new FlatBufferBuilder(); + int reasonOffset = builder.createString(reason); + int connectionDeniedId = ConnectionClosed.createConnectionClosed(builder, reasonOffset); + int id = com.riiablo.net.packet.bnls.BNLS.createBNLS(builder, BNLSData.ConnectionClosed, connectionDeniedId); + builder.finish(id); + + ByteBuffer data = builder.dataBuffer(); + OutputStream out = socket.getOutputStream(); + WritableByteChannel channel = Channels.newChannel(out); + channel.write(data); + return true; + } + + private boolean QueryRealms(Socket socket) throws IOException { FlatBufferBuilder builder = new FlatBufferBuilder(); int[] realms = new int[REALMS.length]; @@ -171,5 +192,42 @@ public class BNLS extends ApplicationAdapter { WritableByteChannel channel = Channels.newChannel(out); channel.write(data); Gdx.app.log(TAG, "returning realms list..."); + return false; + } + + static String generateClientName() { + return String.format("Client-%08X", MathUtils.random(1, Integer.MAX_VALUE - 1)); + } + + private class Client extends Thread { + Socket socket; + AtomicBoolean kill = new AtomicBoolean(false); + + Client(Socket socket) { + super(clientThreads, generateClientName()); + this.socket = socket; + } + + @Override + public void run() { + while (!kill.get()) { + try { + buffer.mark(); + ReadableByteChannel in = Channels.newChannel(socket.getInputStream()); + in.read(buffer); + buffer.limit(buffer.position()); + buffer.reset(); + + com.riiablo.net.packet.bnls.BNLS packet = com.riiablo.net.packet.bnls.BNLS.getRootAsBNLS(buffer); + Gdx.app.log(TAG, "packet type " + BNLSData.name(packet.dataType())); + process(socket, packet); + } catch (Throwable t) { + Gdx.app.log(TAG, t.getMessage()); + } finally { + Gdx.app.log(TAG, "closing socket..."); + if (socket != null) socket.dispose(); + } + } + } } } diff --git a/server/test/com/riiablo/server/BNLSTest.java b/server/test/com/riiablo/server/BNLSTest.java index da24a4d3..0f68353a 100644 --- a/server/test/com/riiablo/server/BNLSTest.java +++ b/server/test/com/riiablo/server/BNLSTest.java @@ -60,7 +60,12 @@ public class BNLSTest { buffer.rewind(); com.riiablo.net.packet.bnls.BNLS packet = com.riiablo.net.packet.bnls.BNLS.getRootAsBNLS(buffer); - Gdx.app.log(TAG, "packet " + packet.dataType()); + Gdx.app.log(TAG, "packet " + BNLSData.name(packet.dataType())); + + if (packet.dataType() == BNLSData.ConnectionClosed) { + Gdx.app.log(TAG, "connection closed."); + return; + } QueryRealms qr = (QueryRealms) packet.data(new QueryRealms()); Gdx.app.log(TAG, "realms:");