refactor packet handler
This commit is contained in:
parent
20492c6a33
commit
704366a2a0
@ -30,6 +30,7 @@ SOURCES += src/advertiser.cpp \
|
||||
src/config_manager.cpp \
|
||||
src/icchatpacket.cpp \
|
||||
src/main.cpp \
|
||||
src/packets.cpp \
|
||||
src/server.cpp \
|
||||
src/ws_client.cpp \
|
||||
src/ws_proxy.cpp
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "include/aopacket.h"
|
||||
#include "include/server.h"
|
||||
#include "include/icchatpacket.h"
|
||||
#include "include/area_data.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@ -74,6 +75,47 @@ class AOClient : public QObject {
|
||||
void fullArup();
|
||||
void sendServerMessage(QString message);
|
||||
|
||||
// Packet headers
|
||||
void pktDefault(AreaData* area, int argc, QStringList argv, AOPacket packet);
|
||||
void pktHardwareId(AreaData* area, int argc, QStringList argv, AOPacket packet);
|
||||
void pktSoftwareId(AreaData* area, int argc, QStringList argv, AOPacket packet);
|
||||
void pktBeginLoad(AreaData* area, int argc, QStringList argv, AOPacket packet);
|
||||
void pktRequestChars(AreaData* area, int argc, QStringList argv, AOPacket packet);
|
||||
void pktRequestMusic(AreaData* area, int argc, QStringList argv, AOPacket packet);
|
||||
void pktLoadingDone(AreaData* area, int argc, QStringList argv, AOPacket packet);
|
||||
void pktCharPassword(AreaData* area, int argc, QStringList argv, AOPacket packet);
|
||||
void pktSelectChar(AreaData* area, int argc, QStringList argv, AOPacket packet);
|
||||
void pktIcChat(AreaData* area, int argc, QStringList argv, AOPacket packet);
|
||||
void pktOocChat(AreaData* area, int argc, QStringList argv, AOPacket packet);
|
||||
void pktPing(AreaData* area, int argc, QStringList argv, AOPacket packet);
|
||||
void pktChangeMusic(AreaData* area, int argc, QStringList argv, AOPacket packet);
|
||||
void pktWtCe(AreaData* area, int argc, QStringList argv, AOPacket packet);
|
||||
void pktHpBar(AreaData* area, int argc, QStringList argv, AOPacket packet);
|
||||
void pktWebSocketIp(AreaData* area, int argc, QStringList argv, AOPacket packet);
|
||||
|
||||
struct PacketInfo {
|
||||
int minArgs;
|
||||
void (AOClient::*action)(AreaData*, int, QStringList, AOPacket);
|
||||
};
|
||||
|
||||
const QMap<QString, PacketInfo> packets {
|
||||
{"HI", {1, &AOClient::pktHardwareId}},
|
||||
{"ID", {2, &AOClient::pktSoftwareId}},
|
||||
{"askchaa", {0, &AOClient::pktBeginLoad}},
|
||||
{"RC", {0, &AOClient::pktRequestChars}},
|
||||
{"RM", {0, &AOClient::pktRequestMusic}},
|
||||
{"RD", {0, &AOClient::pktLoadingDone}},
|
||||
{"PW", {1, &AOClient::pktCharPassword}},
|
||||
{"CC", {3, &AOClient::pktSelectChar}},
|
||||
{"MS", {1, &AOClient::pktIcChat}}, // TODO: doublecheck
|
||||
{"CT", {2, &AOClient::pktOocChat}},
|
||||
{"CH", {1, &AOClient::pktPing}},
|
||||
{"MC", {2, &AOClient::pktChangeMusic}},
|
||||
{"RT", {1, &AOClient::pktWtCe}},
|
||||
{"HP", {2, &AOClient::pktHpBar}},
|
||||
{"WSIP", {1, &AOClient::pktWebSocketIp}}
|
||||
};
|
||||
|
||||
// Commands
|
||||
void cmdDefault(int argc, QStringList argv);
|
||||
void cmdLogin(int argc, QStringList argv);
|
||||
|
177
src/aoclient.cpp
177
src/aoclient.cpp
@ -71,179 +71,16 @@ void AOClient::clientDisconnected()
|
||||
void AOClient::handlePacket(AOPacket packet)
|
||||
{
|
||||
// TODO: like everything here should send a signal
|
||||
//qDebug() << "Received packet:" << packet.header << ":" << packet.contents;
|
||||
// qDebug() << "Received packet:" << packet.header << ":" << packet.contents;
|
||||
AreaData* area = server->areas[current_area];
|
||||
// Lord forgive me
|
||||
if (packet.header == "HI") {
|
||||
setHwid(packet.contents[0]);
|
||||
if(server->ban_manager->isHDIDBanned(getHwid())) {
|
||||
sendPacket("BD", {server->ban_manager->getBanReason(getHwid())});
|
||||
socket->close();
|
||||
return;
|
||||
}
|
||||
sendPacket("ID", {"271828", "akashi", QCoreApplication::applicationVersion()});
|
||||
}
|
||||
else if (packet.header == "ID") {
|
||||
QSettings config("config/config.ini", QSettings::IniFormat);
|
||||
config.beginGroup("Options");
|
||||
QString max_players = config.value("max_players").toString();
|
||||
config.endGroup();
|
||||
PacketInfo info = packets.value(packet.header, {0, &AOClient::pktDefault});
|
||||
|
||||
// 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_alerts", "modcall_reason",
|
||||
"looping_sfx", "additive", "effects"};
|
||||
if (packet.contents.length() < info.minArgs) {
|
||||
qDebug() << "Invalid packet args length. Minimum is" << info.minArgs << "but only" << packet.contents.length() << "were given.";
|
||||
return;
|
||||
}
|
||||
|
||||
sendPacket("PN", {QString::number(server->player_count), max_players});
|
||||
sendPacket("FL", feature_list);
|
||||
}
|
||||
else if (packet.header == "askchaa") {
|
||||
// TODO: add user configurable content
|
||||
// For testing purposes, we will just send enough to get things working
|
||||
sendPacket("SI", {QString::number(server->characters.length()), "0", QString::number(server->area_names.length() + server->music_list.length())});
|
||||
}
|
||||
else if (packet.header == "RC") {
|
||||
sendPacket("SC", server->characters);
|
||||
}
|
||||
else if (packet.header == "RM") {
|
||||
sendPacket("SM", server->area_names + server->music_list);
|
||||
}
|
||||
else if (packet.header == "RD") {
|
||||
server->player_count++;
|
||||
area->player_count++;
|
||||
joined = true;
|
||||
server->updateCharsTaken(area);
|
||||
fullArup(); // Give client all the area data
|
||||
arup(ARUPType::PLAYER_COUNT, true); // Tell everyone there is a new player
|
||||
|
||||
sendPacket("HP", {"1", QString::number(area->def_hp)});
|
||||
sendPacket("HP", {"2", QString::number(area->pro_hp)});
|
||||
sendPacket("FA", server->area_names);
|
||||
sendPacket("OPPASS", {"DEADBEEF"});
|
||||
sendPacket("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 > server->characters.length())
|
||||
return;
|
||||
|
||||
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(area);
|
||||
sendPacket("PV", {"271828", "CID", packet.contents[1]});
|
||||
}
|
||||
else if (packet.header == "MS") {
|
||||
// TODO: validate, validate, validate
|
||||
ICChatPacket ic_packet(packet);
|
||||
if (ic_packet.is_valid)
|
||||
server->broadcast(ic_packet, current_area);
|
||||
}
|
||||
else if (packet.header == "CT") {
|
||||
ooc_name = packet.contents[0];
|
||||
if(packet.contents[1].at(0) == '/') {
|
||||
QStringList argv = packet.contents[1].split(" ", QString::SplitBehavior::SkipEmptyParts);
|
||||
QString command = argv[0].trimmed().toLower();
|
||||
command = command.right(command.length() - 1);
|
||||
argv.removeFirst();
|
||||
int argc = argv.length();
|
||||
handleCommand(command, argc, argv);
|
||||
return;
|
||||
}
|
||||
// TODO: zalgo strip
|
||||
server->broadcast(packet, current_area);
|
||||
}
|
||||
else if (packet.header == "CH") {
|
||||
// Why does this packet exist
|
||||
// At least Crystal made it useful
|
||||
// It is now used for ping measurement
|
||||
sendPacket("CHECK");
|
||||
}
|
||||
else if (packet.header == "MC") {
|
||||
// Due to historical reasons, this
|
||||
// packet has two functions:
|
||||
// Change area, and set music.
|
||||
|
||||
// First, we check if the provided
|
||||
// argument is a valid song
|
||||
QString argument = packet.contents[0];
|
||||
|
||||
for (QString song : server->music_list) {
|
||||
if (song == argument) {
|
||||
// If we have a song, retransmit as-is
|
||||
server->broadcast(packet, current_area);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < server->area_names.length(); i++) {
|
||||
QString area = server->area_names[i];
|
||||
if(area == argument) {
|
||||
changeArea(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (packet.header == "RT") {
|
||||
if (QDateTime::currentDateTime().toSecsSinceEpoch() - last_wtce_time <= 5)
|
||||
return;
|
||||
last_wtce_time = QDateTime::currentDateTime().toSecsSinceEpoch();
|
||||
server->broadcast(packet, current_area);
|
||||
}
|
||||
else if (packet.header == "HP") {
|
||||
if (packet.contents[0] == "1") {
|
||||
area->def_hp = std::min(std::max(0, packet.contents[1].toInt()), 10);
|
||||
}
|
||||
else if (packet.contents[0] == "2") {
|
||||
area->pro_hp = std::min(std::max(0, packet.contents[1].toInt()), 10);
|
||||
}
|
||||
server->broadcast(AOPacket("HP", {"1", QString::number(area->def_hp)}), area->index);
|
||||
server->broadcast(AOPacket("HP", {"2", QString::number(area->pro_hp)}), area->index);
|
||||
}
|
||||
else if (packet.header == "WSIP") {
|
||||
// Special packet to set remote IP from the webao proxy
|
||||
// Only valid if from a local ip
|
||||
if (remote_ip.isLoopback()) {
|
||||
if(server->ban_manager->isIPBanned(QHostAddress(packet.contents[0]))) {
|
||||
sendPacket("BD", {server->ban_manager->getBanReason(QHostAddress(packet.contents[0]))});
|
||||
socket->close();
|
||||
return;
|
||||
}
|
||||
qDebug() << "ws ip set to" << packet.contents[0];
|
||||
remote_ip = QHostAddress(packet.contents[0]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
qDebug() << "Unimplemented packet:" << packet.header;
|
||||
qDebug() << packet.contents;
|
||||
}
|
||||
(this->*(info.action))(area, packet.contents.length(), packet.contents, packet);
|
||||
}
|
||||
|
||||
void AOClient::changeArea(int new_area)
|
||||
|
220
src/packets.cpp
Normal file
220
src/packets.cpp
Normal file
@ -0,0 +1,220 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// akashi - a server for Attorney Online 2 //
|
||||
// Copyright (C) 2020 scatterflower //
|
||||
// //
|
||||
// This program is free software: you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU Affero General Public License as //
|
||||
// published by the Free Software Foundation, either version 3 of the //
|
||||
// License, or (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY{} without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU Affero General Public License for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU Affero General Public License //
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "include/aoclient.h"
|
||||
|
||||
void AOClient::pktDefault(AreaData* area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
qDebug() << "Unimplemented packet:" << packet.header;
|
||||
qDebug() << packet.contents;
|
||||
}
|
||||
|
||||
void AOClient::pktHardwareId(AreaData* area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
setHwid(argv[0]);
|
||||
if(server->ban_manager->isHDIDBanned(getHwid())) {
|
||||
sendPacket("BD", {server->ban_manager->getBanReason(getHwid())});
|
||||
socket->close();
|
||||
return;
|
||||
}
|
||||
sendPacket("ID", {"271828", "akashi", QCoreApplication::applicationVersion()});
|
||||
}
|
||||
|
||||
void AOClient::pktSoftwareId(AreaData* area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
QSettings config("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_alerts", "modcall_reason",
|
||||
"looping_sfx", "additive", "effects"};
|
||||
|
||||
sendPacket("PN", {QString::number(server->player_count), max_players});
|
||||
sendPacket("FL", feature_list);
|
||||
}
|
||||
|
||||
void AOClient::pktBeginLoad(AreaData* area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
// TODO: add user configurable content
|
||||
// For testing purposes, we will just send enough to get things working
|
||||
sendPacket("SI", {QString::number(server->characters.length()), "0", QString::number(server->area_names.length() + server->music_list.length())});
|
||||
}
|
||||
|
||||
void AOClient::pktRequestChars(AreaData* area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
sendPacket("SC", server->characters);
|
||||
}
|
||||
|
||||
void AOClient::pktRequestMusic(AreaData* area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
sendPacket("SM", server->area_names + server->music_list);
|
||||
}
|
||||
|
||||
void AOClient::pktLoadingDone(AreaData* area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
server->player_count++;
|
||||
area->player_count++;
|
||||
joined = true;
|
||||
server->updateCharsTaken(area);
|
||||
fullArup(); // Give client all the area data
|
||||
arup(ARUPType::PLAYER_COUNT, true); // Tell everyone there is a new player
|
||||
|
||||
sendPacket("HP", {"1", QString::number(area->def_hp)});
|
||||
sendPacket("HP", {"2", QString::number(area->pro_hp)});
|
||||
sendPacket("FA", server->area_names);
|
||||
sendPacket("OPPASS", {"DEADBEEF"});
|
||||
sendPacket("DONE");
|
||||
}
|
||||
|
||||
void AOClient::pktCharPassword(AreaData* area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
password = argv[0];
|
||||
}
|
||||
|
||||
void AOClient::pktSelectChar(AreaData* area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
bool argument_ok;
|
||||
int char_id = argv[1].toInt(&argument_ok);
|
||||
if (!argument_ok)
|
||||
return;
|
||||
|
||||
if (current_char != "") {
|
||||
area->characters_taken[current_char] = false;
|
||||
}
|
||||
|
||||
if(char_id > server->characters.length())
|
||||
return;
|
||||
|
||||
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(area);
|
||||
sendPacket("PV", {"271828", "CID", argv[1]});
|
||||
}
|
||||
|
||||
void AOClient::pktIcChat(AreaData* area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
// TODO: validate, validate, validate
|
||||
ICChatPacket ic_packet(packet);
|
||||
if (ic_packet.is_valid)
|
||||
server->broadcast(ic_packet, current_area);
|
||||
}
|
||||
|
||||
void AOClient::pktOocChat(AreaData* area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
ooc_name = argv[0];
|
||||
if(argv[1].at(0) == '/') {
|
||||
QStringList cmd_argv = argv[1].split(" ", QString::SplitBehavior::SkipEmptyParts);
|
||||
QString command = cmd_argv[0].trimmed().toLower();
|
||||
command = command.right(command.length() - 1);
|
||||
cmd_argv.removeFirst();
|
||||
int cmd_argc = argv.length();
|
||||
handleCommand(command, cmd_argc, cmd_argv);
|
||||
return;
|
||||
}
|
||||
// TODO: zalgo strip
|
||||
server->broadcast(packet, current_area);
|
||||
}
|
||||
|
||||
void AOClient::pktPing(AreaData* area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
// Why does this packet exist
|
||||
// At least Crystal made it useful
|
||||
// It is now used for ping measurement
|
||||
sendPacket("CHECK");
|
||||
}
|
||||
|
||||
void AOClient::pktChangeMusic(AreaData* area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
// Due to historical reasons, this
|
||||
// packet has two functions:
|
||||
// Change area, and set music.
|
||||
|
||||
// First, we check if the provided
|
||||
// argument is a valid song
|
||||
QString argument = argv[0];
|
||||
|
||||
for (QString song : server->music_list) {
|
||||
if (song == argument) {
|
||||
// If we have a song, retransmit as-is
|
||||
server->broadcast(packet, current_area);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < server->area_names.length(); i++) {
|
||||
QString area = server->area_names[i];
|
||||
if(area == argument) {
|
||||
changeArea(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AOClient::pktWtCe(AreaData* area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
if (QDateTime::currentDateTime().toSecsSinceEpoch() - last_wtce_time <= 5)
|
||||
return;
|
||||
last_wtce_time = QDateTime::currentDateTime().toSecsSinceEpoch();
|
||||
server->broadcast(packet, current_area);
|
||||
}
|
||||
|
||||
void AOClient::pktHpBar(AreaData* area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
if (argv[0] == "1") {
|
||||
area->def_hp = std::min(std::max(0, argv[1].toInt()), 10);
|
||||
}
|
||||
else if (argv[0] == "2") {
|
||||
area->pro_hp = std::min(std::max(0, argv[1].toInt()), 10);
|
||||
}
|
||||
server->broadcast(AOPacket("HP", {"1", QString::number(area->def_hp)}), area->index);
|
||||
server->broadcast(AOPacket("HP", {"2", QString::number(area->pro_hp)}), area->index);
|
||||
}
|
||||
|
||||
void AOClient::pktWebSocketIp(AreaData* area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
// Special packet to set remote IP from the webao proxy
|
||||
// Only valid if from a local ip
|
||||
if (remote_ip.isLoopback()) {
|
||||
if(server->ban_manager->isIPBanned(QHostAddress(argv[0]))) {
|
||||
sendPacket("BD", {server->ban_manager->getBanReason(QHostAddress(argv[0]))});
|
||||
socket->close();
|
||||
return;
|
||||
}
|
||||
qDebug() << "ws ip set to" << argv[0];
|
||||
remote_ip = QHostAddress(argv[0]);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user