Restoration of backward compatibility (#367)

Since this discussion is going nowhere, I'm restoring it.
This commit is contained in:
Leifa 2024-07-16 21:05:19 +02:00 committed by GitHub
parent 8498e30c02
commit 3459486dea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 130 additions and 174 deletions

View File

@ -31,7 +31,6 @@ INCLUDEPATH += src
SOURCES += \
src/acl_roles_handler.cpp \
src/akashidefs.cpp \
src/aoclient.cpp \
src/network/aopacket.cpp \
src/network/network_socket.cpp \
@ -84,7 +83,6 @@ SOURCES += \
HEADERS += src/aoclient.h \
src/acl_roles_handler.h \
src/akashidefs.h \
src/akashiutils.h \
src/network/aopacket.h \
src/network/network_socket.h \

View File

@ -1,8 +0,0 @@
#include "akashidefs.h"
namespace akashi {
QString get_protocol_version_string()
{
return QString::number(PROTOCOL_MAJOR_VERSION) + "." + QString::number(PROTOCOL_MINOR_VERSION) + "." + QString::number(PROTOCOL_PATCH_VERSION);
}
}

View File

@ -1,21 +0,0 @@
#ifndef AKASHIDEFS_H
#define AKASHIDEFS_H
#include <QString>
namespace akashi {
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
using SplitBehavior = QString::SplitBehavior;
#else
using SplitBehavior = Qt::SplitBehaviorFlags;
#endif
const SplitBehavior KeepEmptyParts = SplitBehavior::KeepEmptyParts;
const SplitBehavior SkipEmptyParts = SplitBehavior::SkipEmptyParts;
QString get_protocol_version_string();
const int PROTOCOL_MAJOR_VERSION = 1;
const int PROTOCOL_MINOR_VERSION = 0;
const int PROTOCOL_PATCH_VERSION = 0;
}
#endif // AKASHIDEFS_H

View File

@ -240,9 +240,9 @@ class AOClient : public QObject
*/
struct ClientVersion
{
int release = -1;
int major = -1;
int minor = -1;
int patch = -1;
};
/**

View File

@ -3,8 +3,6 @@
#include <QDebug>
#include <QSettings>
#include "akashidefs.h"
CommandExtension::CommandExtension() {}
CommandExtension::CommandExtension(QString f_command_name)
@ -139,7 +137,7 @@ bool CommandExtensionCollection::loadFile(QString f_filename)
l_settings.beginGroup(i_group);
QStringList l_aliases = l_settings.value("aliases").toString().split(" ", akashi::SkipEmptyParts);
QStringList l_aliases = l_settings.value("aliases").toString().split(" ", Qt::SkipEmptyParts);
for (QString &i_alias : l_aliases) {
i_alias = i_alias.toLower();
}
@ -155,7 +153,7 @@ bool CommandExtensionCollection::loadFile(QString f_filename)
CommandExtension l_extension(l_command_name);
l_extension.setAliases(l_aliases);
l_extension.setPermissionsByCaption(l_settings.value("permissions").toString().split(" ", akashi::SkipEmptyParts));
l_extension.setPermissionsByCaption(l_settings.value("permissions").toString().split(" ", Qt::SkipEmptyParts));
m_extensions.insert(l_command_name, std::move(l_extension));
l_settings.endGroup();

View File

@ -130,7 +130,7 @@ void AOClient::cmdExamine(int argc, QStringList argv)
AreaData *l_area = server->getAreaById(areaId());
if (l_area->testimony().size() - 1 > 0) {
l_area->restartTestimony();
server->broadcast(PacketFactory::createPacket("RT", {"testimony2"}), areaId());
server->broadcast(PacketFactory::createPacket("RT", {"testimony2", "0"}), areaId());
server->broadcast(PacketFactory::createPacket("MS", {l_area->testimony()[0]}), areaId());
return;
}

View File

@ -1,6 +1,5 @@
#include "packet/packet_ct.h"
#include "akashidefs.h"
#include "config_manager.h"
#include "packet/packet_factory.h"
#include "server.h"
@ -48,7 +47,7 @@ void PacketCT::handlePacket(AreaData *area, AOClient &client) const
return;
AOPacket *final_packet = PacketFactory::createPacket("CT", {client.name(), l_message, "0"});
if (l_message.at(0) == '/') {
QStringList l_cmd_argv = l_message.split(" ", akashi::SkipEmptyParts);
QStringList l_cmd_argv = l_message.split(" ", Qt::SkipEmptyParts);
QString l_command = l_cmd_argv[0].trimmed().toLower();
l_command = l_command.right(l_command.length() - 1);
l_cmd_argv.removeFirst();

View File

@ -48,10 +48,5 @@ void PacketHI::handlePacket(AreaData *area, AOClient &client) const
return;
}
client.sendPacket("PN", {QString::number(client.getServer()->getPlayerCount()), QString::number(ConfigManager::maxPlayers()), ConfigManager::serverDescription()});
if (ConfigManager::assetUrl().isValid()) {
QByteArray l_asset_url = ConfigManager::assetUrl().toEncoded(QUrl::EncodeSpaces);
client.sendPacket("ASS", {l_asset_url});
}
client.sendPacket("ID", {QString::number(client.clientId()), "akashi", QCoreApplication::applicationVersion()});
}

View File

@ -1,6 +1,5 @@
#include "packet/packet_id.h"
#include "akashidefs.h"
#include "config_manager.h"
#include "server.h"
@ -15,7 +14,7 @@ PacketInfo PacketID::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 3,
.min_args = 2,
.header = "ID"};
return info;
}
@ -24,30 +23,46 @@ void PacketID::handlePacket(AreaData *area, AOClient &client) const
{
Q_UNUSED(area)
if (client.m_version.major == akashi::PROTOCOL_MAJOR_VERSION) {
if (client.m_version.release == 2) {
// No double sending of the ID packet!
client.sendPacket("BD", {"A protocol error has been encountered. Packet : ID"});
client.m_socket->close();
return;
}
AOClient::ClientVersion version;
if (m_content[0] == akashi::get_protocol_version_string()) {
version.major = akashi::PROTOCOL_MAJOR_VERSION;
version.minor = akashi::PROTOCOL_MINOR_VERSION;
version.patch = akashi::PROTOCOL_PATCH_VERSION;
}
else {
client.sendPacket("BD", {"A protocol error has been encountered. Packet : ID\nProtocol version not supported."});
client.m_socket->close();
return;
}
if (!ConfigManager::webaoEnabled() && m_content[1] == "webAO") {
if (!ConfigManager::webaoEnabled() && m_content[0] == "webAO") {
client.sendPacket("BD", {"WebAO is disabled on this server."});
client.m_socket->close();
return;
}
client.sendPacket("ID", {QString::number(client.clientId()), "akashi", QCoreApplication::applicationVersion()});
QRegularExpression rx("\\b(\\d+)\\.(\\d+)\\.(\\d+)\\b"); // matches X.X.X (e.g. 2.9.0, 2.4.10, etc.)
QRegularExpressionMatch l_match = rx.match(m_content[1]);
if (l_match.hasMatch()) {
client.m_version.release = l_match.captured(1).toInt();
client.m_version.major = l_match.captured(2).toInt();
client.m_version.minor = l_match.captured(3).toInt();
}
if (client.m_version.release != 2) {
client.sendPacket("BD", {"A protocol error has been encountered. Packet : ID\nRelease version not recognised."});
client.m_socket->close();
return;
}
client.sendPacket("PN", {QString::number(client.getServer()->getPlayerCount()), QString::number(ConfigManager::maxPlayers()), ConfigManager::serverDescription()});
QStringList l_feature_list = {
"noencryption", "yellowtext", "prezoom",
"flipping", "customobjections", "fastloading",
"deskmod", "evidence", "cccc_ic_support",
"arup", "casing_alerts", "modcall_reason",
"looping_sfx", "additive", "effects",
"y_offset", "expanded_desk_mods", "auth_packet", "custom_blips"};
client.sendPacket("FL", l_feature_list);
if (ConfigManager::assetUrl().isValid()) {
QByteArray l_asset_url = ConfigManager::assetUrl().toEncoded(QUrl::EncodeSpaces);
client.sendPacket("ASS", {l_asset_url});
}
}

View File

@ -4,39 +4,6 @@
#include "server.h"
#include <QDebug>
#include <QRegularExpression>
enum CHAT_MESSAGE
{
DESK_MOD,
PRE_EMOTE,
CHAR_NAME,
EMOTE,
MESSAGE,
SIDE,
SFX_NAME,
EMOTE_MOD,
CHAR_ID,
SFX_DELAY,
OBJECTION_MOD,
EVIDENCE_ID,
FLIP,
REALIZATION,
TEXT_COLOR,
SHOWNAME,
OTHER_CHARID,
SELF_OFFSET,
IMMEDIATE,
LOOPING_SFX,
SCREENSHAKE,
FRAME_SCREENSHAKE,
FRAME_REALIZATION,
FRAME_SFX,
ADDITIVE,
EFFECTS,
BLIPNAME,
SLIDE,
};
PacketMS::PacketMS(QStringList &contents) :
AOPacket(contents)
@ -114,7 +81,7 @@ AOPacket *PacketMS::validateIcPacket(AOClient &client) const
<< "3"
<< "4"
<< "5";
QString l_incoming_deskmod = l_incoming_args[DESK_MOD].toString();
QString l_incoming_deskmod = l_incoming_args[0].toString();
if (allowed_desk_mods.contains(l_incoming_deskmod)) {
// **WARNING : THIS IS A HACK!**
// A proper solution would be to deprecate chat as an argument on the clientside
@ -123,41 +90,41 @@ AOPacket *PacketMS::validateIcPacket(AOClient &client) const
l_args.append("1");
}
else {
l_args.append(l_incoming_args[DESK_MOD].toString());
l_args.append(l_incoming_args[0].toString());
}
}
else
return l_invalid;
// preanim
l_args.append(l_incoming_args[PRE_EMOTE].toString());
l_args.append(l_incoming_args[1].toString());
// char name
if (client.character().toLower() != l_incoming_args[CHAR_NAME].toString().toLower()) {
if (client.character().toLower() != l_incoming_args[2].toString().toLower()) {
// Selected char is different from supplied folder name
// This means the user is INI-swapped
if (!area->iniswapAllowed()) {
QStringList l_character_split = l_incoming_args[CHAR_NAME].toString().split("/");
QStringList l_character_split = l_incoming_args[2].toString().split("/");
if (!client.getServer()->getCharacters().contains(l_character_split.at(0), Qt::CaseInsensitive) || l_character_split.contains(".."))
return l_invalid;
}
qDebug() << "INI swap detected from " << client.getIpid();
}
client.m_current_iniswap = l_incoming_args[CHAR_NAME].toString();
l_args.append(l_incoming_args[CHAR_NAME].toString());
client.m_current_iniswap = l_incoming_args[2].toString();
l_args.append(l_incoming_args[2].toString());
// emote
client.m_emote = l_incoming_args[EMOTE].toString();
client.m_emote = l_incoming_args[3].toString();
if (client.m_first_person)
client.m_emote = "";
l_args.append(client.m_emote);
// message text
if (l_incoming_args[MESSAGE].toString().size() > ConfigManager::maxCharacters())
if (l_incoming_args[4].toString().size() > ConfigManager::maxCharacters())
return l_invalid;
// Doublepost prevention. Has to ignore blankposts and testimony commands.
QString l_incoming_msg = client.dezalgo(l_incoming_args[MESSAGE].toString().trimmed());
QString l_incoming_msg = client.dezalgo(l_incoming_args[4].toString().trimmed());
QRegularExpressionMatch match = isTestimonyJumpCommand(client.decodeMessage(l_incoming_msg));
bool msg_is_testimony_cmd = (match.hasMatch() || l_incoming_msg == ">" || l_incoming_msg == "<");
if (!client.m_last_message.isEmpty() // If the last message you sent isn't empty,
@ -186,7 +153,7 @@ AOPacket *PacketMS::validateIcPacket(AOClient &client) const
}
if (client.m_is_disemvoweled) {
QString l_disemvoweled_message = l_incoming_msg.remove(QRegularExpression("[AEIOUaeiou]"));
QString l_disemvoweled_message = l_incoming_msg.remove(QRegExp("[AEIOUaeiou]"));
l_incoming_msg = l_disemvoweled_message;
}
@ -195,15 +162,15 @@ AOPacket *PacketMS::validateIcPacket(AOClient &client) const
// side
// this is validated clientside so w/e
l_args.append(l_incoming_args[SIDE].toString());
if (client.m_pos != l_incoming_args[SIDE].toString()) {
client.m_pos = l_incoming_args[SIDE].toString();
l_args.append(l_incoming_args[5].toString());
if (client.m_pos != l_incoming_args[5].toString()) {
client.m_pos = l_incoming_args[5].toString();
client.m_pos.replace("../", "").replace("..\\", "");
client.updateEvidenceList(client.getServer()->getAreaById(client.areaId()));
}
// sfx name
l_args.append(l_incoming_args[SFX_NAME].toString());
l_args.append(l_incoming_args[6].toString());
// emote modifier
// Now, gather round, y'all. Here is a story that is truly a microcosm of the AO dev experience.
@ -213,7 +180,7 @@ AOPacket *PacketMS::validateIcPacket(AOClient &client) const
// This would crash everyone else's client, and the feature had to be disabled
// But, for some reason, nobody traced the cause of this issue for many many years.
// The serverside fix is needed to ensure invalid values are not sent, because the client sucks
int emote_mod = l_incoming_args[EMOTE_MOD].toInt();
int emote_mod = l_incoming_args[7].toInt();
if (emote_mod == 4)
emote_mod = 6;
@ -222,21 +189,21 @@ AOPacket *PacketMS::validateIcPacket(AOClient &client) const
l_args.append(QString::number(emote_mod));
// char id
if (l_incoming_args[CHAR_ID].toInt() != client.m_char_id)
if (l_incoming_args[8].toInt() != client.m_char_id)
return l_invalid;
l_args.append(l_incoming_args[CHAR_ID].toString());
l_args.append(l_incoming_args[8].toString());
// sfx delay
l_args.append(l_incoming_args[SFX_DELAY].toString());
l_args.append(l_incoming_args[9].toString());
// objection modifier
if (area->isShoutAllowed()) {
if (l_incoming_args[OBJECTION_MOD].toString().contains("4")) {
if (l_incoming_args[10].toString().contains("4")) {
// custom shout includes text metadata
l_args.append(l_incoming_args[OBJECTION_MOD].toString());
l_args.append(l_incoming_args[10].toString());
}
else {
int l_obj_mod = l_incoming_args[OBJECTION_MOD].toInt();
int l_obj_mod = l_incoming_args[10].toInt();
if ((l_obj_mod < 0) || (l_obj_mod > 4)) {
return l_invalid;
}
@ -244,33 +211,33 @@ AOPacket *PacketMS::validateIcPacket(AOClient &client) const
}
}
else {
if (l_incoming_args[OBJECTION_MOD].toString() != "0") {
if (l_incoming_args[10].toString() != "0") {
client.sendServerMessage("Shouts have been disabled in this area.");
}
l_args.append("0");
}
// evidence
int evi_idx = l_incoming_args[EVIDENCE_ID].toInt();
int evi_idx = l_incoming_args[11].toInt();
if (evi_idx > area->evidence().length())
return l_invalid;
l_args.append(QString::number(evi_idx));
// flipping
int l_flip = l_incoming_args[FLIP].toInt();
int l_flip = l_incoming_args[12].toInt();
if (l_flip != 0 && l_flip != 1)
return l_invalid;
client.m_flipping = QString::number(l_flip);
l_args.append(client.m_flipping);
// realization
int realization = l_incoming_args[REALIZATION].toInt();
int realization = l_incoming_args[13].toInt();
if (realization != 0 && realization != 1)
return l_invalid;
l_args.append(QString::number(realization));
// text color
int text_color = l_incoming_args[TEXT_COLOR].toInt();
int text_color = l_incoming_args[14].toInt();
if (text_color < 0 || text_color > 11)
return l_invalid;
l_args.append(QString::number(text_color));
@ -278,7 +245,7 @@ AOPacket *PacketMS::validateIcPacket(AOClient &client) const
// 2.6 packet extensions
if (l_incoming_args.length() >= 19) {
// showname
QString l_incoming_showname = client.dezalgo(l_incoming_args[SHOWNAME].toString().trimmed());
QString l_incoming_showname = client.dezalgo(l_incoming_args[15].toString().trimmed());
if (!(l_incoming_showname == client.character() || l_incoming_showname.isEmpty()) && !area->shownameAllowed()) {
client.sendServerMessage("Shownames are not allowed in this area!");
return l_invalid;
@ -289,7 +256,7 @@ AOPacket *PacketMS::validateIcPacket(AOClient &client) const
}
// if the raw input is not empty but the trimmed input is, use a single space
if (l_incoming_showname.isEmpty() && !l_incoming_args[SHOWNAME].toString().isEmpty())
if (l_incoming_showname.isEmpty() && !l_incoming_args[15].toString().isEmpty())
l_incoming_showname = " ";
l_args.append(l_incoming_showname);
client.setCharacterName(l_incoming_showname);
@ -297,7 +264,7 @@ AOPacket *PacketMS::validateIcPacket(AOClient &client) const
// other char id
// things get a bit hairy here
// don't ask me how this works, because i don't know either
QStringList l_pair_data = l_incoming_args[OTHER_CHARID].toString().split("^");
QStringList l_pair_data = l_incoming_args[16].toString().split("^");
client.m_pairing_with = l_pair_data[0].toInt();
QString l_front_back = "";
if (l_pair_data.length() > 1)
@ -327,15 +294,22 @@ AOPacket *PacketMS::validateIcPacket(AOClient &client) const
l_args.append(l_other_emote);
// self offset
client.m_offset = l_incoming_args[SELF_OFFSET].toString();
client.m_offset = l_incoming_args[17].toString();
// versions 2.6-2.8 cannot validate y-offset so we send them just the x-offset
if ((client.m_version.release == 2) && (client.m_version.major == 6 || client.m_version.major == 7 || client.m_version.major == 8)) {
QString l_x_offset = client.m_offset.split("&")[0];
l_args.append(l_x_offset);
QString l_other_x_offset = l_other_offset.split("&")[0];
l_args.append(l_other_x_offset);
}
else {
l_args.append(client.m_offset);
l_args.append(l_other_offset);
// other flip
}
l_args.append(l_other_flip);
// immediate text processing
int l_immediate = l_incoming_args[IMMEDIATE].toInt();
int l_immediate = l_incoming_args[18].toInt();
if (area->forceImmediate()) {
if (l_args[7] == "1" || l_args[7] == "2") {
l_args[7] = "0";
@ -351,29 +325,31 @@ AOPacket *PacketMS::validateIcPacket(AOClient &client) const
l_args.append(QString::number(l_immediate));
}
// 2.8 packet extensions
if (l_incoming_args.length() >= 26) {
// sfx looping
int l_sfx_loop = l_incoming_args[LOOPING_SFX].toInt();
int l_sfx_loop = l_incoming_args[19].toInt();
if (l_sfx_loop != 0 && l_sfx_loop != 1)
return l_invalid;
l_args.append(QString::number(l_sfx_loop));
// screenshake
int l_screenshake = l_incoming_args[SCREENSHAKE].toInt();
int l_screenshake = l_incoming_args[20].toInt();
if (l_screenshake != 0 && l_screenshake != 1)
return l_invalid;
l_args.append(QString::number(l_screenshake));
// frames shake
l_args.append(l_incoming_args[FRAME_SCREENSHAKE].toString());
l_args.append(l_incoming_args[21].toString());
// frames realization
l_args.append(l_incoming_args[FRAME_REALIZATION].toString());
l_args.append(l_incoming_args[22].toString());
// frames sfx
l_args.append(l_incoming_args[FRAME_SFX].toString());
l_args.append(l_incoming_args[23].toString());
// additive
int l_additive = l_incoming_args[ADDITIVE].toInt();
int l_additive = l_incoming_args[24].toInt();
if (l_additive != 0 && l_additive != 1)
return l_invalid;
else if (area->lastICMessage().isEmpty()) {
@ -388,13 +364,16 @@ AOPacket *PacketMS::validateIcPacket(AOClient &client) const
l_args.append(QString::number(l_additive));
// effect
l_args.append(l_incoming_args[EFFECTS].toString());
l_args.append(l_incoming_args[25].toString());
}
if (l_incoming_args.size() >= 27) {
// blips
l_args.append(l_incoming_args[BLIPNAME].toString());
l_args.append(l_incoming_args[26].toString());
}
if (l_incoming_args.size() >= 28) {
// slide toggle
l_args.append(l_incoming_args[SLIDE].toString());
l_args.append(l_incoming_args[27].toString());
}
// Testimony playback
QString client_name = client.name();
@ -408,7 +387,7 @@ AOPacket *PacketMS::validateIcPacket(AOClient &client) const
if (area->statement() == -1) {
l_args[4] = "~~-- " + l_args[4] + " --";
l_args[14] = "3";
client.getServer()->broadcast(PacketFactory::createPacket("RT", {"testimony1"}), client.areaId());
client.getServer()->broadcast(PacketFactory::createPacket("RT", {"testimony1", "0"}), client.areaId());
}
client.addStatement(l_args);
}

View File

@ -13,7 +13,7 @@ PacketInfo PacketRT::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 2,
.min_args = 1,
.header = "RT"};
return info;
}

View File

@ -19,7 +19,6 @@
#include <QQueue>
#include "akashidefs.h"
#include "area_data.h"
#include "config_manager.h"
#include "db_manager.h"
@ -108,6 +107,8 @@ void AOClient::loginAttempt(QString message)
case DataTypes::AuthType::SIMPLE:
if (message == ConfigManager::modpass()) {
sendPacket("AUTH", {"1"});
if (m_version.release <= 2 && m_version.major <= 9 && m_version.minor <= 0)
sendServerMessage("Logged in as a moderator.");
m_authenticated = true;
m_acl_role_id = ACLRolesHandler::SUPER_ID;
}
@ -133,6 +134,8 @@ void AOClient::loginAttempt(QString message)
m_acl_role_id = server->getDatabaseManager()->getACL(username);
m_moderator_name = username;
sendPacket("AUTH", {"1"});
if (m_version.release <= 2 && m_version.major <= 9 && m_version.minor <= 0)
sendServerMessage("Logged in as a moderator.");
sendServerMessage("Welcome, " + username);
}
else {

View File

@ -1,6 +1,5 @@
#pragma once
#include "akashidefs.h"
#include "aoclient.h"
#include "packet/packet_pr.h"

View File

@ -1,7 +1,6 @@
#include <QObject>
#include <QTest>
#include "akashidefs.h"
#include "network/aopacket.h"
#include "packet/packet_factory.h"
@ -87,7 +86,7 @@ void Packet::createPacketSubclass_data()
<< 2;
QTest::newRow("ID") << "ID#"
<< "ID"
<< 3;
<< 2;
QTest::newRow("MC") << "MC#"
<< "MC"
<< 2;
@ -111,7 +110,7 @@ void Packet::createPacketSubclass_data()
<< 0;
QTest::newRow("RT") << "RT#"
<< "RT"
<< 2;
<< 1;
QTest::newRow("SETCASE") << "SETCASE#"
<< "SETCASE"
<< 7;