massive refactor ty longbyte

This commit is contained in:
scatterflower 2020-08-26 01:28:00 -05:00
parent 676b4cbf56
commit 772e850d37
7 changed files with 252 additions and 198 deletions

View File

@ -1,4 +1,4 @@
QT += core gui network
QT += core gui network websockets
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

View File

@ -1,13 +1,19 @@
#ifndef AOCLIENT_H
#define AOCLIENT_H
#include "include/aopacket.h"
#include "include/server.h"
#include <QCryptographicHash>
#include <QHostAddress>
#include <QTcpSocket>
class AOClient {
class Server;
class AOClient : public QObject {
Q_OBJECT
public:
AOClient(QHostAddress p_remote_ip);
AOClient(Server* p_server, QTcpSocket* p_socket, QObject* parent = nullptr);
~AOClient();
QString getHwid();
@ -21,7 +27,20 @@ class AOClient {
int current_area;
QString current_char;
public slots:
void clientDisconnected();
void clientData();
void sendPacket(AOPacket packet);
private:
Server* server;
QTcpSocket* socket;
void handlePacket(AOPacket packet);
QString partial_packet;
bool is_partial;
QString hwid;
QString ipid;
};

View File

@ -4,6 +4,7 @@
#include "include/aoclient.h"
#include "include/aopacket.h"
#include "include/area_data.h"
#include "include/ws_proxy.h"
#include <QApplication>
#include <QDebug>
@ -14,37 +15,35 @@
#include <QTcpServer>
#include <QTcpSocket>
class AOClient;
class Server : public QObject {
Q_OBJECT
public:
Server(int p_port, int p_ws_port, QObject* parent = nullptr);
void start();
AOClient* getClient(QString ipid);
void updateCharsTaken(AreaData* area);
void broadcast(AOPacket packet);
int player_count;
QStringList characters;
QVector<AreaData*> areas;
signals:
public slots:
void clientConnected();
void clientDisconnected();
void clientData();
private:
void handlePacket(AOPacket packet, QTcpSocket* socket);
QTcpSocket* getClient(QString ipid);
void broadcast(AOPacket packet);
WSProxy* proxy;
QTcpServer* server;
int port;
int ws_port;
QMap<QTcpSocket*, AOClient*> clients;
QString partial_packet;
bool is_partial;
int player_count;
QStringList characters;
QVector<AreaData*> areas;
QVector<AOClient*> clients;
};
#endif // SERVER_H

22
include/ws_proxy.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef WS_PROXY_H
#define WS_PROXY_H
#include <QMap>
#include <QTcpSocket>
#include <QtWebSockets/QtWebSockets>
class WSProxy : public QObject {
Q_OBJECT
public:
WSProxy(QObject* parent);
public slots:
void wsConnected();
private:
QWebSocketServer* server;
QMap<QWebSocket*, QTcpSocket*> tcp_sockets;
QMap<QTcpSocket*, QWebSocket*> web_sockets;
};
#endif // WS_PROXY_H

View File

@ -1,12 +1,170 @@
#include "include/aoclient.h"
AOClient::AOClient(QHostAddress p_remote_ip)
AOClient::AOClient(Server* p_server, QTcpSocket* p_socket, QObject* parent)
: QObject(parent)
{
socket = p_socket;
server = p_server;
joined = false;
password = "";
current_area = 0;
current_char = "";
remote_ip = p_remote_ip;
remote_ip = p_socket->peerAddress();
}
void AOClient::clientData()
{
QString data = QString::fromUtf8(socket->readAll());
// qDebug() << "From" << client->peerAddress() << ":" << data;
if (is_partial) {
data = partial_packet + data;
}
if (!data.endsWith("%")) {
is_partial = true;
}
QStringList all_packets = data.split("%");
all_packets.removeLast(); // Remove the entry after the last delimiter
for (QString single_packet : all_packets) {
AOPacket packet(single_packet);
handlePacket(packet);
}
}
void AOClient::clientDisconnected()
{
qDebug() << remote_ip << "disconnected";
if (joined)
server->player_count--;
if (current_char != "")
server->areas.value(current_area)->characters_taken[current_char] =
false;
server->updateCharsTaken(server->areas.value(current_area));
}
void AOClient::handlePacket(AOPacket packet)
{
// TODO: like everything here should send a signal
qDebug() << "Received packet:" << packet.header << ":" << packet.contents;
AreaData* area = server->areas.value(current_area);
// Lord forgive me
if (packet.header == "HI") {
setHwid(packet.contents[0]);
AOPacket response(
"ID", {"271828", "akashi", QApplication::applicationVersion()});
sendPacket(response);
}
else if (packet.header == "ID") {
QSettings config("config.ini", QSettings::IniFormat);
config.beginGroup("Options");
QString max_players = config.value("max_players").toString();
config.endGroup();
// Full feature list as of AO 2.8.5
// The only ones that are critical to ensuring the server works are
// "noencryption" and "fastloading"
// TODO: make the rest of these user configurable
QStringList feature_list = {
"noencryption", "yellowtext", "prezoom",
"flipping", "customobjections", "fastloading",
"deskmod", "evidence", "cccc_ic_support",
"arup", "casing_alserts", "modcall_reason",
"looping_sfx", "additive", "effects"};
AOPacket response_pn(
"PN", {QString::number(server->player_count), max_players});
AOPacket response_fl("FL", feature_list);
sendPacket(response_pn);
sendPacket(response_fl);
}
else if (packet.header == "askchaa") {
// TODO: add user configurable content
// For testing purposes, we will just send enough to get things working
AOPacket response(
"SI", {QString::number(server->characters.length()), "0", "1"});
sendPacket(response);
}
else if (packet.header == "RC") {
AOPacket response("SC", server->characters);
sendPacket(response);
}
else if (packet.header == "RM") {
AOPacket response("SM", {"~stop.mp3"});
sendPacket(response);
}
else if (packet.header == "RD") {
server->player_count++;
joined = true;
server->updateCharsTaken(area);
AOPacket response_op("OPPASS", {"DEADBEEF"});
AOPacket response_done("DONE", {});
sendPacket(response_op);
sendPacket(response_done);
}
else if (packet.header == "PW") {
password = packet.contents[0];
}
else if (packet.header == "CC") {
bool argument_ok;
int char_id = packet.contents[1].toInt(&argument_ok);
if (!argument_ok)
return;
if (current_char != "") {
area->characters_taken[current_char] = false;
}
if (char_id >= 0) {
QString char_selected = server->characters[char_id];
bool taken = area->characters_taken.value(char_selected);
if (taken || char_selected == "")
return;
area->characters_taken[char_selected] = true;
current_char = char_selected;
}
else {
current_char = "";
}
server->updateCharsTaken(server->areas.value(current_area));
AOPacket response_pv("PV", {"271828", "CID", packet.contents[1]});
sendPacket(response_pv);
}
else if (packet.header == "MS") {
// TODO: validate, validate, validate
server->broadcast(packet);
}
else if (packet.header == "CT") {
// TODO: commands
// TODO: zalgo strip
server->broadcast(packet);
}
else if (packet.header == "CH") {
// Why does this packet exist
AOPacket response("CHECK", {});
sendPacket(response);
}
else if (packet.header == "whoami") {
AOPacket response(
"CT", {"Made with love", "by scatterflower and windrammer"});
sendPacket(response);
}
else {
qDebug() << "Unimplemented packet:" << packet.header;
qDebug() << packet.contents;
}
socket->flush();
}
void AOClient::sendPacket(AOPacket packet)
{
qDebug() << "Sent packet:" << packet.header << ":" << packet.contents;
socket->write(packet.toUtf8());
socket->flush();
}
QString AOClient::getHwid() { return hwid; }

View File

@ -48,206 +48,52 @@ void Server::start()
void Server::clientConnected()
{
QTcpSocket* client = server->nextPendingConnection();
AOClient* ao_client = new AOClient(client->peerAddress());
clients.insert(client, ao_client);
connect(client, SIGNAL(disconnected()), this, SLOT(clientDisconnected()));
connect(client, SIGNAL(readyRead()), this, SLOT(clientData()));
QTcpSocket* socket = server->nextPendingConnection();
AOClient* client = new AOClient(this, socket, this);
clients.append(client);
connect(socket, &QTcpSocket::disconnected, client,
&AOClient::clientDisconnected);
connect(socket, &QTcpSocket::disconnected, this, [=] {
qDebug() << "removed client" << client->getIpid();
clients.removeAll(client);
delete client;
});
connect(socket, &QTcpSocket::readyRead, client, &AOClient::clientData);
AOPacket decryptor(
"decryptor", {"NOENCRYPT"}); // This is the infamous workaround for
// tsuserver4 It should disable fantacrypt
// completely in any client 2.4.3 or newer
client->write(decryptor.toUtf8());
client->sendPacket(decryptor);
qDebug() << client->peerAddress().toString() << "connected";
qDebug() << client->remote_ip.toString() << "connected";
}
void Server::clientDisconnected()
void Server::updateCharsTaken(AreaData* area)
{
if (QTcpSocket* client = dynamic_cast<QTcpSocket*>(sender())) {
qDebug() << client->peerAddress() << "disconnected";
AOClient* ao_client = clients.value(client);
if (ao_client->joined)
player_count--;
if(ao_client->current_char != "")
areas.value(ao_client->current_area)
->characters_taken[ao_client->current_char] = false;
delete ao_client;
clients.remove(client);
QStringList chars_taken;
for (QString cur_char : area->characters_taken.keys()) {
chars_taken.append(area->characters_taken.value(cur_char)
? QStringLiteral("-1")
: QStringLiteral("0"));
}
}
void Server::clientData()
{
if (QTcpSocket* client = dynamic_cast<QTcpSocket*>(sender())) {
QString data = QString::fromUtf8(client->readAll());
// qDebug() << "From" << client->peerAddress() << ":" << data;
if (is_partial) {
data = partial_packet + data;
}
if (!data.endsWith("%")) {
is_partial = true;
}
QStringList all_packets = data.split("%");
all_packets.removeLast(); // Remove the entry after the last delimiter
for (QString single_packet : all_packets) {
AOPacket packet(single_packet);
handlePacket(packet, client);
}
}
}
void Server::handlePacket(AOPacket packet, QTcpSocket* socket)
{
// TODO: like everything here should send a signal
qDebug() << "Received packet:" << packet.header << ":" << packet.contents;
AOClient* client = clients.value(socket);
AreaData* area = areas.value(client->current_area);
// Lord forgive me
if (packet.header == "HI") {
AOClient* client = clients.value(socket);
client->setHwid(packet.contents[0]);
AOPacket response(
"ID", {"271828", "akashi", QApplication::applicationVersion()});
socket->write(response.toUtf8());
}
else if (packet.header == "ID") {
QSettings config("config.ini", QSettings::IniFormat);
config.beginGroup("Options");
QString max_players = config.value("max_players").toString();
config.endGroup();
// Full feature list as of AO 2.8.5
// The only ones that are critical to ensuring the server works are
// "noencryption" and "fastloading"
// TODO: make the rest of these user configurable
QStringList feature_list = {
"noencryption", "yellowtext", "prezoom",
"flipping", "customobjections", "fastloading",
"deskmod", "evidence", "cccc_ic_support",
"arup", "casing_alserts", "modcall_reason",
"looping_sfx", "additive", "effects"};
AOPacket response_pn("PN",
{QString::number(player_count), max_players});
AOPacket response_fl("FL", feature_list);
socket->write(response_pn.toUtf8());
socket->write(response_fl.toUtf8());
}
else if (packet.header == "askchaa") {
// TODO: add user configurable content
// For testing purposes, we will just send enough to get things working
AOPacket response("SI",
{QString::number(characters.length()), "0", "1"});
qDebug() << response.toString();
socket->write(response.toUtf8());
}
else if (packet.header == "RC") {
AOPacket response("SC", characters);
socket->write(response.toUtf8());
}
else if (packet.header == "RM") {
AOPacket response("SM", {"~stop.mp3"});
socket->write(response.toUtf8());
}
else if (packet.header == "RD") {
player_count++;
client->joined = true;
QStringList chars_taken;
for (QString cur_char : area->characters_taken.keys()) {
chars_taken.append(area->characters_taken.value(cur_char)
? QStringLiteral("-1")
: QStringLiteral("0"));
}
AOPacket response_cc("CharsCheck", chars_taken);
AOPacket response_op("OPPASS", {"DEADBEEF"});
AOPacket response_done("DONE", {});
broadcast(response_cc);
socket->write(response_op.toUtf8());
socket->write(response_done.toUtf8());
}
else if (packet.header == "PW") {
client->password = packet.contents[0];
}
else if (packet.header == "CC") {
bool argument_ok;
int char_id = packet.contents[1].toInt(&argument_ok);
if (!argument_ok)
return;
if (client->current_char != "") {
area->characters_taken[client->current_char] = false;
}
if(char_id >= 0) {
QString char_selected = characters[char_id];
bool taken = area->characters_taken.value(char_selected);
if (taken || char_selected == "")
return;
area->characters_taken[char_selected] = true;
client->current_char = char_selected;
} else {
client->current_char = "";
}
QStringList chars_taken;
for (QString cur_char : area->characters_taken.keys()) {
chars_taken.append(area->characters_taken.value(cur_char)
? QStringLiteral("-1")
: QStringLiteral("0"));
}
AOPacket response_cc("CharsCheck", chars_taken);
AOPacket response_pv("PV", {"271828", "CID", packet.contents[1]});
socket->write(response_pv.toUtf8());
broadcast(response_cc);
}
else if (packet.header == "MS") {
// TODO: validate, validate, validate
broadcast(packet);
}
else if (packet.header == "CT") {
// TODO: commands
// TODO: zalgo strip
broadcast(packet);
}
else if (packet.header == "CH") {
// Why does this packet exist
AOPacket response("CHECK", {});
socket->write(response.toUtf8());
}
else if (packet.header == "what") {
AOPacket response(
"CT", {"Made with love", "by scatterflower and windrammer"});
}
else {
qDebug() << "Unimplemented packet:" << packet.header;
qDebug() << packet.contents;
}
socket->flush();
AOPacket response_cc("CharsCheck", chars_taken);
broadcast(response_cc);
}
void Server::broadcast(AOPacket packet)
{
// TODO: make this selective to the current area only
for (QTcpSocket* client : clients.keys()) {
client->write(packet.toUtf8());
client->flush();
for (AOClient* client : clients) {
client->sendPacket(packet);
}
}
QTcpSocket* Server::getClient(QString ipid)
AOClient* Server::getClient(QString ipid)
{
for (QTcpSocket* client : clients.keys()) {
if (clients.value(client)->getIpid() == ipid)
for (AOClient* client : clients) {
if (client->getIpid() == ipid)
return client;
}
return nullptr;

10
src/ws_proxy.cpp Normal file
View File

@ -0,0 +1,10 @@
#include "include/ws_proxy.h"
WSProxy::WSProxy(QObject* parent) : QObject(parent)
{
server = new QWebSocketServer(QStringLiteral(""),
QWebSocketServer::NonSecureMode, this);
connect(server, SIGNAL(newConnection()), this, SLOT(wsConnected()));
}
void WSProxy::wsConnected() {}