From 9f5d134eee922bf820d4a3adcc244cb229b4aecf Mon Sep 17 00:00:00 2001 From: Collin Smith Date: Sat, 30 Nov 2019 20:02:36 -0800 Subject: [PATCH] Added MSI (master slave instancer) to create child D2GS processes Added MSI (master slave instancer) to create child D2GS processes This is intended to act as a temp structure for testing until support for actual servers are added where an API can generate proper instances --- .../desktop__networking_.xml | 1 + core/gen/com/riiablo/net/packet/msi/MSI.java | 39 +++++ .../com/riiablo/net/packet/msi/MSIData.java | 14 ++ .../com/riiablo/net/packet/msi/Result.java | 14 ++ .../riiablo/net/packet/msi/StartInstance.java | 41 +++++ core/src/com/riiablo/net/GameSession.java | 7 + core/src/com/riiablo/net/msi/MSI.fbs | 13 ++ .../src/com/riiablo/net/msi/StartInstance.fbs | 15 ++ server/d2gs/build.gradle | 1 + .../mcp/src/com/riiablo/server/mcp/MCP.java | 74 ++++++++- .../mcp/src/com/riiablo/server/mcp/MSI.java | 143 ++++++++++++++++++ 11 files changed, 356 insertions(+), 6 deletions(-) create mode 100644 core/gen/com/riiablo/net/packet/msi/MSI.java create mode 100644 core/gen/com/riiablo/net/packet/msi/MSIData.java create mode 100644 core/gen/com/riiablo/net/packet/msi/Result.java create mode 100644 core/gen/com/riiablo/net/packet/msi/StartInstance.java create mode 100644 core/src/com/riiablo/net/msi/MSI.fbs create mode 100644 core/src/com/riiablo/net/msi/StartInstance.fbs create mode 100644 server/mcp/src/com/riiablo/server/mcp/MSI.java diff --git a/.idea/runConfigurations/desktop__networking_.xml b/.idea/runConfigurations/desktop__networking_.xml index f42b6f49..c99513e4 100644 --- a/.idea/runConfigurations/desktop__networking_.xml +++ b/.idea/runConfigurations/desktop__networking_.xml @@ -2,6 +2,7 @@ + diff --git a/core/gen/com/riiablo/net/packet/msi/MSI.java b/core/gen/com/riiablo/net/packet/msi/MSI.java new file mode 100644 index 00000000..8a55f3ee --- /dev/null +++ b/core/gen/com/riiablo/net/packet/msi/MSI.java @@ -0,0 +1,39 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +package com.riiablo.net.packet.msi; + +import java.nio.*; +import java.lang.*; +import java.util.*; +import com.google.flatbuffers.*; + +@SuppressWarnings("unused") +public final class MSI extends Table { + public static MSI getRootAsMSI(ByteBuffer _bb) { return getRootAsMSI(_bb, new MSI()); } + public static MSI getRootAsMSI(ByteBuffer _bb, MSI obj) { _bb.order(ByteOrder.LITTLE_ENDIAN); return (obj.__assign(_bb.getInt(_bb.position()) + _bb.position(), _bb)); } + public void __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; } + public MSI __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; } + + public byte dataType() { int o = __offset(4); return o != 0 ? bb.get(o + bb_pos) : 0; } + public Table data(Table obj) { int o = __offset(6); return o != 0 ? __union(obj, o) : null; } + + public static int createMSI(FlatBufferBuilder builder, + byte data_type, + int dataOffset) { + builder.startObject(2); + MSI.addData(builder, dataOffset); + MSI.addDataType(builder, data_type); + return MSI.endMSI(builder); + } + + public static void startMSI(FlatBufferBuilder builder) { builder.startObject(2); } + public static void addDataType(FlatBufferBuilder builder, byte dataType) { builder.addByte(0, dataType, 0); } + public static void addData(FlatBufferBuilder builder, int dataOffset) { builder.addOffset(1, dataOffset, 0); } + public static int endMSI(FlatBufferBuilder builder) { + int o = builder.endObject(); + return o; + } + public static void finishMSIBuffer(FlatBufferBuilder builder, int offset) { builder.finish(offset); } + public static void finishSizePrefixedMSIBuffer(FlatBufferBuilder builder, int offset) { builder.finishSizePrefixed(offset); } +} + diff --git a/core/gen/com/riiablo/net/packet/msi/MSIData.java b/core/gen/com/riiablo/net/packet/msi/MSIData.java new file mode 100644 index 00000000..58265ad5 --- /dev/null +++ b/core/gen/com/riiablo/net/packet/msi/MSIData.java @@ -0,0 +1,14 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +package com.riiablo.net.packet.msi; + +public final class MSIData { + private MSIData() { } + public static final byte NONE = 0; + public static final byte StartInstance = 1; + + public static final String[] names = { "NONE", "StartInstance", }; + + public static String name(int e) { return names[e]; } +} + diff --git a/core/gen/com/riiablo/net/packet/msi/Result.java b/core/gen/com/riiablo/net/packet/msi/Result.java new file mode 100644 index 00000000..95d33dfb --- /dev/null +++ b/core/gen/com/riiablo/net/packet/msi/Result.java @@ -0,0 +1,14 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +package com.riiablo.net.packet.msi; + +public final class Result { + private Result() { } + public static final byte SUCCESS = 0; + public static final byte FAILURE = 1; + + public static final String[] names = { "SUCCESS", "FAILURE", }; + + public static String name(int e) { return names[e]; } +} + diff --git a/core/gen/com/riiablo/net/packet/msi/StartInstance.java b/core/gen/com/riiablo/net/packet/msi/StartInstance.java new file mode 100644 index 00000000..f8cb4e8d --- /dev/null +++ b/core/gen/com/riiablo/net/packet/msi/StartInstance.java @@ -0,0 +1,41 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +package com.riiablo.net.packet.msi; + +import java.nio.*; +import java.lang.*; +import java.util.*; +import com.google.flatbuffers.*; + +@SuppressWarnings("unused") +public final class StartInstance extends Table { + public static StartInstance getRootAsStartInstance(ByteBuffer _bb) { return getRootAsStartInstance(_bb, new StartInstance()); } + public static StartInstance getRootAsStartInstance(ByteBuffer _bb, StartInstance obj) { _bb.order(ByteOrder.LITTLE_ENDIAN); return (obj.__assign(_bb.getInt(_bb.position()) + _bb.position(), _bb)); } + public void __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; } + public StartInstance __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; } + + public byte result() { int o = __offset(4); return o != 0 ? bb.get(o + bb_pos) : 0; } + public int ip() { int o = __offset(6); return o != 0 ? bb.getInt(o + bb_pos) : 0; } + public short port() { int o = __offset(8); return o != 0 ? bb.getShort(o + bb_pos) : 0; } + + public static int createStartInstance(FlatBufferBuilder builder, + byte result, + int ip, + short port) { + builder.startObject(3); + StartInstance.addIp(builder, ip); + StartInstance.addPort(builder, port); + StartInstance.addResult(builder, result); + return StartInstance.endStartInstance(builder); + } + + public static void startStartInstance(FlatBufferBuilder builder) { builder.startObject(3); } + public static void addResult(FlatBufferBuilder builder, byte result) { builder.addByte(0, result, 0); } + public static void addIp(FlatBufferBuilder builder, int ip) { builder.addInt(1, ip, 0); } + public static void addPort(FlatBufferBuilder builder, short port) { builder.addShort(2, port, 0); } + public static int endStartInstance(FlatBufferBuilder builder) { + int o = builder.endObject(); + return o; + } +} + diff --git a/core/src/com/riiablo/net/GameSession.java b/core/src/com/riiablo/net/GameSession.java index 671fb504..dfbf1ba7 100644 --- a/core/src/com/riiablo/net/GameSession.java +++ b/core/src/com/riiablo/net/GameSession.java @@ -1,6 +1,7 @@ package com.riiablo.net; import com.riiablo.net.packet.mcp.CreateGame; +import com.riiablo.net.packet.msi.StartInstance; public class GameSession { public String name; @@ -28,6 +29,12 @@ public class GameSession { desc = builder.desc; } + public GameSession setConnectInfo(StartInstance info) { + ip = info.ip(); + port = info.port(); + return this; + } + @Override public String toString() { return name; diff --git a/core/src/com/riiablo/net/msi/MSI.fbs b/core/src/com/riiablo/net/msi/MSI.fbs new file mode 100644 index 00000000..d2620ce8 --- /dev/null +++ b/core/src/com/riiablo/net/msi/MSI.fbs @@ -0,0 +1,13 @@ +include "StartInstance.fbs"; + +namespace com.riiablo.net.packet.msi; + +union MSIData { + StartInstance, +} + +table MSI { + data:MSIData; +} + +root_type MSI; \ No newline at end of file diff --git a/core/src/com/riiablo/net/msi/StartInstance.fbs b/core/src/com/riiablo/net/msi/StartInstance.fbs new file mode 100644 index 00000000..d1689708 --- /dev/null +++ b/core/src/com/riiablo/net/msi/StartInstance.fbs @@ -0,0 +1,15 @@ +namespace com.riiablo.net.packet.msi; + +enum Result : byte { + SUCCESS = 0, + FAILURE = 1, +} + +table StartInstance { + // request + + // response + result:Result; + ip:int32; + port:int16; +} diff --git a/server/d2gs/build.gradle b/server/d2gs/build.gradle index 10a09c4b..0ce9d69c 100644 --- a/server/d2gs/build.gradle +++ b/server/d2gs/build.gradle @@ -23,6 +23,7 @@ task dist(type: Jar) { manifest { attributes 'Server-Class': project.mainClassName + attributes 'Main-Class': project.mainClassName } } diff --git a/server/mcp/src/com/riiablo/server/mcp/MCP.java b/server/mcp/src/com/riiablo/server/mcp/MCP.java index 7e661ee1..55094079 100644 --- a/server/mcp/src/com/riiablo/server/mcp/MCP.java +++ b/server/mcp/src/com/riiablo/server/mcp/MCP.java @@ -21,9 +21,13 @@ import com.riiablo.net.packet.mcp.JoinGame; import com.riiablo.net.packet.mcp.ListGames; import com.riiablo.net.packet.mcp.MCPData; import com.riiablo.net.packet.mcp.Result; +import com.riiablo.net.packet.msi.MSI; +import com.riiablo.net.packet.msi.MSIData; +import com.riiablo.net.packet.msi.StartInstance; import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.InetAddress; @@ -245,20 +249,38 @@ public class MCP extends ApplicationAdapter { } private boolean CreateGame(Socket socket, com.riiablo.net.packet.mcp.MCP packet) throws IOException { - CreateGame createGame = (CreateGame) packet.data(new CreateGame()); - String gameName = createGame.gameName(); + final CreateGame createGame = (CreateGame) packet.data(new CreateGame()); + final String gameName = createGame.gameName(); Gdx.app.debug(TAG, "Attempting to create " + gameName + " for " + socket.getRemoteAddress()); - FlatBufferBuilder builder = new FlatBufferBuilder(); + final FlatBufferBuilder builder = new FlatBufferBuilder(); CreateGame.startCreateGame(builder); if (sessions.containsKey(gameName)) { CreateGame.addResult(builder, Result.ALREADY_EXISTS); } else if (sessions.size() >= 4) { CreateGame.addResult(builder, Result.SERVER_DOWN); } else { - CreateGame.addResult(builder, Result.SUCCESS); - sessions.put(gameName, new GameSession(createGame)); - Gdx.app.debug(TAG, "Created session " + gameName); + final GameSession session = new GameSession(createGame); + StartInstance(createGame, new ResponseListener() { + @Override + public void handleResponse(MSI msi) { + StartInstance startInstance = (StartInstance) msi.data(new StartInstance()); + switch (startInstance.result()) { + case Result.SUCCESS: + sessions.put(gameName, session.setConnectInfo(startInstance)); + CreateGame.addResult(builder, Result.SUCCESS); + Gdx.app.debug(TAG, "Created session " + gameName); + break; + default: + CreateGame.addResult(builder, Result.SERVER_DOWN); + } + } + + @Override + public void failed(Throwable t) { + Gdx.app.error(TAG, t.getMessage(), t); + } + }); } int createGameOffset = CreateGame.endCreateGame(builder); @@ -304,6 +326,46 @@ public class MCP extends ApplicationAdapter { return false; } + interface ResponseListener { + void handleResponse(MSI msi); + void failed(Throwable t); + } + + private void StartInstance(CreateGame createGame, ResponseListener listener) { + Gdx.app.debug(TAG, "Requesting game instance for " + createGame); + FlatBufferBuilder builder = new FlatBufferBuilder(); + StartInstance.startStartInstance(builder); + int startInstanceOffset = StartInstance.endStartInstance(builder); + int id = MSI.createMSI(builder, MSIData.StartInstance, startInstanceOffset); + builder.finish(id); + ByteBuffer data = builder.dataBuffer(); + + Socket socket = null; + try { + socket = Gdx.net.newClientSocket(Net.Protocol.TCP, "localhost", com.riiablo.server.mcp.MSI.PORT, null); + + OutputStream out = socket.getOutputStream(); + WritableByteChannel channelOut = Channels.newChannel(out); + channelOut.write(data); + + buffer.clear(); + buffer.mark(); + InputStream in = socket.getInputStream(); + ReadableByteChannel channelIn = Channels.newChannel(in); + channelIn.read(buffer); + buffer.limit(buffer.position()); + buffer.reset(); + + MSI packet = MSI.getRootAsMSI(buffer); + Gdx.app.log(TAG, "packet type " + MCPData.name(packet.dataType())); + listener.handleResponse(packet); + } catch (Throwable t) { + listener.failed(t); + } finally { + if (socket != null) socket.dispose(); + } + } + static String generateClientName() { return String.format("Client-%08X", MathUtils.random(1, Integer.MAX_VALUE - 1)); } diff --git a/server/mcp/src/com/riiablo/server/mcp/MSI.java b/server/mcp/src/com/riiablo/server/mcp/MSI.java new file mode 100644 index 00000000..91a43724 --- /dev/null +++ b/server/mcp/src/com/riiablo/server/mcp/MSI.java @@ -0,0 +1,143 @@ +package com.riiablo.server.mcp; + +import com.google.flatbuffers.FlatBufferBuilder; + +import com.badlogic.gdx.Application; +import com.badlogic.gdx.ApplicationAdapter; +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.net.ServerSocket; +import com.badlogic.gdx.net.Socket; +import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.BufferUtils; +import com.riiablo.net.packet.msi.MSIData; +import com.riiablo.net.packet.msi.Result; +import com.riiablo.net.packet.msi.StartInstance; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; +import java.text.DateFormat; +import java.util.Calendar; + +public class MSI extends ApplicationAdapter { + private static final String TAG = "MSI"; + + static final int PORT = 6112; + + public static void main(String[] args) { + HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration(); + new HeadlessApplication(new MSI(), config); + } + + ServerSocket server; + ByteBuffer buffer; + final Array instances = new Array<>(); + + MSI() {} + + @Override + public void create() { + Gdx.app.setLogLevel(Application.LOG_DEBUG); + + final Calendar calendar = Calendar.getInstance(); + DateFormat format = DateFormat.getDateTimeInstance(); + Gdx.app.log(TAG, format.format(calendar.getTime())); + + try { + InetAddress address = InetAddress.getLocalHost(); + Gdx.app.log(TAG, "IP Address: " + address.getHostAddress() + ":" + PORT); + Gdx.app.log(TAG, "Host Name: " + address.getHostName()); + } catch (UnknownHostException e) { + Gdx.app.error(TAG, e.getMessage(), e); + } + + Gdx.app.log(TAG, "Starting server..."); + server = Gdx.net.newServerSocket(Net.Protocol.TCP, PORT, null); + buffer = BufferUtils.newByteBuffer(4096); + } + + @Override + public void render() { + Socket socket = null; + try { + Gdx.app.log(TAG, "waiting..."); + socket = server.accept(null); + Gdx.app.log(TAG, "connection from " + socket.getRemoteAddress()); + + buffer.clear(); + buffer.mark(); + ReadableByteChannel in = Channels.newChannel(socket.getInputStream()); + in.read(buffer); + buffer.limit(buffer.position()); + buffer.reset(); + + com.riiablo.net.packet.msi.MSI packet = com.riiablo.net.packet.msi.MSI.getRootAsMSI(buffer); + Gdx.app.log(TAG, "packet type " + MSIData.name(packet.dataType())); + process(socket, packet); + } catch (Throwable t) { + if (socket != null) socket.dispose(); + } + } + + private void process(Socket socket, com.riiablo.net.packet.msi.MSI packet) throws IOException { + switch (packet.dataType()) { + case MSIData.StartInstance: + StartInstance(socket, packet); + break; + default: + Gdx.app.error(TAG, "Unknown packet type: " + packet.dataType()); + } + } + + private boolean StartInstance(Socket socket, com.riiablo.net.packet.msi.MSI packet) throws IOException { + Gdx.app.debug(TAG, "Starting instance..."); + + try { + File outFile = new File("D2GS.tmp"); + ProcessBuilder processBuilder = new ProcessBuilder("java", "-jar", "server/d2gs/build/libs/d2gs-1.0.jar"); + processBuilder.redirectOutput(ProcessBuilder.Redirect.to(outFile)); + processBuilder.redirectError(ProcessBuilder.Redirect.to(outFile)); + Process process = processBuilder.start(); + instances.add(process); + } catch (Throwable t) { + Gdx.app.error(TAG, t.getMessage(), t); + } + + int ip = 2130706433; // 127.0.0.1 + short port = 6114; + + FlatBufferBuilder builder = new FlatBufferBuilder(); + StartInstance.startStartInstance(builder); + StartInstance.addResult(builder, Result.SUCCESS); + StartInstance.addIp(builder, ip); + StartInstance.addPort(builder, port); + int startInstanceOffset = StartInstance.endStartInstance(builder); + int id = com.riiablo.net.packet.msi.MSI.createMSI(builder, MSIData.StartInstance, startInstanceOffset); + builder.finish(id); + + ByteBuffer data = builder.dataBuffer(); + OutputStream out = socket.getOutputStream(); + WritableByteChannel channel = Channels.newChannel(out); + channel.write(data); + Gdx.app.debug(TAG, "Returning instance at " + InetAddress.getByAddress(ByteBuffer.allocate(4).putInt(ip).array())); + return true; + } + + @Override + public void dispose() { + Gdx.app.log(TAG, "Shutting down..."); + server.dispose(); + for (Process process : instances) { + process.destroy(); + } + } +}