mirror of
https://github.com/collinsmith/riiablo.git
synced 2025-02-06 00:57:03 +07:00
Added rudimentary support for BNCS (chat service)
This commit is contained in:
parent
c009982e13
commit
b64900c52c
18
build.gradle
18
build.gradle
@ -234,6 +234,24 @@ project(":server:bnls") {
|
||||
}
|
||||
}
|
||||
|
||||
project(":server:bncs") {
|
||||
apply plugin: "java"
|
||||
|
||||
dependencies {
|
||||
compile project(":core")
|
||||
compile "com.badlogicgames.gdx:gdx-backend-headless:$gdxVersion"
|
||||
compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile group: 'commons-cli', name: 'commons-cli', version: cliVersion
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testCompile 'junit:junit:4.12'
|
||||
}
|
||||
}
|
||||
|
||||
project(":server:mcp") {
|
||||
apply plugin: "java"
|
||||
|
||||
|
40
core/gen/com/riiablo/net/packet/bncs/BNCS.java
Normal file
40
core/gen/com/riiablo/net/packet/bncs/BNCS.java
Normal file
@ -0,0 +1,40 @@
|
||||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
package com.riiablo.net.packet.bncs;
|
||||
|
||||
import com.google.flatbuffers.FlatBufferBuilder;
|
||||
import com.google.flatbuffers.Table;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class BNCS extends Table {
|
||||
public static BNCS getRootAsBNCS(ByteBuffer _bb) { return getRootAsBNCS(_bb, new BNCS()); }
|
||||
public static BNCS getRootAsBNCS(ByteBuffer _bb, BNCS 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 BNCS __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 createBNCS(FlatBufferBuilder builder,
|
||||
byte data_type,
|
||||
int dataOffset) {
|
||||
builder.startObject(2);
|
||||
BNCS.addData(builder, dataOffset);
|
||||
BNCS.addDataType(builder, data_type);
|
||||
return BNCS.endBNCS(builder);
|
||||
}
|
||||
|
||||
public static void startBNCS(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 endBNCS(FlatBufferBuilder builder) {
|
||||
int o = builder.endObject();
|
||||
return o;
|
||||
}
|
||||
public static void finishBNCSBuffer(FlatBufferBuilder builder, int offset) { builder.finish(offset); }
|
||||
public static void finishSizePrefixedBNCSBuffer(FlatBufferBuilder builder, int offset) { builder.finishSizePrefixed(offset); }
|
||||
}
|
||||
|
14
core/gen/com/riiablo/net/packet/bncs/BNCSData.java
Normal file
14
core/gen/com/riiablo/net/packet/bncs/BNCSData.java
Normal file
@ -0,0 +1,14 @@
|
||||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
package com.riiablo.net.packet.bncs;
|
||||
|
||||
public final class BNCSData {
|
||||
private BNCSData() { }
|
||||
public static final byte NONE = 0;
|
||||
public static final byte ChatEvent = 1;
|
||||
|
||||
public static final String[] names = { "NONE", "ChatEvent", };
|
||||
|
||||
public static String name(int e) { return names[e]; }
|
||||
}
|
||||
|
46
core/gen/com/riiablo/net/packet/bncs/ChatEvent.java
Normal file
46
core/gen/com/riiablo/net/packet/bncs/ChatEvent.java
Normal file
@ -0,0 +1,46 @@
|
||||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
package com.riiablo.net.packet.bncs;
|
||||
|
||||
import com.google.flatbuffers.FlatBufferBuilder;
|
||||
import com.google.flatbuffers.Table;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class ChatEvent extends Table {
|
||||
public static ChatEvent getRootAsChatEvent(ByteBuffer _bb) { return getRootAsChatEvent(_bb, new ChatEvent()); }
|
||||
public static ChatEvent getRootAsChatEvent(ByteBuffer _bb, ChatEvent 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 ChatEvent __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; }
|
||||
|
||||
public byte eid() { int o = __offset(4); return o != 0 ? bb.get(o + bb_pos) : 19; }
|
||||
public String name() { int o = __offset(6); return o != 0 ? __string(o + bb_pos) : null; }
|
||||
public ByteBuffer nameAsByteBuffer() { return __vector_as_bytebuffer(6, 1); }
|
||||
public ByteBuffer nameInByteBuffer(ByteBuffer _bb) { return __vector_in_bytebuffer(_bb, 6, 1); }
|
||||
public String text() { int o = __offset(8); return o != 0 ? __string(o + bb_pos) : null; }
|
||||
public ByteBuffer textAsByteBuffer() { return __vector_as_bytebuffer(8, 1); }
|
||||
public ByteBuffer textInByteBuffer(ByteBuffer _bb) { return __vector_in_bytebuffer(_bb, 8, 1); }
|
||||
|
||||
public static int createChatEvent(FlatBufferBuilder builder,
|
||||
byte eid,
|
||||
int nameOffset,
|
||||
int textOffset) {
|
||||
builder.startObject(3);
|
||||
ChatEvent.addText(builder, textOffset);
|
||||
ChatEvent.addName(builder, nameOffset);
|
||||
ChatEvent.addEid(builder, eid);
|
||||
return ChatEvent.endChatEvent(builder);
|
||||
}
|
||||
|
||||
public static void startChatEvent(FlatBufferBuilder builder) { builder.startObject(3); }
|
||||
public static void addEid(FlatBufferBuilder builder, byte eid) { builder.addByte(0, eid, 19); }
|
||||
public static void addName(FlatBufferBuilder builder, int nameOffset) { builder.addOffset(1, nameOffset, 0); }
|
||||
public static void addText(FlatBufferBuilder builder, int textOffset) { builder.addOffset(2, textOffset, 0); }
|
||||
public static int endChatEvent(FlatBufferBuilder builder) {
|
||||
int o = builder.endObject();
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
29
core/gen/com/riiablo/net/packet/bncs/EID.java
Normal file
29
core/gen/com/riiablo/net/packet/bncs/EID.java
Normal file
@ -0,0 +1,29 @@
|
||||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
package com.riiablo.net.packet.bncs;
|
||||
|
||||
public final class EID {
|
||||
private EID() { }
|
||||
public static final byte EID_SHOWUSER = 1;
|
||||
public static final byte EID_JOIN = 2;
|
||||
public static final byte EID_LEAVE = 3;
|
||||
public static final byte EID_WHISPER = 4;
|
||||
public static final byte EID_TALK = 5;
|
||||
public static final byte EID_BROADCAST = 6;
|
||||
public static final byte EID_CHANNEL = 7;
|
||||
public static final byte EID_USERFLAGS = 9;
|
||||
public static final byte EID_WHISPERSENT = 10;
|
||||
public static final byte EID_CHANNELFULL = 13;
|
||||
public static final byte EID_CHANNELDOESNOTEXIST = 14;
|
||||
public static final byte EID_CHANNELRESTRICTED = 15;
|
||||
public static final byte EID_INFO = 18;
|
||||
public static final byte EID_ERROR = 19;
|
||||
public static final byte EID_IGNORE = 21;
|
||||
public static final byte EID_ACCEPT = 22;
|
||||
public static final byte EID_EMOTE = 23;
|
||||
|
||||
public static final String[] names = { "EID_SHOWUSER", "EID_JOIN", "EID_LEAVE", "EID_WHISPER", "EID_TALK", "EID_BROADCAST", "EID_CHANNEL", "", "EID_USERFLAGS", "EID_WHISPERSENT", "", "", "EID_CHANNELFULL", "EID_CHANNELDOESNOTEXIST", "EID_CHANNELRESTRICTED", "", "", "EID_INFO", "EID_ERROR", "", "EID_IGNORE", "EID_ACCEPT", "EID_EMOTE", };
|
||||
|
||||
public static String name(int e) { return names[e - EID_SHOWUSER]; }
|
||||
}
|
||||
|
13
core/src/com/riiablo/net/bncs/BNCS.fbs
Normal file
13
core/src/com/riiablo/net/bncs/BNCS.fbs
Normal file
@ -0,0 +1,13 @@
|
||||
include "ChatEvent.fbs";
|
||||
|
||||
namespace com.riiablo.net.packet.bncs;
|
||||
|
||||
union BNCSData {
|
||||
ChatEvent,
|
||||
}
|
||||
|
||||
table BNCS {
|
||||
data:BNCSData;
|
||||
}
|
||||
|
||||
root_type BNCS;
|
27
core/src/com/riiablo/net/bncs/ChatEvent.fbs
Normal file
27
core/src/com/riiablo/net/bncs/ChatEvent.fbs
Normal file
@ -0,0 +1,27 @@
|
||||
namespace com.riiablo.net.packet.bncs;
|
||||
|
||||
enum EID : byte {
|
||||
EID_SHOWUSER = 0x01, // User in channel
|
||||
EID_JOIN = 0x02, // User joined channel
|
||||
EID_LEAVE = 0x03, // User left channel
|
||||
EID_WHISPER = 0x04, // Recieved whisper
|
||||
EID_TALK = 0x05, // Chat text
|
||||
EID_BROADCAST = 0x06, // Server broadcast
|
||||
EID_CHANNEL = 0x07, // Channel information
|
||||
EID_USERFLAGS = 0x09, // Flags update
|
||||
EID_WHISPERSENT = 0x0A, // Sent whisper
|
||||
EID_CHANNELFULL = 0x0D, // Channel full
|
||||
EID_CHANNELDOESNOTEXIST = 0x0E, // Channel doesn't exist
|
||||
EID_CHANNELRESTRICTED = 0x0F, // Channel is restricted
|
||||
EID_INFO = 0x12, // Information
|
||||
EID_ERROR = 0x13, // Error message
|
||||
EID_IGNORE = 0x15, // Notifies that a user has been ignored (DEFUNCT)
|
||||
EID_ACCEPT = 0x16, // Notifies that a user has been unignored (DEFUNCT)
|
||||
EID_EMOTE = 0x17, // Emote
|
||||
}
|
||||
|
||||
table ChatEvent {
|
||||
eid:EID = EID_ERROR;
|
||||
name:string;
|
||||
text:string;
|
||||
}
|
29
server/bncs/build.gradle
Normal file
29
server/bncs/build.gradle
Normal 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.bncs.BNCS"
|
||||
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
|
257
server/bncs/src/com/riiablo/server/bncs/Main.java
Normal file
257
server/bncs/src/com/riiablo/server/bncs/Main.java
Normal file
@ -0,0 +1,257 @@
|
||||
package com.riiablo.server.bncs;
|
||||
|
||||
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.math.MathUtils;
|
||||
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 java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.text.DateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class Main extends ApplicationAdapter {
|
||||
private static final String TAG = "D2CS";
|
||||
|
||||
private static final int PORT = 6113;
|
||||
private static final int MAX_CLIENTS = 32;
|
||||
|
||||
ServerSocket server;
|
||||
ByteBuffer buffer;
|
||||
Thread main;
|
||||
AtomicBoolean kill;
|
||||
ThreadGroup clientThreads;
|
||||
final Array<Client> clients = new Array<>(MAX_CLIENTS);
|
||||
final Array<Packet> packets = new Array<>(32);
|
||||
final Array<Packet> cache = new Array<>(32);
|
||||
|
||||
public static void main(String[] args) {
|
||||
HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration();
|
||||
new HeadlessApplication(new Main(), config);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
clientThreads = new ThreadGroup("D2CSClients");
|
||||
|
||||
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());
|
||||
synchronized (clients) {
|
||||
if (clients.size >= MAX_CLIENTS) {
|
||||
try {
|
||||
ConnectionDenied(socket, "Server is Full");
|
||||
} catch (Throwable ignored) {
|
||||
} finally {
|
||||
socket.dispose();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
ConnectionAccepted(socket);
|
||||
int id = clients.size;
|
||||
Client client = new Client(id, socket);
|
||||
clients.add(client);
|
||||
client.start();
|
||||
} catch (Throwable ignored) {
|
||||
socket.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Gdx.app.log(TAG, "killing child threads...");
|
||||
synchronized (clients) {
|
||||
for (Client client : clients) {
|
||||
if (client != null) {
|
||||
client.kill.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
clients.clear();
|
||||
}
|
||||
|
||||
Gdx.app.log(TAG, "killing thread...");
|
||||
}
|
||||
});
|
||||
main.setName("D2CS");
|
||||
main.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
Gdx.app.log(TAG, "Shutting down...");
|
||||
kill.set(true);
|
||||
server.dispose();
|
||||
try {
|
||||
main.join();
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
synchronized (packets) {
|
||||
cache.clear();
|
||||
cache.addAll(packets);
|
||||
packets.clear();
|
||||
}
|
||||
|
||||
for (Packet packet : cache) {
|
||||
process(packet);
|
||||
}
|
||||
}
|
||||
|
||||
private void process(Packet packet) {
|
||||
synchronized (clients) {
|
||||
for (Client client : clients) {
|
||||
try {
|
||||
client.socket.getOutputStream().write(packet.data);
|
||||
} catch (Throwable t) {
|
||||
Gdx.app.error(TAG, t.getMessage(), t);
|
||||
client.kill.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
// BNCS data = packet.data;
|
||||
// switch (data.dataType()) {
|
||||
// case BNCSData.ChatEvent:
|
||||
// synchronized (clients) {
|
||||
// for (Client client : clients) {
|
||||
// }
|
||||
// }
|
||||
// break;
|
||||
// default:
|
||||
// Gdx.app.error(TAG, "Unknown packet type: " + data.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.d2gs.D2GS.createD2GS(builder, D2GSData.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.d2gs.D2GS.createD2GS(builder, D2GSData.ConnectionAccepted, connectionAcceptedId);
|
||||
// builder.finish(id);
|
||||
//
|
||||
// ByteBuffer data = builder.dataBuffer();
|
||||
// OutputStream out = socket.getOutputStream();
|
||||
// WritableByteChannel channel = Channels.newChannel(out);
|
||||
// channel.write(data);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void process(int id, byte[] packet) {
|
||||
synchronized (packets) {
|
||||
packets.add(Packet.of(id, packet));
|
||||
}
|
||||
}
|
||||
|
||||
static String generateClientName() {
|
||||
return String.format("Client-%08X", MathUtils.random(1, Integer.MAX_VALUE - 1));
|
||||
}
|
||||
|
||||
private class Client extends Thread {
|
||||
int id;
|
||||
Socket socket;
|
||||
AtomicBoolean kill = new AtomicBoolean(false);
|
||||
|
||||
Client(int id, Socket socket) {
|
||||
super(clientThreads, generateClientName());
|
||||
this.id = id;
|
||||
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();
|
||||
|
||||
byte[] data = com.riiablo.util.BufferUtils.readRemaining(buffer);
|
||||
|
||||
/*
|
||||
BNCS packet = BNCS.getRootAsBNCS(buffer);
|
||||
Gdx.app.log(TAG, "packet type " + D2GSData.name(packet.dataType()));
|
||||
*/
|
||||
process(id, data);
|
||||
} catch (Throwable t) {
|
||||
Gdx.app.log(TAG, t.getMessage(), t);
|
||||
kill.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
Gdx.app.log(TAG, "closing socket...");
|
||||
if (socket != null) socket.dispose();
|
||||
synchronized (clients) {
|
||||
clients.removeValue(this, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Packet {
|
||||
int id;
|
||||
byte[] data;
|
||||
|
||||
static Packet of(int id, byte[] data) {
|
||||
Packet packet = new Packet();
|
||||
packet.id = id;
|
||||
packet.data = data;
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
}
|
@ -3,4 +3,4 @@ include 'desktop'
|
||||
include 'android'
|
||||
include 'tools', 'ds1viewer', 'mpqviewer'
|
||||
include 'tester', 'mpqlib'
|
||||
include 'server:bnls', 'server:mcp', 'server:d2gs'
|
||||
include 'server:bnls', 'server:bncs', 'server:mcp', 'server:d2gs'
|
||||
|
Loading…
Reference in New Issue
Block a user