Added MCP server module which acts as the server browser

Added MCP server module which acts as the server browser
MCP only supports listing games at this time
Minor changes to LoginScreen to reflect how LobbyScreen was written differently
Added desktop with networking run config which starts up server instances and then a client
Created GameSession class which is used as a cache for mcp GameSession packets
This commit is contained in:
Collin Smith
2019-11-30 01:46:56 -08:00
parent 5e23ac48b0
commit ac35310ace
24 changed files with 1039 additions and 76 deletions

View File

@ -0,0 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="desktop (networking)" type="CompoundRunConfigurationType" factoryName="Compound Run Configuration">
<toRun type="Application" name="BNLS" />
<toRun type="Application" name="MCP" />
<toRun type="Application" name="desktop (debug) (854x480)" />
<method />
</configuration>
</component>

View File

@ -236,6 +236,26 @@ project(":server:bnls") {
} }
} }
project(":server:mcp") {
apply plugin: "java"
dependencies {
// TODO: $gdxVersion is pulling an older file for some reason
compile "com.badlogicgames.gdx:gdx-backend-headless:$gdxVersion"
compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
}
dependencies {
compile project(":core")
compile group: 'commons-cli', name: 'commons-cli', version: cliVersion
compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.6'
}
dependencies {
testCompile 'junit:junit:4.12'
}
}
project(":mpqlib") { project(":mpqlib") {
apply plugin: "java" apply plugin: "java"

View File

@ -0,0 +1,24 @@
// automatically generated by the FlatBuffers compiler, do not modify
package com.riiablo.net.packet.mcp;
import java.nio.*;
import java.lang.*;
import java.util.*;
import com.google.flatbuffers.*;
@SuppressWarnings("unused")
public final class ConnectionAccepted extends Table {
public static ConnectionAccepted getRootAsConnectionAccepted(ByteBuffer _bb) { return getRootAsConnectionAccepted(_bb, new ConnectionAccepted()); }
public static ConnectionAccepted getRootAsConnectionAccepted(ByteBuffer _bb, ConnectionAccepted 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 ConnectionAccepted __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; }
public static void startConnectionAccepted(FlatBufferBuilder builder) { builder.startObject(0); }
public static int endConnectionAccepted(FlatBufferBuilder builder) {
int o = builder.endObject();
return o;
}
}

View File

@ -0,0 +1,35 @@
// automatically generated by the FlatBuffers compiler, do not modify
package com.riiablo.net.packet.mcp;
import java.nio.*;
import java.lang.*;
import java.util.*;
import com.google.flatbuffers.*;
@SuppressWarnings("unused")
public final class ConnectionClosed extends Table {
public static ConnectionClosed getRootAsConnectionClosed(ByteBuffer _bb) { return getRootAsConnectionClosed(_bb, new ConnectionClosed()); }
public static ConnectionClosed getRootAsConnectionClosed(ByteBuffer _bb, ConnectionClosed 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 ConnectionClosed __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; }
public String reason() { int o = __offset(4); return o != 0 ? __string(o + bb_pos) : null; }
public ByteBuffer reasonAsByteBuffer() { return __vector_as_bytebuffer(4, 1); }
public ByteBuffer reasonInByteBuffer(ByteBuffer _bb) { return __vector_in_bytebuffer(_bb, 4, 1); }
public static int createConnectionClosed(FlatBufferBuilder builder,
int reasonOffset) {
builder.startObject(1);
ConnectionClosed.addReason(builder, reasonOffset);
return ConnectionClosed.endConnectionClosed(builder);
}
public static void startConnectionClosed(FlatBufferBuilder builder) { builder.startObject(1); }
public static void addReason(FlatBufferBuilder builder, int reasonOffset) { builder.addOffset(0, reasonOffset, 0); }
public static int endConnectionClosed(FlatBufferBuilder builder) {
int o = builder.endObject();
return o;
}
}

View File

@ -0,0 +1,67 @@
// automatically generated by the FlatBuffers compiler, do not modify
package com.riiablo.net.packet.mcp;
import java.nio.*;
import java.lang.*;
import java.util.*;
import com.google.flatbuffers.*;
@SuppressWarnings("unused")
public final class CreateGame extends Table {
public static CreateGame getRootAsCreateGame(ByteBuffer _bb) { return getRootAsCreateGame(_bb, new CreateGame()); }
public static CreateGame getRootAsCreateGame(ByteBuffer _bb, CreateGame 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 CreateGame __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; }
public int diff() { int o = __offset(4); return o != 0 ? bb.getInt(o + bb_pos) : 0; }
public int levelDifference() { int o = __offset(6); return o != 0 ? bb.getInt(o + bb_pos) : 0; }
public int maxPlayers() { int o = __offset(8); return o != 0 ? bb.getInt(o + bb_pos) : 0; }
public String gameName() { int o = __offset(10); return o != 0 ? __string(o + bb_pos) : null; }
public ByteBuffer gameNameAsByteBuffer() { return __vector_as_bytebuffer(10, 1); }
public ByteBuffer gameNameInByteBuffer(ByteBuffer _bb) { return __vector_in_bytebuffer(_bb, 10, 1); }
public String password() { int o = __offset(12); return o != 0 ? __string(o + bb_pos) : null; }
public ByteBuffer passwordAsByteBuffer() { return __vector_as_bytebuffer(12, 1); }
public ByteBuffer passwordInByteBuffer(ByteBuffer _bb) { return __vector_in_bytebuffer(_bb, 12, 1); }
public String description() { int o = __offset(14); return o != 0 ? __string(o + bb_pos) : null; }
public ByteBuffer descriptionAsByteBuffer() { return __vector_as_bytebuffer(14, 1); }
public ByteBuffer descriptionInByteBuffer(ByteBuffer _bb) { return __vector_in_bytebuffer(_bb, 14, 1); }
public int gameToken() { int o = __offset(16); return o != 0 ? bb.getInt(o + bb_pos) : 0; }
public int result() { int o = __offset(18); return o != 0 ? bb.getInt(o + bb_pos) : 0; }
public static int createCreateGame(FlatBufferBuilder builder,
int diff,
int levelDifference,
int maxPlayers,
int gameNameOffset,
int passwordOffset,
int descriptionOffset,
int gameToken,
int result) {
builder.startObject(8);
CreateGame.addResult(builder, result);
CreateGame.addGameToken(builder, gameToken);
CreateGame.addDescription(builder, descriptionOffset);
CreateGame.addPassword(builder, passwordOffset);
CreateGame.addGameName(builder, gameNameOffset);
CreateGame.addMaxPlayers(builder, maxPlayers);
CreateGame.addLevelDifference(builder, levelDifference);
CreateGame.addDiff(builder, diff);
return CreateGame.endCreateGame(builder);
}
public static void startCreateGame(FlatBufferBuilder builder) { builder.startObject(8); }
public static void addDiff(FlatBufferBuilder builder, int diff) { builder.addInt(0, diff, 0); }
public static void addLevelDifference(FlatBufferBuilder builder, int levelDifference) { builder.addInt(1, levelDifference, 0); }
public static void addMaxPlayers(FlatBufferBuilder builder, int maxPlayers) { builder.addInt(2, maxPlayers, 0); }
public static void addGameName(FlatBufferBuilder builder, int gameNameOffset) { builder.addOffset(3, gameNameOffset, 0); }
public static void addPassword(FlatBufferBuilder builder, int passwordOffset) { builder.addOffset(4, passwordOffset, 0); }
public static void addDescription(FlatBufferBuilder builder, int descriptionOffset) { builder.addOffset(5, descriptionOffset, 0); }
public static void addGameToken(FlatBufferBuilder builder, int gameToken) { builder.addInt(6, gameToken, 0); }
public static void addResult(FlatBufferBuilder builder, int result) { builder.addInt(7, result, 0); }
public static int endCreateGame(FlatBufferBuilder builder) {
int o = builder.endObject();
return o;
}
}

View File

@ -0,0 +1,53 @@
// automatically generated by the FlatBuffers compiler, do not modify
package com.riiablo.net.packet.mcp;
import java.nio.*;
import java.lang.*;
import java.util.*;
import com.google.flatbuffers.*;
@SuppressWarnings("unused")
public final class GameSession extends Table {
public static GameSession getRootAsGameSession(ByteBuffer _bb) { return getRootAsGameSession(_bb, new GameSession()); }
public static GameSession getRootAsGameSession(ByteBuffer _bb, GameSession 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 GameSession __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; }
public long index() { int o = __offset(4); return o != 0 ? (long)bb.getInt(o + bb_pos) & 0xFFFFFFFFL : 0L; }
public int players() { int o = __offset(6); return o != 0 ? bb.get(o + bb_pos) & 0xFF : 0; }
public String name() { int o = __offset(8); return o != 0 ? __string(o + bb_pos) : null; }
public ByteBuffer nameAsByteBuffer() { return __vector_as_bytebuffer(8, 1); }
public ByteBuffer nameInByteBuffer(ByteBuffer _bb) { return __vector_in_bytebuffer(_bb, 8, 1); }
public String desc() { int o = __offset(10); return o != 0 ? __string(o + bb_pos) : null; }
public ByteBuffer descAsByteBuffer() { return __vector_as_bytebuffer(10, 1); }
public ByteBuffer descInByteBuffer(ByteBuffer _bb) { return __vector_in_bytebuffer(_bb, 10, 1); }
public long flags() { int o = __offset(12); return o != 0 ? (long)bb.getInt(o + bb_pos) & 0xFFFFFFFFL : 0L; }
public static int createGameSession(FlatBufferBuilder builder,
long index,
int players,
int nameOffset,
int descOffset,
long flags) {
builder.startObject(5);
GameSession.addFlags(builder, flags);
GameSession.addDesc(builder, descOffset);
GameSession.addName(builder, nameOffset);
GameSession.addIndex(builder, index);
GameSession.addPlayers(builder, players);
return GameSession.endGameSession(builder);
}
public static void startGameSession(FlatBufferBuilder builder) { builder.startObject(5); }
public static void addIndex(FlatBufferBuilder builder, long index) { builder.addInt(0, (int)index, (int)0L); }
public static void addPlayers(FlatBufferBuilder builder, int players) { builder.addByte(1, (byte)players, (byte)0); }
public static void addName(FlatBufferBuilder builder, int nameOffset) { builder.addOffset(2, nameOffset, 0); }
public static void addDesc(FlatBufferBuilder builder, int descOffset) { builder.addOffset(3, descOffset, 0); }
public static void addFlags(FlatBufferBuilder builder, long flags) { builder.addInt(4, (int)flags, (int)0L); }
public static int endGameSession(FlatBufferBuilder builder) {
int o = builder.endObject();
return o;
}
}

View File

@ -0,0 +1,49 @@
// automatically generated by the FlatBuffers compiler, do not modify
package com.riiablo.net.packet.mcp;
import java.nio.*;
import java.lang.*;
import java.util.*;
import com.google.flatbuffers.*;
@SuppressWarnings("unused")
public final class JoinGame extends Table {
public static JoinGame getRootAsJoinGame(ByteBuffer _bb) { return getRootAsJoinGame(_bb, new JoinGame()); }
public static JoinGame getRootAsJoinGame(ByteBuffer _bb, JoinGame 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 JoinGame __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; }
public String gameName() { int o = __offset(4); return o != 0 ? __string(o + bb_pos) : null; }
public ByteBuffer gameNameAsByteBuffer() { return __vector_as_bytebuffer(4, 1); }
public ByteBuffer gameNameInByteBuffer(ByteBuffer _bb) { return __vector_in_bytebuffer(_bb, 4, 1); }
public String password() { int o = __offset(6); return o != 0 ? __string(o + bb_pos) : null; }
public ByteBuffer passwordAsByteBuffer() { return __vector_as_bytebuffer(6, 1); }
public ByteBuffer passwordInByteBuffer(ByteBuffer _bb) { return __vector_in_bytebuffer(_bb, 6, 1); }
public long ip() { int o = __offset(8); return o != 0 ? (long)bb.getInt(o + bb_pos) & 0xFFFFFFFFL : 0L; }
public int result() { int o = __offset(10); return o != 0 ? bb.getInt(o + bb_pos) : 0; }
public static int createJoinGame(FlatBufferBuilder builder,
int gameNameOffset,
int passwordOffset,
long ip,
int result) {
builder.startObject(4);
JoinGame.addResult(builder, result);
JoinGame.addIp(builder, ip);
JoinGame.addPassword(builder, passwordOffset);
JoinGame.addGameName(builder, gameNameOffset);
return JoinGame.endJoinGame(builder);
}
public static void startJoinGame(FlatBufferBuilder builder) { builder.startObject(4); }
public static void addGameName(FlatBufferBuilder builder, int gameNameOffset) { builder.addOffset(0, gameNameOffset, 0); }
public static void addPassword(FlatBufferBuilder builder, int passwordOffset) { builder.addOffset(1, passwordOffset, 0); }
public static void addIp(FlatBufferBuilder builder, long ip) { builder.addInt(2, (int)ip, (int)0L); }
public static void addResult(FlatBufferBuilder builder, int result) { builder.addInt(3, result, 0); }
public static int endJoinGame(FlatBufferBuilder builder) {
int o = builder.endObject();
return o;
}
}

View File

@ -0,0 +1,41 @@
// automatically generated by the FlatBuffers compiler, do not modify
package com.riiablo.net.packet.mcp;
import java.nio.*;
import java.lang.*;
import java.util.*;
import com.google.flatbuffers.*;
@SuppressWarnings("unused")
public final class ListGames extends Table {
public static ListGames getRootAsListGames(ByteBuffer _bb) { return getRootAsListGames(_bb, new ListGames()); }
public static ListGames getRootAsListGames(ByteBuffer _bb, ListGames 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 ListGames __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; }
public long flags() { int o = __offset(4); return o != 0 ? (long)bb.getInt(o + bb_pos) & 0xFFFFFFFFL : 0L; }
public GameSession games(int j) { return games(new GameSession(), j); }
public GameSession games(GameSession obj, int j) { int o = __offset(6); return o != 0 ? obj.__assign(__indirect(__vector(o) + j * 4), bb) : null; }
public int gamesLength() { int o = __offset(6); return o != 0 ? __vector_len(o) : 0; }
public static int createListGames(FlatBufferBuilder builder,
long flags,
int gamesOffset) {
builder.startObject(2);
ListGames.addGames(builder, gamesOffset);
ListGames.addFlags(builder, flags);
return ListGames.endListGames(builder);
}
public static void startListGames(FlatBufferBuilder builder) { builder.startObject(2); }
public static void addFlags(FlatBufferBuilder builder, long flags) { builder.addInt(0, (int)flags, (int)0L); }
public static void addGames(FlatBufferBuilder builder, int gamesOffset) { builder.addOffset(1, gamesOffset, 0); }
public static int createGamesVector(FlatBufferBuilder builder, int[] data) { builder.startVector(4, data.length, 4); for (int i = data.length - 1; i >= 0; i--) builder.addOffset(data[i]); return builder.endVector(); }
public static void startGamesVector(FlatBufferBuilder builder, int numElems) { builder.startVector(4, numElems, 4); }
public static int endListGames(FlatBufferBuilder builder) {
int o = builder.endObject();
return o;
}
}

View File

@ -0,0 +1,39 @@
// automatically generated by the FlatBuffers compiler, do not modify
package com.riiablo.net.packet.mcp;
import java.nio.*;
import java.lang.*;
import java.util.*;
import com.google.flatbuffers.*;
@SuppressWarnings("unused")
public final class MCP extends Table {
public static MCP getRootAsMCP(ByteBuffer _bb) { return getRootAsMCP(_bb, new MCP()); }
public static MCP getRootAsMCP(ByteBuffer _bb, MCP 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 MCP __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 createMCP(FlatBufferBuilder builder,
byte data_type,
int dataOffset) {
builder.startObject(2);
MCP.addData(builder, dataOffset);
MCP.addDataType(builder, data_type);
return MCP.endMCP(builder);
}
public static void startMCP(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 endMCP(FlatBufferBuilder builder) {
int o = builder.endObject();
return o;
}
public static void finishMCPBuffer(FlatBufferBuilder builder, int offset) { builder.finish(offset); }
public static void finishSizePrefixedMCPBuffer(FlatBufferBuilder builder, int offset) { builder.finishSizePrefixed(offset); }
}

View File

@ -0,0 +1,18 @@
// automatically generated by the FlatBuffers compiler, do not modify
package com.riiablo.net.packet.mcp;
public final class MCPData {
private MCPData() { }
public static final byte NONE = 0;
public static final byte ConnectionClosed = 1;
public static final byte ConnectionAccepted = 2;
public static final byte CreateGame = 3;
public static final byte JoinGame = 4;
public static final byte ListGames = 5;
public static final String[] names = { "NONE", "ConnectionClosed", "ConnectionAccepted", "CreateGame", "JoinGame", "ListGames", };
public static String name(int e) { return names[e]; }
}

View File

@ -0,0 +1,23 @@
// automatically generated by the FlatBuffers compiler, do not modify
package com.riiablo.net.packet.mcp;
public final class Result {
private Result() { }
public static final int SUCCESS = 0;
public static final int INVALID_NAME = 30;
public static final int ALREAD_EXISTS = 31;
public static final int SERVER_DOWN = 32;
public static final int INVALID_PASSWORD = 41;
public static final int GAME_DOES_NOT_EXIST = 42;
public static final int GAME_IS_FULL = 43;
public static final int LEVEL_REQUIREMENTS = 44;
public static final int HARDCORE_DEAD = 110;
public static final int HARDCORE_SOFTCORE = 113;
public static final int UNABLE_NIGHTMARE = 115;
public static final int UNABLE_HELL = 116;
public static final int NONEXP_EXPANSION = 120;
public static final int EXPANSION_NONEXP = 121;
public static final int NONLADDER_LADDER = 125;
}

View File

@ -0,0 +1,19 @@
package com.riiablo.net;
public class GameSession {
public String name;
public String password;
public String desc;
public GameSession() {}
public GameSession(com.riiablo.net.packet.mcp.GameSession game) {
name = game.name();
desc = game.desc();
}
@Override
public String toString() {
return name;
}
}

View File

@ -0,0 +1,4 @@
namespace com.riiablo.net.packet.mcp;
table ConnectionAccepted {
}

View File

@ -0,0 +1,5 @@
namespace com.riiablo.net.packet.mcp;
table ConnectionClosed {
reason:string;
}

View File

@ -0,0 +1,25 @@
include "Result.fbs";
namespace com.riiablo.net.packet.mcp;
//enum Result : int {
// SUCCESS = 0x00,
// INVALID_NAME = 0x1E,
// ALREAD_EXISTS = 0x1F,
// SERVER_DOWN = 0x20,
// HARDCORE_DEAD = 0x6E,
//}
table CreateGame {
// request
diff:int;
levelDifference:int;
maxPlayers:int;
gameName:string;
password:string;
description:string;
// response
gameToken:int;
result:Result;
}

View File

@ -0,0 +1,28 @@
include "Result.fbs";
namespace com.riiablo.net.packet.mcp;
//enum Result : int {
// SUCCESS = 0x00,
// INVALID_PASSWORD = 0x29,
// GAME_DOES_NOT_EXIST = 0x2A,
// GAME_IS_FULL = 0x2B,
// LEVEL_REQUIREMENTS = 0x2C,
// HARDCORE_DEAD = 0x6E,
// HARDCORE_SOFTCORE = 0x71,
// UNABLE_NIGHTMARE = 0x73,
// UNABLE_HELL = 0x74,
// NONEXP_EXPANSION = 0x78,
// EXPANSION_NONEXP = 0x79,
// NONLADDER_LADDER = 0x7D,
//}
table JoinGame {
// request
gameName:string;
password:string;
// response
ip:uint32;
result:Result;
}

View File

@ -0,0 +1,17 @@
namespace com.riiablo.net.packet.mcp;
table ListGames {
// request
flags:uint32;
// response
games:[GameSession];
}
table GameSession {
index:uint32;
players:uint8;
name:string;
desc:string;
flags:uint32;
}

View File

@ -0,0 +1,22 @@
include "Result.fbs";
include "ConnectionClosed.fbs";
include "ConnectionAccepted.fbs";
include "CreateGame.fbs";
include "JoinGame.fbs";
include "ListGames.fbs";
namespace com.riiablo.net.packet.mcp;
union MCPData {
ConnectionClosed,
ConnectionAccepted,
CreateGame,
JoinGame,
ListGames,
}
table MCP {
data:MCPData;
}
root_type MCP;

View File

@ -0,0 +1,19 @@
namespace com.riiablo.net.packet.mcp;
enum Result : int {
SUCCESS = 0x00,
INVALID_NAME = 0x1E,
ALREAD_EXISTS = 0x1F,
SERVER_DOWN = 0x20,
INVALID_PASSWORD = 0x29,
GAME_DOES_NOT_EXIST = 0x2A,
GAME_IS_FULL = 0x2B,
LEVEL_REQUIREMENTS = 0x2C,
HARDCORE_DEAD = 0x6E,
HARDCORE_SOFTCORE = 0x71,
UNABLE_NIGHTMARE = 0x73,
UNABLE_HELL = 0x74,
NONEXP_EXPANSION = 0x78,
EXPANSION_NONEXP = 0x79,
NONLADDER_LADDER = 0x7D,
}

View File

@ -1,12 +1,13 @@
package com.riiablo.screen; package com.riiablo.screen;
import com.google.flatbuffers.FlatBufferBuilder;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input; import com.badlogic.gdx.Input;
import com.badlogic.gdx.Net; import com.badlogic.gdx.Net;
import com.badlogic.gdx.ScreenAdapter; import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.assets.AssetDescriptor; import com.badlogic.gdx.assets.AssetDescriptor;
import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.net.HttpRequestBuilder;
import com.badlogic.gdx.net.Socket; import com.badlogic.gdx.net.Socket;
import com.badlogic.gdx.net.SocketHints; import com.badlogic.gdx.net.SocketHints;
import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Actor;
@ -24,17 +25,20 @@ import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Json; import com.badlogic.gdx.utils.BufferUtils;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.SerializationException;
import com.riiablo.CharData; import com.riiablo.CharData;
import com.riiablo.Riiablo; import com.riiablo.Riiablo;
import com.riiablo.codec.DC6; import com.riiablo.codec.DC6;
import com.riiablo.graphics.PaletteIndexedBatch; import com.riiablo.graphics.PaletteIndexedBatch;
import com.riiablo.loader.DC6Loader; import com.riiablo.loader.DC6Loader;
import com.riiablo.net.GameSession;
import com.riiablo.net.packet.mcp.ListGames;
import com.riiablo.net.packet.mcp.MCP;
import com.riiablo.net.packet.mcp.MCPData;
import com.riiablo.server.Account; import com.riiablo.server.Account;
import com.riiablo.server.Session;
import com.riiablo.server.SessionError;
import com.riiablo.util.EventUtils; import com.riiablo.util.EventUtils;
import com.riiablo.widget.Label; import com.riiablo.widget.Label;
import com.riiablo.widget.TextArea; import com.riiablo.widget.TextArea;
@ -45,13 +49,20 @@ import org.apache.commons.io.IOUtils;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.net.ConnectException; import java.nio.ByteBuffer;
import java.net.SocketTimeoutException; import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.concurrent.atomic.AtomicBoolean;
public class LobbyScreen extends ScreenAdapter { public class LobbyScreen extends ScreenAdapter {
private static final String TAG = "LobbyScreen"; private static final String TAG = "LobbyScreen";
private static final boolean DEBUG = true;
private static final boolean DEBUG_CONNECTION = DEBUG && true;
// FIXME: This background is not feasible and will always require shaving, easier to just use // FIXME: This background is not feasible and will always require shaving, easier to just use
// component panels and button groups to create my own? // component panels and button groups to create my own?
@ -91,6 +102,8 @@ public class LobbyScreen extends ScreenAdapter {
private PrintWriter out; private PrintWriter out;
private BufferedReader in; private BufferedReader in;
private Connection connection;
public LobbyScreen(Account account, CharData player) { public LobbyScreen(Account account, CharData player) {
this.account = account; this.account = account;
this.player = player; this.player = player;
@ -277,47 +290,47 @@ public class LobbyScreen extends ScreenAdapter {
btnCreateGame.addListener(new ClickListener() { btnCreateGame.addListener(new ClickListener() {
@Override @Override
public void clicked(InputEvent event, float x, float y) { public void clicked(InputEvent event, float x, float y) {
Net.HttpRequest request = new HttpRequestBuilder() // Net.HttpRequest request = new HttpRequestBuilder()
.newRequest() // .newRequest()
.method(Net.HttpMethods.POST) // .method(Net.HttpMethods.POST)
.url("http://" + Riiablo.client.getRealm() + ":6112/create-session") // .url("http://" + Riiablo.client.getRealm() + ":6112/create-session")
.jsonContent(new Session.Builder() {{ // .jsonContent(new Session.Builder() {{
name = tfGameName.getText(); // name = tfGameName.getText();
password = tfPassword.getText(); // password = tfPassword.getText();
desc = tfDesc.getText(); // desc = tfDesc.getText();
}}) // }})
.build(); // .build();
Gdx.net.sendHttpRequest(request, new Net.HttpResponseListener() { // Gdx.net.sendHttpRequest(request, new Net.HttpResponseListener() {
@Override // @Override
public void handleHttpResponse(Net.HttpResponse httpResponse) { // public void handleHttpResponse(Net.HttpResponse httpResponse) {
String response = httpResponse.getResultAsString(); // String response = httpResponse.getResultAsString();
try { // try {
final Session session = new Json().fromJson(Session.class, response); // final Session session = new Json().fromJson(Session.class, response);
Gdx.app.log(TAG, "create-session " + response); // Gdx.app.log(TAG, "create-session " + response);
Gdx.app.postRunnable(new Runnable() { // Gdx.app.postRunnable(new Runnable() {
@Override // @Override
public void run() { // public void run() {
Socket socket = Gdx.net.newClientSocket(Net.Protocol.TCP, session.host, session.port, null); // Socket socket = Gdx.net.newClientSocket(Net.Protocol.TCP, session.host, session.port, null);
Gdx.app.log(TAG, "create-session connect " + session.host + ":" + session.port + " " + socket.isConnected()); // Gdx.app.log(TAG, "create-session connect " + session.host + ":" + session.port + " " + socket.isConnected());
Riiablo.client.pushScreen(new GameLoadingScreen(new GameScreen(player, socket))); // Riiablo.client.pushScreen(new GameLoadingScreen(new GameScreen(player, socket)));
} // }
}); // });
} catch (SerializationException e) { // } catch (SerializationException e) {
SessionError error = new Json().fromJson(SessionError.class, response); // SessionError error = new Json().fromJson(SessionError.class, response);
Gdx.app.log(TAG, "create-session " + error.toString()); // Gdx.app.log(TAG, "create-session " + error.toString());
} // }
} // }
//
@Override // @Override
public void failed(Throwable t) { // public void failed(Throwable t) {
Gdx.app.log(TAG, "create-session " + t.getMessage()); // Gdx.app.log(TAG, "create-session " + t.getMessage());
} // }
//
@Override // @Override
public void cancelled() { // public void cancelled() {
Gdx.app.log(TAG, "create-session " + "cancelled"); // Gdx.app.log(TAG, "create-session " + "cancelled");
} // }
}); // });
} }
}); });
tfGameName.addListener(new ChangeListener() { tfGameName.addListener(new ChangeListener() {
@ -392,7 +405,7 @@ public class LobbyScreen extends ScreenAdapter {
List.ListStyle style3 = new List.ListStyle(Riiablo.fonts.fontformal10, Riiablo.colors.gold, Riiablo.colors.white, List.ListStyle style3 = new List.ListStyle(Riiablo.fonts.fontformal10, Riiablo.colors.gold, Riiablo.colors.white,
new TextureRegionDrawable(Riiablo.textures.white)); new TextureRegionDrawable(Riiablo.textures.white));
final List<Session> list = new List<>(style3); final List<GameSession> list = new List<>(style3);
list.setPosition(14, 54); list.setPosition(14, 54);
list.setSize(158, 177); list.setSize(158, 177);
addActor(list); addActor(list);
@ -412,7 +425,7 @@ public class LobbyScreen extends ScreenAdapter {
public void changed(ChangeEvent event, Actor actor) { public void changed(ChangeEvent event, Actor actor) {
list.getSelection().setRequired(true); list.getSelection().setRequired(true);
btnJoinGame.setDisabled(list.getSelection().isEmpty()); btnJoinGame.setDisabled(list.getSelection().isEmpty());
Session selected = list.getSelected(); GameSession selected = list.getSelected();
tfGameName.setText(selected != null ? selected.toString() : ""); tfGameName.setText(selected != null ? selected.toString() : "");
} }
}); });
@ -420,14 +433,14 @@ public class LobbyScreen extends ScreenAdapter {
@Override @Override
public void clicked(InputEvent event, float x, float y) { public void clicked(InputEvent event, float x, float y) {
if (btnJoinGame.isDisabled()) return; if (btnJoinGame.isDisabled()) return;
final Session session = list.getSelected(); final GameSession session = list.getSelected();
Gdx.app.log(TAG, "join-session " + session); Gdx.app.log(TAG, "join-session " + session);
Gdx.app.postRunnable(new Runnable() { Gdx.app.postRunnable(new Runnable() {
@Override @Override
public void run() { public void run() {
Socket socket = Gdx.net.newClientSocket(Net.Protocol.TCP, session.host, session.port, null); // Socket socket = Gdx.net.newClientSocket(Net.Protocol.TCP, session.host, session.port, null);
Gdx.app.log(TAG, "join-session connect " + session.host + ":" + session.port + " " + socket.isConnected()); // Gdx.app.log(TAG, "join-session connect " + session.host + ":" + session.port + " " + socket.isConnected());
Riiablo.client.pushScreen(new GameLoadingScreen(new GameScreen(player, socket))); // Riiablo.client.pushScreen(new GameLoadingScreen(new GameScreen(player, socket)));
} }
}); });
} }
@ -459,31 +472,22 @@ public class LobbyScreen extends ScreenAdapter {
list.clearItems(); list.clearItems();
list.getSelection().setRequired(false); list.getSelection().setRequired(false);
stage.setKeyboardFocus(tfGameName); stage.setKeyboardFocus(tfGameName);
Net.HttpRequest request = new HttpRequestBuilder()
.newRequest() ListGames(new ResponseListener() {
.method(Net.HttpMethods.GET)
.url("http://" + Riiablo.client.getRealm() + ":6112/get-sessions")
.build();
Gdx.net.sendHttpRequest(request, new Net.HttpResponseListener() {
@Override @Override
public void handleHttpResponse(Net.HttpResponse httpResponse) { public void handleResponse(MCP packet) {
Array<Session> sessions = (Array<Session>) new Json().fromJson(Array.class, httpResponse.getResultAsStream()); Array<GameSession> sessions = new Array<>();
ListGames listGames = (ListGames) packet.data(new ListGames());
for (int i = 0, length = listGames.gamesLength(); i < length; i++) {
sessions.add(new GameSession(listGames.games(i)));
}
Gdx.app.log(TAG, sessions.toString()); Gdx.app.log(TAG, sessions.toString());
list.setItems(sessions); list.setItems(sessions);
} }
@Override @Override
public void failed(Throwable t) { public void failed(Throwable t) {
if (t.getClass() == SocketTimeoutException.class Gdx.app.error(TAG, t.getMessage(), t);
|| t.getClass() == ConnectException.class) {
Gdx.app.log(TAG, t.getMessage());
} else {
Gdx.app.log(TAG, t.getMessage(), t);
}
}
@Override
public void cancelled() {
} }
}); });
} }
@ -521,8 +525,10 @@ public class LobbyScreen extends ScreenAdapter {
Riiablo.input.addProcessor(stage); Riiablo.input.addProcessor(stage);
connect(); connect();
connectToMCP();
} }
// BNCS
private void connect() { private void connect() {
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
@ -540,6 +546,20 @@ public class LobbyScreen extends ScreenAdapter {
}).start(); }).start();
} }
private void connectToMCP() {
assert connection == null;
Socket socket = null;
try {
socket = Gdx.net.newClientSocket(Net.Protocol.TCP, Riiablo.client.getRealm(), 6111, null);
connection = new Connection(socket);
connection.start();
} catch (GdxRuntimeException t) {
Gdx.app.error(TAG, t.getMessage());
if (connection != null) connection.kill.set(true);
else if (socket != null) socket.dispose();
}
}
@Override @Override
public void hide() { public void hide() {
Riiablo.input.removeProcessor(stage); Riiablo.input.removeProcessor(stage);
@ -557,6 +577,7 @@ public class LobbyScreen extends ScreenAdapter {
Riiablo.assets.unload(chatrighttopbuttonsDescriptor.fileName); Riiablo.assets.unload(chatrighttopbuttonsDescriptor.fileName);
Riiablo.assets.unload(cancelbuttonblankDescriptor.fileName); Riiablo.assets.unload(cancelbuttonblankDescriptor.fileName);
Riiablo.assets.unload(gamebuttonblankDescriptor.fileName); Riiablo.assets.unload(gamebuttonblankDescriptor.fileName);
if (connection != null) connection.dispose();
} }
@Override @Override
@ -583,6 +604,30 @@ public class LobbyScreen extends ScreenAdapter {
stage.draw(); stage.draw();
} }
private void process(Socket socket, MCP packet) throws IOException {
switch (packet.dataType()) {
case MCPData.ConnectionClosed:
Gdx.app.debug(TAG, "Connection closed :(");
break;
case MCPData.ConnectionAccepted:
Gdx.app.debug(TAG, "Connection accepted!");
break;
default:
Gdx.app.error(TAG, "Unknown packet type: " + packet.dataType());
}
}
private void ListGames(ResponseListener listener) {
Gdx.app.debug(TAG, "Requesting games list");
FlatBufferBuilder builder = new FlatBufferBuilder();
ListGames.startListGames(builder);
int listGamesOffset = ListGames.endListGames(builder);
int id = MCP.createMCP(builder, MCPData.ListGames, listGamesOffset);
builder.finish(id);
ByteBuffer data = builder.dataBuffer();
connection.sendRequest(data, listener);
}
static class TabbedPane extends Container<TabbedPane.TabGroup> { static class TabbedPane extends Container<TabbedPane.TabGroup> {
TextureRegion defaultBackground; TextureRegion defaultBackground;
ClickListener clickListener; ClickListener clickListener;
@ -665,4 +710,93 @@ public class LobbyScreen extends ScreenAdapter {
@Override public void exited() {} @Override public void exited() {}
} }
} }
private enum State {
PENDING,
WAITING
}
interface ResponseListener {
void handleResponse(MCP packet);
void failed(Throwable t);
}
class Connection extends Thread implements Disposable {
Socket socket;
ByteBuffer buffer = BufferUtils.newByteBuffer(4096);
AtomicBoolean kill = new AtomicBoolean(false);
FlatBufferBuilder builder = new FlatBufferBuilder();
LobbyScreen.State state = LobbyScreen.State.PENDING;
Connection(Socket socket) {
super(Connection.class.getName());
this.socket = socket;
}
public void sendRequest(ByteBuffer data, ResponseListener listener) {
if (state != LobbyScreen.State.WAITING) throw new IllegalStateException("Sending request before connection has been accepted!");
try {
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();
MCP packet = MCP.getRootAsMCP(buffer);
Gdx.app.log(TAG, "packet type " + MCPData.name(packet.dataType()));
listener.handleResponse(packet);
} catch (Throwable t) {
listener.failed(t);
}
}
@Override
public void run() {
Gdx.app.log(TAG, "Connecting to MCP " + socket.getRemoteAddress());
while (!kill.get()) {
try {
switch (state) {
case PENDING: {
if (DEBUG_CONNECTION) Gdx.app.debug(TAG, "pending connection...");
buffer.clear();
buffer.mark();
ReadableByteChannel in = Channels.newChannel(socket.getInputStream());
in.read(buffer);
buffer.limit(buffer.position());
buffer.reset();
MCP packet = MCP.getRootAsMCP(buffer);
Gdx.app.log(TAG, "packet type " + MCPData.name(packet.dataType()));
process(socket, packet);
state = LobbyScreen.State.WAITING;
}
break;
case WAITING:
try {
Thread.sleep(100); // sleep to save cpu
} catch (Throwable ignored) {}
break;
}
} catch (Throwable t) {
Gdx.app.log(TAG, t.getMessage(), t);
kill.set(true);
}
}
Gdx.app.log(TAG, "closing socket...");
if (socket != null) socket.dispose();
}
@Override
public void dispose() {
kill.set(true);
}
}
} }

View File

@ -18,6 +18,7 @@ import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.BufferUtils; import com.badlogic.gdx.utils.BufferUtils;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.GdxRuntimeException; import com.badlogic.gdx.utils.GdxRuntimeException;
import com.riiablo.Riiablo; import com.riiablo.Riiablo;
import com.riiablo.codec.Animation; import com.riiablo.codec.Animation;
@ -125,7 +126,7 @@ public class LoginScreen extends ScreenAdapter {
connection.start(); connection.start();
} catch (GdxRuntimeException t) { } catch (GdxRuntimeException t) {
Gdx.app.error(TAG, t.getMessage()); Gdx.app.error(TAG, t.getMessage());
if (connection != null) connection.kill.set(true); if (connection != null) connection.dispose();
else if (socket != null) socket.dispose(); else if (socket != null) socket.dispose();
} }
} else if (actor == btnAccountSettings) { } else if (actor == btnAccountSettings) {
@ -230,7 +231,7 @@ public class LoginScreen extends ScreenAdapter {
Riiablo.assets.unload(buttonDescriptor.fileName); Riiablo.assets.unload(buttonDescriptor.fileName);
Riiablo.assets.unload(selectDescriptor.fileName); Riiablo.assets.unload(selectDescriptor.fileName);
Riiablo.assets.unload(textbox2Descriptor.fileName); Riiablo.assets.unload(textbox2Descriptor.fileName);
if (connection != null) connection.kill.set(true); if (connection != null) connection.dispose();
} }
@Override @Override
@ -275,7 +276,7 @@ public class LoginScreen extends ScreenAdapter {
ACCEPTED ACCEPTED
} }
private class Connection extends Thread { private class Connection extends Thread implements Disposable {
Socket socket; Socket socket;
ByteBuffer buffer = BufferUtils.newByteBuffer(4096); ByteBuffer buffer = BufferUtils.newByteBuffer(4096);
AtomicBoolean kill = new AtomicBoolean(false); AtomicBoolean kill = new AtomicBoolean(false);
@ -290,6 +291,7 @@ public class LoginScreen extends ScreenAdapter {
@Override @Override
public void run() { public void run() {
Gdx.app.log(TAG, "Connecting to BNLS " + socket.getRemoteAddress());
while (!kill.get()) { while (!kill.get()) {
try { try {
switch (state) { switch (state) {
@ -348,5 +350,10 @@ public class LoginScreen extends ScreenAdapter {
Gdx.app.log(TAG, "closing socket..."); Gdx.app.log(TAG, "closing socket...");
if (socket != null) socket.dispose(); if (socket != null) socket.dispose();
} }
@Override
public void dispose() {
kill.set(true);
}
} }
} }

29
server/mcp/build.gradle Normal file
View File

@ -0,0 +1,29 @@
apply plugin: "java"
sourceCompatibility = 1.7
sourceSets.main.java.srcDirs = [ "src/" ]
sourceSets.test.java.srcDirs = [ "test/" ]
project.ext.mainClassName = "com.riiablo.server.mcp.MCP"
project.ext.assetsDir = new File("../android/assets");
task run(dependsOn: classes, type: JavaExec) {
main = project.mainClassName
classpath = sourceSets.main.runtimeClasspath
standardInput = System.in
workingDir = project.assetsDir
ignoreExitValue = true
}
task dist(type: Jar) {
from files(sourceSets.main.output.classesDir)
from files(sourceSets.main.output.resourcesDir)
from {configurations.compile.collect {zipTree(it)}}
from files(project.assetsDir);
manifest {
attributes 'Server-Class': project.mainClassName
}
}
dist.dependsOn classes

View File

@ -0,0 +1,277 @@
package com.riiablo.server.mcp;
import com.google.flatbuffers.FlatBufferBuilder;
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.math.MathUtils;
import com.badlogic.gdx.net.ServerSocket;
import com.badlogic.gdx.net.Socket;
import com.badlogic.gdx.utils.BufferUtils;
import com.riiablo.net.GameSession;
import com.riiablo.net.packet.bnls.ConnectionAccepted;
import com.riiablo.net.packet.bnls.ConnectionClosed;
import com.riiablo.net.packet.mcp.ListGames;
import com.riiablo.net.packet.mcp.MCPData;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
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;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
public class MCP extends ApplicationAdapter {
private static final String TAG = "MCP";
private static final int PORT = 6111;
private static final int MAX_CLIENTS = 32;
public static void main(String[] args) {
HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration();
new HeadlessApplication(new MCP(), config);
}
ServerSocket server;
ByteBuffer buffer;
Thread main;
AtomicBoolean kill;
Thread cli;
ThreadGroup clientThreads;
CopyOnWriteArrayList<Client> CLIENTS = new CopyOnWriteArrayList<>();
Map<String, GameSession> sessions = new ConcurrentHashMap<>();
{
sessions.put("test1", new GameSession() {{
this.name = "test1";
this.desc = "desc1";
}});
sessions.put("test2", new GameSession() {{
this.name = "test2";
this.desc = "desc2";
}});
sessions.put("test3", new GameSession() {{
this.name = "test3";
this.desc = "desc3";
}});
}
MCP() {}
@Override
public void create() {
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);
}
clientThreads = new ThreadGroup("MCPClients");
Gdx.app.log(TAG, "Starting server...");
server = Gdx.net.newServerSocket(Net.Protocol.TCP, PORT, null);
buffer = BufferUtils.newByteBuffer(4096);
kill = new AtomicBoolean(false);
main = new Thread(new Runnable() {
@Override
public void run() {
while (!kill.get()) {
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 {
try {
ConnectionAccepted(socket);
new Client(socket).start();
} catch (Throwable ignored) {
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...");
}
});
main.setName("MCP");
main.start();
cli = new Thread(new Runnable() {
@Override
public void run() {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (!kill.get()) {
try {
if (!reader.ready()) continue;
String in = reader.readLine();
if (in.equalsIgnoreCase("exit")) {
Gdx.app.exit();
} else if (in.equalsIgnoreCase("games")) {
Gdx.app.log(TAG, "games:");
for (GameSession session : sessions.values()) {
Gdx.app.log(TAG, " " + session);
}
}
} catch (Throwable t) {
Gdx.app.log(TAG, t.getMessage());
}
}
}
});
cli.setName("CLI");
cli.start();
}
@Override
public void dispose() {
Gdx.app.log(TAG, "Shutting down...");
kill.set(true);
server.dispose();
try {
main.join();
} catch (Throwable ignored) {}
}
private void process(Socket socket, com.riiablo.net.packet.mcp.MCP packet) throws IOException {
switch (packet.dataType()) {
case MCPData.ListGames:
ListGames(socket, packet);
break;
// case MCPData.LoginResponse:
// LoginResponse(socket, packet);
// break;
default:
Gdx.app.error(TAG, "Unknown packet type: " + packet.dataType());
}
}
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.mcp.MCP.createMCP(builder, MCPData.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 ConnectionAccepted(Socket socket) throws IOException {
Gdx.app.debug(TAG, "Connection accepted!");
FlatBufferBuilder builder = new FlatBufferBuilder();
ConnectionAccepted.startConnectionAccepted(builder);
int connectionAcceptedId = ConnectionAccepted.endConnectionAccepted(builder);
int id = com.riiablo.net.packet.mcp.MCP.createMCP(builder, MCPData.ConnectionAccepted, connectionAcceptedId);
builder.finish(id);
ByteBuffer data = builder.dataBuffer();
OutputStream out = socket.getOutputStream();
WritableByteChannel channel = Channels.newChannel(out);
channel.write(data);
return false;
}
private boolean ListGames(Socket socket, com.riiablo.net.packet.mcp.MCP packet) throws IOException {
ListGames listGames = (ListGames) packet.data(new ListGames());
Gdx.app.debug(TAG, "Games list requested by " + socket.getRemoteAddress());
FlatBufferBuilder builder = new FlatBufferBuilder();
int i = 0;
int[] sessions = new int[this.sessions.size()];
for (GameSession session : this.sessions.values()) {
sessions[i++] = com.riiablo.net.packet.mcp.GameSession.createGameSession(builder, i, 0, builder.createString(session.name), builder.createString(session.desc), 0);
}
int sessionsVec = ListGames.createGamesVector(builder, sessions);
ListGames.startListGames(builder);
ListGames.addGames(builder, sessionsVec);
int listGamesOffset = ListGames.endListGames(builder);
int id = com.riiablo.net.packet.mcp.MCP.createMCP(builder, MCPData.ListGames, listGamesOffset);
builder.finish(id);
ByteBuffer data = builder.dataBuffer();
OutputStream out = socket.getOutputStream();
WritableByteChannel channel = Channels.newChannel(out);
channel.write(data);
Gdx.app.log(TAG, "returning games 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.clear();
buffer.mark();
ReadableByteChannel in = Channels.newChannel(socket.getInputStream());
if (in.read(buffer) == -1) {
kill.set(true);
break;
}
buffer.limit(buffer.position());
buffer.reset();
com.riiablo.net.packet.mcp.MCP packet = com.riiablo.net.packet.mcp.MCP.getRootAsMCP(buffer);
Gdx.app.log(TAG, "packet type " + MCPData.name(packet.dataType()));
process(socket, packet);
} catch (Throwable t) {
Gdx.app.log(TAG, t.getMessage(), t);
kill.set(true);
}
}
Gdx.app.log(TAG, "closing socket...");
if (socket != null) socket.dispose();
}
}
}

View File

@ -1 +1 @@
include 'tools', 'tester', 'ds1viewer', 'mpqviewer', 'server:bnls', 'desktop', 'android', 'mpqlib', 'core' include 'tools', 'tester', 'ds1viewer', 'mpqviewer', 'server:bnls', 'server:mcp', 'desktop', 'android', 'mpqlib', 'core'