Replace packet handler with templates (#300)

* Replace packet handler with templates

* Move all packet functions into classes

* Liberal use of Q_UNSUED

* Add correct argument count to packets

* Fix windows build issues

* Partially implement argument type validation
* Where applicable.
* Checking if we can convert from a string to a string would be useless

* Fix unit tests for AOPacket

Co-authored-by: scatterflower <marisa@scatterflower.online>
Co-authored-by: Salanto <62221668+Salanto@users.noreply.github.com>
This commit is contained in:
scatterflower 2022-07-07 10:33:41 -05:00 committed by GitHub
parent 66b58b77bf
commit ca84b54597
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
73 changed files with 2432 additions and 1485 deletions

View File

@ -145,8 +145,8 @@ jobs:
- name: Deploy OpenSSL - name: Deploy OpenSSL
run: | run: |
curl https://mirror.firedaemon.com/OpenSSL/openssl-1.1.1o.zip --output openssl-1.1.1o.zip curl https://mirror.firedaemon.com/OpenSSL/openssl-1.1.1p.zip --output openssl-1.1.1p.zip
tar -xf openssl-1.1.1o.zip tar -xf openssl-1.1.1p.zip
copy .\openssl-1.1\x64\bin\libcrypto-1_1-x64.dll .\bin\libcrypto-1_1-x64.dll copy .\openssl-1.1\x64\bin\libcrypto-1_1-x64.dll .\bin\libcrypto-1_1-x64.dll
copy .\openssl-1.1\x64\bin\libssl-1_1-x64.dll .\bin\libssl-1_1-x64.dll copy .\openssl-1.1\x64\bin\libssl-1_1-x64.dll .\bin\libssl-1_1-x64.dll

View File

@ -2,7 +2,8 @@ QT += network websockets core sql
QT -= gui QT -= gui
TEMPLATE = app TEMPLATE = app
CONFIG += c++11 console unix:CONFIG += c++1z console
win32: CONFIG+=c++2a console
coverage { coverage {
LIBS += -lgcov LIBS += -lgcov

View File

@ -6,7 +6,8 @@ TEMPLATE = lib
# Apparently, Windows needs a static config to make a dynamic library? # Apparently, Windows needs a static config to make a dynamic library?
# Look, I dunno. # Look, I dunno.
# Linux works just fine with `shared` only. # Linux works just fine with `shared` only.
CONFIG += shared static c++11 unix: CONFIG += shared static c++1z
win32: CONFIG+= shared static c++2a
coverage { coverage {
QMAKE_CXXFLAGS += --coverage -g -Og # -fprofile-arcs -ftest-coverage QMAKE_CXXFLAGS += --coverage -g -Og # -fprofile-arcs -ftest-coverage
@ -28,7 +29,7 @@ DESTDIR = $$PWD/../bin
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
# Enable this to print network messages to the console # Enable this to print network messages to the console
#DEFINES += NET_DEBUG DEFINES += NET_DEBUG
SOURCES += \ SOURCES += \
src/acl_roles_handler.cpp \ src/acl_roles_handler.cpp \
@ -55,11 +56,34 @@ SOURCES += \
src/logger/u_logger.cpp \ src/logger/u_logger.cpp \
src/logger/writer_modcall.cpp \ src/logger/writer_modcall.cpp \
src/logger/writer_full.cpp \ src/logger/writer_full.cpp \
src/music_manager.cpp src/music_manager.cpp \
src/packet/packet_factory.cpp \
src/packet/packet_generic.cpp \
src/packet/packet_hi.cpp \
src/packet/packet_id.cpp \
src/packet/packet_askchaa.cpp \
src/packet/packet_casea.cpp \
src/packet/packet_cc.cpp \
src/packet/packet_ch.cpp \
src/packet/packet_ct.cpp \
src/packet/packet_de.cpp \
src/packet/packet_ee.cpp \
src/packet/packet_hp.cpp \
src/packet/packet_mc.cpp \
src/packet/packet_ms.cpp \
src/packet/packet_pe.cpp \
src/packet/packet_pw.cpp \
src/packet/packet_rc.cpp \
src/packet/packet_rd.cpp \
src/packet/packet_rm.cpp \
src/packet/packet_rt.cpp \
src/packet/packet_setcase.cpp \
src/packet/packet_zz.cpp
HEADERS += include/aoclient.h \ HEADERS += include/aoclient.h \
include/acl_roles_handler.h \ include/acl_roles_handler.h \
include/akashidefs.h \ include/akashidefs.h \
include/akashiutils.h \
include/network/aopacket.h \ include/network/aopacket.h \
include/network/network_socket.h \ include/network/network_socket.h \
include/area_data.h \ include/area_data.h \
@ -74,4 +98,27 @@ HEADERS += include/aoclient.h \
include/logger/u_logger.h \ include/logger/u_logger.h \
include/logger/writer_modcall.h \ include/logger/writer_modcall.h \
include/logger/writer_full.h \ include/logger/writer_full.h \
include/music_manager.h include/music_manager.h \
include/packet/packet_factory.h \
include/packet/packet_info.h \
include/packet/packet_generic.h \
include/packet/packet_hi.h \
include/packet/packet_id.h \
include/packet/packet_askchaa.h \
include/packet/packet_casea.h \
include/packet/packet_cc.h \
include/packet/packet_ch.h \
include/packet/packet_ct.h \
include/packet/packet_de.h \
include/packet/packet_ee.h \
include/packet/packet_hp.h \
include/packet/packet_mc.h \
include/packet/packet_ms.h \
include/packet/packet_pe.h \
include/packet/packet_pw.h \
include/packet/packet_rc.h \
include/packet/packet_rd.h \
include/packet/packet_rm.h \
include/packet/packet_rt.h \
include/packet/packet_setcase.h \
include/packet/packet_zz.h

View File

@ -0,0 +1,57 @@
//////////////////////////////////////////////////////////////////////////////////////
// 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/>. //
//////////////////////////////////////////////////////////////////////////////////////
#ifndef AKASHI_UTILS_H
#define AKASHI_UTILS_H
#include <QVariant>
#include <math.h>
class AkashiUtils
{
private:
AkashiUtils(){};
public:
template <typename T>
static inline bool checkArgType(QString arg)
{
QVariant qvar = arg;
if (!qvar.canConvert<T>())
return false;
if (std::is_same<T, int>()) {
bool ok;
qvar.toInt(&ok);
return ok;
}
else if (std::is_same<T, float>()) {
bool ok;
float f = qvar.toFloat(&ok);
return ok && !isnan((float)f);
}
else if (std::is_same<T, double>()) {
bool ok;
double d = qvar.toDouble(&ok);
return ok && !isnan((double)d);
}
return true;
};
};
#endif // AKASHI_UTILS_H

View File

@ -34,6 +34,8 @@ class AreaData;
class DBManager; class DBManager;
class MusicManager; class MusicManager;
class Server; class Server;
class NetworkSocket;
class AOPacket;
/** /**
* @brief Represents a client connected to the server running Attorney Online 2 or one of its derivatives. * @brief Represents a client connected to the server running Attorney Online 2 or one of its derivatives.
@ -318,6 +320,37 @@ class AOClient : public QObject
*/ */
bool m_is_spectator = true; bool m_is_spectator = true;
/**
* @brief The hardware ID of the client.
*
* @details Generated based on the client's own supplied hardware ID.
* The client supplied hardware ID is generally a machine unique ID.
*/
QString m_hwid;
/**
* @brief The network socket used by the client. Can either be a Websocket or TCP Socket.
*/
NetworkSocket *m_socket;
/**
* @brief The IPID of the client.
*
* @details Generated based on the client's IP, but cannot be reversed to identify the client's IP.
*/
QString m_ipid;
/**
* @brief The type of area update, used for area update (ARUP) packets.
*/
enum ARUPType
{
PLAYER_COUNT, //!< The packet contains player count updates.
STATUS, //!< The packet contains area status updates.
CM, //!< The packet contains updates about who's the CM of what area.
LOCKED //!< The packet contains updates about what areas are locked.
};
/** /**
* @brief Checks if the client's ACL role has permission for the given permission. * @brief Checks if the client's ACL role has permission for the given permission.
* *
@ -341,109 +374,6 @@ class AOClient : public QObject
*/ */
void setSpectator(bool f_spectator); void setSpectator(bool f_spectator);
/**
* @brief The spectator character ID
*
* @details You may assume that AO has a sane way to determine if a user is a spectator
* or an actual player. Well, to nobodys surprise, this is not the case, so the character id -1 is used
* to determine if a client has entered spectator or user mode. I am making this a const mostly
* for the case this could change at some point in the future, but don't count on it.
*/
const int SPECTATOR_ID = -1;
public slots:
/**
* @brief Handles an incoming packet, checking for authorisation and minimum argument count.
*
* @param packet The incoming packet.
*/
void handlePacket(AOPacket packet);
/**
* @brief A slot for when the client disconnects from the server.
*/
void clientDisconnected();
/**
* @brief A slot for sending a packet to the client.
*
* @param packet The packet to send.
*/
void sendPacket(AOPacket packet);
/**
* @overload
*/
void sendPacket(QString header, QStringList contents);
/**
* @overload
*/
void sendPacket(QString header);
/**
* @brief A slot for when the client's AFK timer runs out.
*/
void onAfkTimeout();
signals:
/**
* @brief This signal is emitted when the client has completed the participation handshake.
*/
void joined();
private:
/**
* @brief The network socket used by the client. Can either be a Websocket or TCP Socket.
*/
NetworkSocket *m_socket;
/**
* @brief A pointer to the Server, used for updating server variables that depend on the client (e.g. amount of players in an area).
*/
Server *server;
/**
* @brief The type of area update, used for area update (ARUP) packets.
*/
enum ARUPType
{
PLAYER_COUNT, //!< The packet contains player count updates.
STATUS, //!< The packet contains area status updates.
CM, //!< The packet contains updates about who's the CM of what area.
LOCKED //!< The packet contains updates about what areas are locked.
};
/**
* @brief Handles an incoming command, checking for authorisation and minimum argument count.
*
* @param command The incoming command.
* @param argc The amount of arguments the command was called with. Equivalent to `argv.size()`.
* @param argv The arguments the command was called with.
*/
void handleCommand(QString command, int argc, QStringList argv);
/**
* @brief Changes the area the client is in.
*
* @param new_area The ID of the new area.
*/
void changeArea(int new_area);
/**
* @brief Changes the client's character.
*
* @param char_id The character ID of the client's new character.
*/
bool changeCharacter(int char_id);
/**
* @brief Changes the client's in-character position.
*
* @param new_pos The new position of the client.
*/
void changePosition(QString new_pos);
/** /**
* @brief Sends or announces an ARUP update. * @brief Sends or announces an ARUP update.
* *
@ -479,114 +409,12 @@ class AOClient : public QObject
*/ */
void sendServerBroadcast(QString message); void sendServerBroadcast(QString message);
/**
* @name Packet headers
*
* @details These functions implement the AO2-style packet handling.
* As these should generally be the same across server software, I see no reason to document them specifically.
*
* You can check out the AO2 network protocol for explanations.
*
* All packet handling functions share the same parameters:
*
* @param area The area the client is in. Some packets make use of the client's current area.
* @param argc The amount of arguments in the packet, not counting the header. Same as `argv.size()`.
* @param argv The arguments in the packet, once again, not counting the header.
* @param packet The... arguments in the packet. Yes, exactly the same as `argv`, just packed into an AOPacket.
*
* @see https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md for the AO2 network protocol.
*/
///@{
/// A "default" packet handler, to be used for error checking and copying other packet handlers.
void pktDefault(AreaData *area, int argc, QStringList argv, AOPacket packet);
/// Implements [hardware ID](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#hard-drive-id).
void pktHardwareId(AreaData *area, int argc, QStringList argv, AOPacket packet);
/**
* @brief Implements [feature list](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#feature-list) and
* [player count](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#player-count).
*/
void pktSoftwareId(AreaData *area, int argc, QStringList argv, AOPacket packet);
/// Implements [resource counts](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#resource-counts).
void pktBeginLoad(AreaData *area, int argc, QStringList argv, AOPacket packet);
/// Implements [character list](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#character-list).
void pktRequestChars(AreaData *area, int argc, QStringList argv, AOPacket packet);
/// Implements [music list](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#music-list).
void pktRequestMusic(AreaData *area, int argc, QStringList argv, AOPacket packet);
/// Implements [the final loading confirmation](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#final-confirmation).
void pktLoadingDone(AreaData *area, int argc, QStringList argv, AOPacket packet);
/**
* @brief Implements character passwording. This is not on the netcode documentation as of writing.
*
* @todo Link packet details when it gets into the netcode documentation.
*/
void pktCharPassword(AreaData *area, int argc, QStringList argv, AOPacket packet);
/// Implements [character selection](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#choose-character).
void pktSelectChar(AreaData *area, int argc, QStringList argv, AOPacket packet);
/// Implements [the in-character messaging hell](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#in-character-message).
void pktIcChat(AreaData *area, int argc, QStringList argv, AOPacket packet);
/// Implements [out-of-character messages](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#out-of-character-message).
void pktOocChat(AreaData *area, int argc, QStringList argv, AOPacket packet);
/// Implements [the keepalive packet](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#keep-alive).
void pktPing(AreaData *area, int argc, QStringList argv, AOPacket packet);
/**
* @brief Implements [music](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#music) and
* [area changing](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#switch-area).
*/
void pktChangeMusic(AreaData *area, int argc, QStringList argv, AOPacket packet);
/**
* @brief Implements [the witness testimony / cross examination / judge decision popups]
* (https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#witness-testimonycross-examination-wtce).
*/
void pktWtCe(AreaData *area, int argc, QStringList argv, AOPacket packet);
/// Implements [penalty bars](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#penalty-health-bars).
void pktHpBar(AreaData *area, int argc, QStringList argv, AOPacket packet);
/// Implements [moderator calling](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#call-mod).
void pktModCall(AreaData *area, int argc, QStringList argv, AOPacket packet);
/// Implements [adding evidence](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#add).
void pktAddEvidence(AreaData *area, int argc, QStringList argv, AOPacket packet);
/// Implements [removing evidence](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#remove).
void pktRemoveEvidence(AreaData *area, int argc, QStringList argv, AOPacket packet);
/// Implements [editing evidence](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#edit).
void pktEditEvidence(AreaData *area, int argc, QStringList argv, AOPacket packet);
/// Implements [updating casing preferences](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#case-preferences-update).
void pktSetCase(AreaData *area, int argc, QStringList argv, AOPacket packet);
/// Implements [announcing a case](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#case-alert).
void pktAnnounceCase(AreaData *area, int argc, QStringList argv, AOPacket packet);
///@}
/**
* @name Packet helper functions
*/
///@{
/** /**
* @brief Calls AOClient::updateEvidenceList() for every client in the current client's area. * @brief Calls AOClient::updateEvidenceList() for every client in the current client's area.
* *
* @param area The current client's area. * @param area The current client's area.
*/ */
void sendEvidenceList(AreaData *area); void sendEvidenceList(AreaData *area) const;
/** /**
* @brief Updates the evidence list in the area for the client. * @brief Updates the evidence list in the area for the client.
@ -595,15 +423,6 @@ class AOClient : public QObject
*/ */
void updateEvidenceList(AreaData *area); void updateEvidenceList(AreaData *area);
/**
* @brief Attempts to validate that hellish abomination that Attorney Online 2 calls an in-character packet.
*
* @param packet The packet to validate.
*
* @return A validated version of the input packet if it is correct, or an `"INVALID"` packet if it is not.
*/
AOPacket validateIcPacket(AOPacket packet);
/** /**
* @brief Removes excessive combining characters from a text. * @brief Removes excessive combining characters from a text.
* *
@ -624,7 +443,101 @@ class AOClient : public QObject
*/ */
bool checkEvidenceAccess(AreaData *area); bool checkEvidenceAccess(AreaData *area);
///@} /**
* @brief Changes the client's character.
*
* @param char_id The character ID of the client's new character.
*/
bool changeCharacter(int char_id);
/**
* @brief A helper function for logging in a client as moderator.
*
* @param message The OOC message the client has sent.
*/
void loginAttempt(QString message);
/**
* @brief Changes the area the client is in.
*
* @param new_area The ID of the new area.
*/
void changeArea(int new_area);
/**
* @brief Handles an incoming command, checking for authorisation and minimum argument count.
*
* @param command The incoming command.
* @param argc The amount of arguments the command was called with. Equivalent to `argv.size()`.
* @param argv The arguments the command was called with.
*/
void handleCommand(QString command, int argc, QStringList argv);
/**
* @brief A helper function for decoding AO encoding from a QString.
*
* @param incoming_message QString to be decoded.
*/
QString decodeMessage(QString incoming_message);
/**
* @brief Adds the last send IC-Message to QVector of the respective area.
*
* @details This one pulls double duty to both append IC-Messages to the QVector or insert them, depending on the current recorder enum.
*
* @param packet The MS-Packet being recorded with their color changed to green.
*/
void addStatement(QStringList packet);
/**
* @brief Updates the currently displayed IC-Message with the next one send
* @param packet The IC-Message that will overwrite the currently stored one.
* @return Returns the updated IC-Message to be send to the other users. It also changes the color to green.
*/
QStringList updateStatement(QStringList packet);
/**
* @brief Convenience function to generate a random integer number between the given minimum and maximum values.
*
* @param min The minimum possible value for the random integer, inclusive.
* @param max The maximum possible value for the random integer, exclusive.
*
* @warning `max` must be greater than `min`.
*
* @return A randomly generated integer within the bounds given.
*/
int genRand(int min, int max);
/**
* @brief A helper function to add recorded packets to an area's judgelog.
*
* @param area Pointer to the area where the packet was sent.
*
* @param client Pointer to the client that sent the packet.
*
* @param action String containing the info that is being recorded.
*/
void updateJudgeLog(AreaData *area, AOClient *client, QString action);
/**
* @brief Pointer to the servers music manager instance.
*/
MusicManager *m_music_manager;
/**
* @brief The text of the last in-character message that was sent by the client.
*
* @details Used to determine if the incoming message is a duplicate.
*/
QString m_last_message;
/**
* @brief The time in seconds since the client last sent a Witness Testimony / Cross Examination
* popup packet.
*
* @details Used to filter out potential spam.
*/
long m_last_wtce_time;
/** /**
* @name Packet helper global variables * @name Packet helper global variables
@ -641,15 +554,6 @@ class AOClient : public QObject
*/ */
QString m_acl_role_id; QString m_acl_role_id;
/**
* @brief The client's character ID.
*
* @details A character ID is just the character's index in the server's character list.
*
* In general, the client assumes that this is a continuous block starting from 0.
*/
int m_char_id = -1;
/** /**
* @brief The character ID of the other character that the client wants to pair up with. * @brief The character ID of the other character that the client wants to pair up with.
* *
@ -687,13 +591,85 @@ class AOClient : public QObject
///@} ///@}
/// Describes a packet's interpretation details. /**
struct PacketInfo * @brief The client's character ID.
{ *
ACLRole::Permission acl_permission; //!< The permissions necessary for the packet. * @details A character ID is just the character's index in the server's character list.
int minArgs; //!< The minimum arguments needed for the packet to be interpreted correctly / make sense. *
void (AOClient::*action)(AreaData *, int, QStringList, AOPacket); * In general, the client assumes that this is a continuous block starting from 0.
}; */
int m_char_id = -1;
/**
* @brief The spectator character ID
*
* @details You may assume that AO has a sane way to determine if a user is a spectator
* or an actual player. Well, to nobodys surprise, this is not the case, so the character id -1 is used
* to determine if a client has entered spectator or user mode. I am making this a const mostly
* for the case this could change at some point in the future, but don't count on it.
*/
const int SPECTATOR_ID = -1;
public slots:
/**
* @brief Handles an incoming packet, checking for authorisation and minimum argument count.
*
* @param packet The incoming packet.
*/
void handlePacket(AOPacket *packet);
/**
* @brief A slot for when the client disconnects from the server.
*/
void clientDisconnected();
/**
* @brief A slot for sending a packet to the client.
*
* @param packet The packet to send.
*/
void sendPacket(AOPacket *packet);
/**
* @overload
*/
void sendPacket(QString header, QStringList contents);
/**
* @overload
*/
void sendPacket(QString header);
/**
* @brief A slot for when the client's AFK timer runs out.
*/
void onAfkTimeout();
signals:
/**
* @brief This signal is emitted when the client has completed the participation handshake.
*/
void joined();
private:
/**
* @brief A pointer to the Server, used for updating server variables that depend on the client (e.g. amount of players in an area).
*/
Server *server;
/**
* @brief Changes the client's in-character position.
*
* @param new_pos The new position of the client.
*/
void changePosition(QString new_pos);
/**
* @name Packet helper functions
*/
///@{
///@}
/** /**
* @property PacketInfo::action * @property PacketInfo::action
@ -706,38 +682,6 @@ class AOClient : public QObject
* @param AOPacket This is a duplicated version of the QStringList above, containing the same data. * @param AOPacket This is a duplicated version of the QStringList above, containing the same data.
*/ */
/**
* @brief The list of packets that the server can interpret.
*
* @showinitializer
*
* @tparam QString The header of the packet that uniquely identifies it.
* @tparam PacketInfo The details of the packet.
* See @ref PacketInfo "the type's documentation" for more details.
*/
const QMap<QString, PacketInfo> packets{
{"HI", {ACLRole::NONE, 1, &AOClient::pktHardwareId}},
{"ID", {ACLRole::NONE, 2, &AOClient::pktSoftwareId}},
{"askchaa", {ACLRole::NONE, 0, &AOClient::pktBeginLoad}},
{"RC", {ACLRole::NONE, 0, &AOClient::pktRequestChars}},
{"RM", {ACLRole::NONE, 0, &AOClient::pktRequestMusic}},
{"RD", {ACLRole::NONE, 0, &AOClient::pktLoadingDone}},
{"PW", {ACLRole::NONE, 1, &AOClient::pktCharPassword}},
{"CC", {ACLRole::NONE, 3, &AOClient::pktSelectChar}},
{"MS", {ACLRole::NONE, 15, &AOClient::pktIcChat}},
{"CT", {ACLRole::NONE, 2, &AOClient::pktOocChat}},
{"CH", {ACLRole::NONE, 1, &AOClient::pktPing}},
{"MC", {ACLRole::NONE, 2, &AOClient::pktChangeMusic}},
{"RT", {ACLRole::NONE, 1, &AOClient::pktWtCe}},
{"HP", {ACLRole::NONE, 2, &AOClient::pktHpBar}},
{"ZZ", {ACLRole::NONE, 0, &AOClient::pktModCall}},
{"PE", {ACLRole::NONE, 3, &AOClient::pktAddEvidence}},
{"DE", {ACLRole::NONE, 1, &AOClient::pktRemoveEvidence}},
{"EE", {ACLRole::NONE, 4, &AOClient::pktEditEvidence}},
{"SETCASE", {ACLRole::NONE, 7, &AOClient::pktSetCase}},
{"CASEA", {ACLRole::NONE, 6, &AOClient::pktAnnounceCase}},
};
/** /**
* @name Authentication * @name Authentication
*/ */
@ -2018,18 +1962,6 @@ class AOClient : public QObject
*/ */
QStringList buildAreaList(int area_idx); QStringList buildAreaList(int area_idx);
/**
* @brief Convenience function to generate a random integer number between the given minimum and maximum values.
*
* @param min The minimum possible value for the random integer, inclusive.
* @param max The maximum possible value for the random integer, exclusive.
*
* @warning `max` must be greater than `min`.
*
* @return A randomly generated integer within the bounds given.
*/
int genRand(int min, int max);
/** /**
* @brief A convenience function for rolling dice. * @brief A convenience function for rolling dice.
* *
@ -2066,15 +1998,6 @@ class AOClient : public QObject
long long parseTime(QString input); long long parseTime(QString input);
QString getReprimand(bool f_positive = false); QString getReprimand(bool f_positive = false);
/**
* @brief Adds the last send IC-Message to QVector of the respective area.
*
* @details This one pulls double duty to both append IC-Messages to the QVector or insert them, depending on the current recorder enum.
*
* @param packet The MS-Packet being recorded with their color changed to green.
*/
void addStatement(QStringList packet);
/** /**
* @brief Clears QVector of the current area. * @brief Clears QVector of the current area.
* *
@ -2083,13 +2006,6 @@ class AOClient : public QObject
*/ */
void clearTestimony(); void clearTestimony();
/**
* @brief Updates the currently displayed IC-Message with the next one send
* @param packet The IC-Message that will overwrite the currently stored one.
* @return Returns the updated IC-Message to be send to the other users. It also changes the color to green.
*/
QStringList updateStatement(QStringList packet);
/** /**
* @brief Called when area enum is set to PLAYBACK. Sends the IC-Message stored at the current statement. * @brief Called when area enum is set to PLAYBACK. Sends the IC-Message stored at the current statement.
* @return IC-Message stored in the QVector. * @return IC-Message stored in the QVector.
@ -2150,71 +2066,11 @@ class AOClient : public QObject
*/ */
bool is_partial; bool is_partial;
/**
* @brief The hardware ID of the client.
*
* @details Generated based on the client's own supplied hardware ID.
* The client supplied hardware ID is generally a machine unique ID.
*/
QString m_hwid;
/**
* @brief The IPID of the client.
*
* @details Generated based on the client's IP, but cannot be reversed to identify the client's IP.
*/
QString m_ipid;
/**
* @brief The time in seconds since the client last sent a Witness Testimony / Cross Examination
* popup packet.
*
* @details Used to filter out potential spam.
*/
long m_last_wtce_time;
/**
* @brief The text of the last in-character message that was sent by the client.
*
* @details Used to determine if the incoming message is a duplicate.
*/
QString m_last_message;
/**
* @brief Pointer to the servers music manager instance.
*/
MusicManager *m_music_manager;
/**
* @brief A helper function to add recorded packets to an area's judgelog.
*
* @param area Pointer to the area where the packet was sent.
*
* @param client Pointer to the client that sent the packet.
*
* @param action String containing the info that is being recorded.
*/
void updateJudgeLog(AreaData *area, AOClient *client, QString action);
/**
* @brief A helper function for decoding AO encoding from a QString.
*
* @param incoming_message QString to be decoded.
*/
QString decodeMessage(QString incoming_message);
/** /**
* @brief The size, in bytes, of the last data the client sent to the server. * @brief The size, in bytes, of the last data the client sent to the server.
*/ */
int last_read = 0; int last_read = 0;
/**
* @brief A helper function for logging in a client as moderator.
*
* @param message The OOC message the client has sent.
*/
void loginAttempt(QString message);
signals: signals:
/** /**

View File

@ -31,6 +31,7 @@
class ConfigManager; class ConfigManager;
class Logger; class Logger;
class MusicManager; class MusicManager;
class AOPacket;
/** /**
* @brief Represents an area on the server, a distinct "room" for people to chat in. * @brief Represents an area on the server, a distinct "room" for people to chat in.
@ -927,7 +928,7 @@ class AreaData : public QObject
/** /**
* @brief Sends a packet to every client inside the area. * @brief Sends a packet to every client inside the area.
*/ */
void sendAreaPacket(AOPacket f_packet, int f_area_index); void sendAreaPacket(AOPacket *f_packet, int f_area_index);
/** /**
* @brief sendAreaPacketClient Sends a packet to the specified client. * @brief sendAreaPacketClient Sends a packet to the specified client.
@ -936,7 +937,7 @@ class AreaData : public QObject
* *
* @param f_user_id The user ID of the client. * @param f_user_id The user ID of the client.
*/ */
void sendAreaPacketClient(AOPacket f_packet, int f_user_id); void sendAreaPacketClient(AOPacket *f_packet, int f_user_id);
/** /**
* @brief userJoinedArea Signals that a new client has joined an area. * @brief userJoinedArea Signals that a new client has joined an area.

View File

@ -168,7 +168,7 @@ class MusicManager : public QObject
* *
* @param f_user_id temporary userid of the incoming client. * @param f_user_id temporary userid of the incoming client.
*/ */
void sendFMPacket(AOPacket f_packet, int f_user_id); void sendFMPacket(AOPacket *f_packet, int f_user_id);
/** /**
* @brief Sends the FM packet with the musiclist of the area when changes are made. * @brief Sends the FM packet with the musiclist of the area when changes are made.
@ -177,7 +177,7 @@ class MusicManager : public QObject
* *
* @param f_area_index Index of the current area the edit is made in. * @param f_area_index Index of the current area the edit is made in.
*/ */
void sendAreaFMPacket(AOPacket f_packet, int f_area_index); void sendAreaFMPacket(AOPacket *f_packet, int f_area_index);
private: private:
/** /**

View File

@ -23,6 +23,12 @@
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include "include/aoclient.h"
#include "include/area_data.h"
#include "include/packet/packet_info.h"
class AOClient;
/** /**
* @brief An Attorney Online 2 compatible packet. * @brief An Attorney Online 2 compatible packet.
* *
@ -32,20 +38,7 @@
class AOPacket class AOPacket
{ {
public: public:
/** AOPacket(QStringList p_contents);
* @brief Creates an AOPacket with the given header and contents.
*
* @param p_header The header for the packet.
* @param p_contents The contents of the packet.
*/
AOPacket(QString p_header, QStringList p_contents);
/**
* @brief Create an AOPacket from an incoming network message.
*
* @param f_packet An escaped string with header and content.
*/
AOPacket(QString f_packet);
/** /**
* @brief Destructor for the AOPacket * @brief Destructor for the AOPacket
@ -59,13 +52,6 @@ class AOPacket
*/ */
const QStringList getContent(); const QStringList getContent();
/**
* @brief Returns the header of the packet.
*
* @return The packets header.
*/
QString getHeader();
/** /**
* @brief Converts the header and content into a single string. * @brief Converts the header and content into a single string.
* *
@ -125,15 +111,13 @@ class AOPacket
*/ */
bool isPacketEscaped(); bool isPacketEscaped();
private: virtual PacketInfo getPacketInfo() const = 0;
/** virtual void handlePacket(AreaData *area, AOClient &client) const = 0;
* @brief The header of the packet. virtual bool validatePacket() const = 0;
*
* @see https://github.com/AttorneyOnline/docs/blob/master/AO%20Documentation/docs/development/network.md#network-protocol
* for a general explanation on Attorney Online 2's network protocl.
*/
QString m_header;
static void registerPackets();
protected:
/** /**
* @brief The contents of the packet. * @brief The contents of the packet.
*/ */

View File

@ -25,6 +25,8 @@
#include "include/network/aopacket.h" #include "include/network/aopacket.h"
class AOPacket;
class NetworkSocket : public QObject class NetworkSocket : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -73,7 +75,7 @@ class NetworkSocket : public QObject
* *
* @param Packet to be written to the socket. * @param Packet to be written to the socket.
*/ */
void write(AOPacket f_packet); void write(AOPacket *f_packet);
signals: signals:
@ -81,7 +83,7 @@ class NetworkSocket : public QObject
* @brief handlePacket * @brief handlePacket
* @param f_packet * @param f_packet
*/ */
void handlePacket(AOPacket f_packet); void handlePacket(AOPacket *f_packet);
/** /**
* @brief Emitted when the socket has been closed and the client is disconnected. * @brief Emitted when the socket has been closed and the client is disconnected.

View File

@ -0,0 +1,14 @@
#ifndef PACKET_ASKCHAA_H
#define PACKET_ASKCHAA_H
#include "include/network/aopacket.h"
class PacketAskchaa : public AOPacket
{
public:
PacketAskchaa(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
};
#endif

View File

@ -0,0 +1,14 @@
#ifndef PACKET_CASEA_H
#define PACKET_CASEA_H
#include "include/network/aopacket.h"
class PacketCasea : public AOPacket
{
public:
PacketCasea(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
};
#endif

View File

@ -0,0 +1,14 @@
#ifndef PACKET_CC_H
#define PACKET_CC_H
#include "include/network/aopacket.h"
class PacketCC : public AOPacket
{
public:
PacketCC(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
};
#endif

View File

@ -0,0 +1,14 @@
#ifndef PACKET_CH_H
#define PACKET_CH_H
#include "include/network/aopacket.h"
class PacketCH : public AOPacket
{
public:
PacketCH(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
};
#endif

View File

@ -0,0 +1,14 @@
#ifndef PACKET_CT_H
#define PACKET_CT_H
#include "include/network/aopacket.h"
class PacketCT : public AOPacket
{
public:
PacketCT(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
};
#endif

View File

@ -0,0 +1,14 @@
#ifndef PACKET_DE_H
#define PACKET_DE_H
#include "include/network/aopacket.h"
class PacketDE : public AOPacket
{
public:
PacketDE(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
};
#endif

View File

@ -0,0 +1,14 @@
#ifndef PACKET_EE_H
#define PACKET_EE_H
#include "include/network/aopacket.h"
class PacketEE : public AOPacket
{
public:
PacketEE(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
};
#endif

View File

@ -0,0 +1,20 @@
#include "include/network/aopacket.h"
class PacketFactory
{
public:
// thingy here to register/map strings to constructors
static AOPacket *createPacket(QString header, QStringList contents);
static AOPacket *createPacket(QString raw_packet);
template <typename T>
static void registerClass(QString header) { class_map[header] = &createInstance<T>; };
private:
template <typename T>
static AOPacket *createInstance(QStringList contents) { return new T(contents); };
template <typename T>
static AOPacket *createInstance(QString header, QStringList contents) { return new T(header, contents); };
typedef std::map<QString, AOPacket *(*)(QStringList)> type_map;
static inline type_map class_map;
};

View File

@ -0,0 +1,17 @@
#ifndef PACKET_GENERIC_H
#define PACKET_GENERIC_H
#include "include/network/aopacket.h"
class PacketGeneric : public AOPacket
{
public:
PacketGeneric(QString header, QStringList contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
private:
QString header;
};
#endif

View File

@ -0,0 +1,17 @@
#ifndef PACKET_HI_H
#define PACKET_HI_H
#include "include/network/aopacket.h"
class PacketHI : public AOPacket
{
public:
PacketHI(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
private:
QString header;
};
#endif

View File

@ -0,0 +1,14 @@
#ifndef PACKET_HP_H
#define PACKET_HP_H
#include "include/network/aopacket.h"
class PacketHP : public AOPacket
{
public:
PacketHP(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
};
#endif

View File

@ -0,0 +1,14 @@
#ifndef PACKET_ID_H
#define PACKET_ID_H
#include "include/network/aopacket.h"
class PacketID : public AOPacket
{
public:
PacketID(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
};
#endif

View File

@ -0,0 +1,14 @@
#ifndef PACKET_INFO_H
#define PACKET_INFO_H
#include "include/acl_roles_handler.h"
/// Describes a packet's interpretation details.
class PacketInfo
{
public:
ACLRole::Permission acl_permission; //!< The permissions necessary for the packet.
int min_args; //!< The minimum arguments needed for the packet to be interpreted correctly / make sense.
QString header;
};
#endif

View File

@ -0,0 +1,14 @@
#ifndef PACKET_MC_H
#define PACKET_MC_H
#include "include/network/aopacket.h"
class PacketMC : public AOPacket
{
public:
PacketMC(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
};
#endif

View File

@ -0,0 +1,17 @@
#ifndef PACKET_MS_H
#define PACKET_MS_H
#include "include/network/aopacket.h"
class PacketMS : public AOPacket
{
public:
PacketMS(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
private:
AOPacket *validateIcPacket(AOClient &client) const;
};
#endif

View File

@ -0,0 +1,14 @@
#ifndef PACKET_PE_H
#define PACKET_PE_H
#include "include/network/aopacket.h"
class PacketPE : public AOPacket
{
public:
PacketPE(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
};
#endif

View File

@ -0,0 +1,14 @@
#ifndef PACKET_PW_H
#define PACKET_PW_H
#include "include/network/aopacket.h"
class PacketPW : public AOPacket
{
public:
PacketPW(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
};
#endif

View File

@ -0,0 +1,14 @@
#ifndef PACKET_RC_H
#define PACKET_RC_H
#include "include/network/aopacket.h"
class PacketRC : public AOPacket
{
public:
PacketRC(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
};
#endif

View File

@ -0,0 +1,14 @@
#ifndef PACKET_RD_H
#define PACKET_RD_H
#include "include/network/aopacket.h"
class PacketRD : public AOPacket
{
public:
PacketRD(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
};
#endif

View File

@ -0,0 +1,14 @@
#ifndef PACKET_RM_H
#define PACKET_RM_H
#include "include/network/aopacket.h"
class PacketRM : public AOPacket
{
public:
PacketRM(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
};
#endif

View File

@ -0,0 +1,14 @@
#ifndef PACKET_RT_H
#define PACKET_RT_H
#include "include/network/aopacket.h"
class PacketRT : public AOPacket
{
public:
PacketRT(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
};
#endif

View File

@ -0,0 +1,14 @@
#ifndef PACKET_SETCASE_H
#define PACKET_SETCASE_H
#include "include/network/aopacket.h"
class PacketSetcase : public AOPacket
{
public:
PacketSetcase(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
};
#endif

View File

@ -0,0 +1,14 @@
#ifndef PACKET_ZZ_H
#define PACKET_ZZ_H
#include "include/network/aopacket.h"
class PacketZZ : public AOPacket
{
public:
PacketZZ(QStringList &contents);
virtual PacketInfo getPacketInfo() const;
virtual void handlePacket(AreaData *area, AOClient &client) const;
virtual bool validatePacket() const;
};
#endif

View File

@ -182,14 +182,14 @@ class Server : public QObject
* *
* @note Does nothing if an area by the given index does not exist. * @note Does nothing if an area by the given index does not exist.
*/ */
void broadcast(AOPacket packet, int area_index); void broadcast(AOPacket *packet, int area_index);
/** /**
* @brief Sends a packet to all clients in the server. * @brief Sends a packet to all clients in the server.
* *
* @param packet The packet to send to the clients. * @param packet The packet to send to the clients.
*/ */
void broadcast(AOPacket packet); void broadcast(AOPacket *packet);
/** /**
* @brief Sends a packet to a specific usergroup.. * @brief Sends a packet to a specific usergroup..
@ -198,7 +198,7 @@ class Server : public QObject
* *
* @param ENUM to determine the targets of the altered packet. * @param ENUM to determine the targets of the altered packet.
*/ */
void broadcast(AOPacket packet, TARGET_TYPE target); void broadcast(AOPacket *packet, TARGET_TYPE target);
/** /**
* @brief Sends a packet to clients, sends an altered packet to a specific usergroup. * @brief Sends a packet to clients, sends an altered packet to a specific usergroup.
@ -209,7 +209,7 @@ class Server : public QObject
* *
* @param ENUM to determine the targets of the altered packet. * @param ENUM to determine the targets of the altered packet.
*/ */
void broadcast(AOPacket packet, AOPacket other_packet, enum TARGET_TYPE target); void broadcast(AOPacket *packet, AOPacket *other_packet, enum TARGET_TYPE target);
/** /**
* @brief Sends a packet to a single client. * @brief Sends a packet to a single client.
@ -218,7 +218,7 @@ class Server : public QObject
* *
* @param The temporary userID of the client. * @param The temporary userID of the client.
*/ */
void unicast(AOPacket f_packet, int f_client_id); void unicast(AOPacket *f_packet, int f_client_id);
/** /**
* @brief Returns the character's character ID (= their index in the character list). * @brief Returns the character's character ID (= their index in the character list).

View File

@ -21,7 +21,7 @@
#include "include/command_extension.h" #include "include/command_extension.h"
#include "include/config_manager.h" #include "include/config_manager.h"
#include "include/db_manager.h" #include "include/db_manager.h"
#include "include/network/aopacket.h" #include "include/packet/packet_factory.h"
#include "include/server.h" #include "include/server.h"
const QMap<QString, AOClient::CommandInfo> AOClient::COMMANDS{ const QMap<QString, AOClient::CommandInfo> AOClient::COMMANDS{
@ -176,37 +176,36 @@ void AOClient::clientDisconnected()
emit clientSuccessfullyDisconnected(m_id); emit clientSuccessfullyDisconnected(m_id);
} }
void AOClient::handlePacket(AOPacket packet) void AOClient::handlePacket(AOPacket *packet)
{ {
#ifdef NET_DEBUG #ifdef NET_DEBUG
qDebug() << "Received packet:" << packet.getHeader() << ":" << packet.getContent() << "args length:" << packet.getContent().length(); qDebug() << "Received packet:" << packet->getPacketInfo().header << ":" << packet->getContent() << "args length:" << packet->getContent().length();
#endif #endif
AreaData *l_area = server->getAreaById(m_current_area); AreaData *l_area = server->getAreaById(m_current_area);
PacketInfo l_info = packets.value(packet.getHeader(), {ACLRole::NONE, 0, &AOClient::pktDefault});
if (packet.getContent().join("").size() > 16384) { if (packet->getContent().join("").size() > 16384) {
return; return;
} }
if (!checkPermission(l_info.acl_permission)) { if (!checkPermission(packet->getPacketInfo().acl_permission)) {
return; return;
} }
if (packet.getHeader() != "CH" && m_joined) { if (packet->getPacketInfo().header != "CH" && m_joined) {
if (m_is_afk) if (m_is_afk)
sendServerMessage("You are no longer AFK."); sendServerMessage("You are no longer AFK.");
m_is_afk = false; m_is_afk = false;
m_afk_timer->start(ConfigManager::afkTimeout() * 1000); m_afk_timer->start(ConfigManager::afkTimeout() * 1000);
} }
if (packet.getContent().length() < l_info.minArgs) { if (packet->getContent().length() < packet->getPacketInfo().min_args) {
#ifdef NET_DEBUG #ifdef NET_DEBUG
qDebug() << "Invalid packet args length. Minimum is" << l_info.minArgs << "but only" << packet.getContent().length() << "were given."; qDebug() << "Invalid packet args length. Minimum is" << packet->getPacketInfo().min_args << "but only" << packet->getContent().length() << "were given.";
#endif #endif
return; return;
} }
(this->*(l_info.action))(l_area, packet.getContent().length(), packet.getContent(), packet); packet->handlePacket(l_area, *this);
} }
void AOClient::changeArea(int new_area) void AOClient::changeArea(int new_area)
@ -385,7 +384,7 @@ void AOClient::arup(ARUPType type, bool broadcast)
} }
} }
if (broadcast) if (broadcast)
server->broadcast(AOPacket("ARUP", l_arup_data)); server->broadcast(PacketFactory::createPacket("ARUP", l_arup_data));
else else
sendPacket("ARUP", l_arup_data); sendPacket("ARUP", l_arup_data);
} }
@ -398,22 +397,22 @@ void AOClient::fullArup()
arup(ARUPType::LOCKED, false); arup(ARUPType::LOCKED, false);
} }
void AOClient::sendPacket(AOPacket packet) void AOClient::sendPacket(AOPacket *packet)
{ {
#ifdef NET_DEBUG #ifdef NET_DEBUG
qDebug() << "Sent packet:" << packet.getHeader() << ":" << packet.getContent(); qDebug() << "Sent packet:" << packet->getPacketInfo().header << ":" << packet->getContent();
#endif #endif
m_socket->write(packet); m_socket->write(packet);
} }
void AOClient::sendPacket(QString header, QStringList contents) void AOClient::sendPacket(QString header, QStringList contents)
{ {
sendPacket(AOPacket(header, contents)); sendPacket(PacketFactory::createPacket(header, contents));
} }
void AOClient::sendPacket(QString header) void AOClient::sendPacket(QString header)
{ {
sendPacket(AOPacket(header, {})); sendPacket(PacketFactory::createPacket(header, {}));
} }
void AOClient::calculateIpid() void AOClient::calculateIpid()
@ -438,12 +437,12 @@ void AOClient::sendServerMessage(QString message)
void AOClient::sendServerMessageArea(QString message) void AOClient::sendServerMessageArea(QString message)
{ {
server->broadcast(AOPacket("CT", {ConfigManager::serverName(), message, "1"}), m_current_area); server->broadcast(PacketFactory::createPacket("CT", {ConfigManager::serverName(), message, "1"}), m_current_area);
} }
void AOClient::sendServerBroadcast(QString message) void AOClient::sendServerBroadcast(QString message)
{ {
server->broadcast(AOPacket("CT", {ConfigManager::serverName(), message, "1"})); server->broadcast(PacketFactory::createPacket("CT", {ConfigManager::serverName(), message, "1"}));
} }
bool AOClient::checkPermission(ACLRole::Permission f_permission) const bool AOClient::checkPermission(ACLRole::Permission f_permission) const
@ -516,10 +515,10 @@ AOClient::AOClient(Server *p_server, NetworkSocket *socket, QObject *parent, int
m_current_area(0), m_current_area(0),
m_current_char(""), m_current_char(""),
m_socket(socket), m_socket(socket),
server(p_server), m_music_manager(p_manager),
is_partial(false),
m_last_wtce_time(0), m_last_wtce_time(0),
m_music_manager(p_manager) server(p_server),
is_partial(false)
{ {
m_afk_timer = new QTimer; m_afk_timer = new QTimer;
m_afk_timer->setSingleShot(true); m_afk_timer->setSingleShot(true);

View File

@ -21,7 +21,7 @@
#include "include/area_data.h" #include "include/area_data.h"
#include "include/config_manager.h" #include "include/config_manager.h"
#include "include/music_manager.h" #include "include/music_manager.h"
#include "include/network/aopacket.h" #include "include/packet/packet_factory.h"
AreaData::AreaData(QString p_name, int p_index, MusicManager *p_music_manager = nullptr) : AreaData::AreaData(QString p_name, int p_index, MusicManager *p_music_manager = nullptr) :
m_index(p_index), m_index(p_index),
@ -108,7 +108,7 @@ void AreaData::clientJoinedArea(int f_charId, int f_userId)
emit userJoinedArea(m_index, f_userId); emit userJoinedArea(m_index, f_userId);
// The name will never be shown as we are using a spectator ID. Still nice for people who network sniff. // The name will never be shown as we are using a spectator ID. Still nice for people who network sniff.
// We auto-loop this so you'll never sit in silence unless wanted. // We auto-loop this so you'll never sit in silence unless wanted.
emit sendAreaPacketClient(AOPacket("MC", {m_currentMusic, QString::number(-1), ConfigManager::serverName(), QString::number(1)}), f_userId); emit sendAreaPacketClient(PacketFactory::createPacket("MC", {m_currentMusic, QString::number(-1), ConfigManager::serverName(), QString::number(1)}), f_userId);
} }
QList<int> AreaData::owners() const QList<int> AreaData::owners() const
@ -624,7 +624,7 @@ QString AreaData::addJukeboxSong(QString f_song)
if (l_song.second > 0) { if (l_song.second > 0) {
if (m_jukebox_queue.size() == 0) { if (m_jukebox_queue.size() == 0) {
emit sendAreaPacket(AOPacket("MC", {l_song.first, QString::number(-1)}), index()); emit sendAreaPacket(PacketFactory::createPacket("MC", {l_song.first, QString::number(-1)}), index());
m_jukebox_timer->start(l_song.second * 1000); m_jukebox_timer->start(l_song.second * 1000);
setCurrentMusic(f_song); setCurrentMusic(f_song);
setMusicPlayedBy("Jukebox"); setMusicPlayedBy("Jukebox");
@ -650,7 +650,7 @@ void AreaData::switchJukeboxSong()
if (m_jukebox_queue.size() == 1) { if (m_jukebox_queue.size() == 1) {
l_song_name = m_jukebox_queue[0]; l_song_name = m_jukebox_queue[0];
QPair<QString, float> l_song = m_music_manager->songInformation(l_song_name, index()); QPair<QString, float> l_song = m_music_manager->songInformation(l_song_name, index());
emit sendAreaPacket(AOPacket("MC", {l_song.first, "-1"}), m_index); emit sendAreaPacket(PacketFactory::createPacket("MC", {l_song.first, "-1"}), m_index);
m_jukebox_timer->start(l_song.second * 1000); m_jukebox_timer->start(l_song.second * 1000);
} }
else { else {
@ -658,7 +658,7 @@ void AreaData::switchJukeboxSong()
l_song_name = m_jukebox_queue[l_random_index]; l_song_name = m_jukebox_queue[l_random_index];
QPair<QString, float> l_song = m_music_manager->songInformation(l_song_name, index()); QPair<QString, float> l_song = m_music_manager->songInformation(l_song_name, index());
emit sendAreaPacket(AOPacket("MC", {l_song.first, "-1"}), m_index); emit sendAreaPacket(PacketFactory::createPacket("MC", {l_song.first, "-1"}), m_index);
m_jukebox_timer->start(l_song.second * 1000); m_jukebox_timer->start(l_song.second * 1000);
m_jukebox_queue.remove(l_random_index); m_jukebox_queue.remove(l_random_index);

View File

@ -19,7 +19,7 @@
#include "include/area_data.h" #include "include/area_data.h"
#include "include/config_manager.h" #include "include/config_manager.h"
#include "include/network/aopacket.h" #include "include/packet/packet_factory.h"
#include "include/server.h" #include "include/server.h"
// This file is for commands under the area category in aoclient.h // This file is for commands under the area category in aoclient.h
@ -298,7 +298,7 @@ void AOClient::cmdSetBackground(int argc, QStringList argv)
if (m_authenticated || !area->bgLocked()) { if (m_authenticated || !area->bgLocked()) {
if (server->getBackgrounds().contains(f_background, Qt::CaseInsensitive) || area->ignoreBgList() == true) { if (server->getBackgrounds().contains(f_background, Qt::CaseInsensitive) || area->ignoreBgList() == true) {
area->setBackground(f_background); area->setBackground(f_background);
server->broadcast(AOPacket("BN", {f_background}), m_current_area); server->broadcast(PacketFactory::createPacket("BN", {f_background}), m_current_area);
sendServerMessageArea(m_current_char + " changed the background to " + f_background); sendServerMessageArea(m_current_char + " changed the background to " + f_background);
} }
else { else {
@ -321,7 +321,7 @@ void AOClient::cmdBgLock(int argc, QStringList argv)
l_area->toggleBgLock(); l_area->toggleBgLock();
}; };
server->broadcast(AOPacket("CT", {ConfigManager::serverName(), m_current_char + " locked the background.", "1"}), m_current_area); server->broadcast(PacketFactory::createPacket("CT", {ConfigManager::serverName(), m_current_char + " locked the background.", "1"}), m_current_area);
} }
void AOClient::cmdBgUnlock(int argc, QStringList argv) void AOClient::cmdBgUnlock(int argc, QStringList argv)
@ -335,7 +335,7 @@ void AOClient::cmdBgUnlock(int argc, QStringList argv)
l_area->toggleBgLock(); l_area->toggleBgLock();
}; };
server->broadcast(AOPacket("CT", {ConfigManager::serverName(), m_current_char + " unlocked the background.", "1"}), m_current_area); server->broadcast(PacketFactory::createPacket("CT", {ConfigManager::serverName(), m_current_char + " unlocked the background.", "1"}), m_current_area);
} }
void AOClient::cmdStatus(int argc, QStringList argv) void AOClient::cmdStatus(int argc, QStringList argv)
@ -347,7 +347,7 @@ void AOClient::cmdStatus(int argc, QStringList argv)
if (l_area->changeStatus(l_arg)) { if (l_area->changeStatus(l_arg)) {
arup(ARUPType::STATUS, true); arup(ARUPType::STATUS, true);
server->broadcast(AOPacket("CT", {ConfigManager::serverName(), m_current_char + " changed status to " + l_arg.toUpper(), "1"}), m_current_area); server->broadcast(PacketFactory::createPacket("CT", {ConfigManager::serverName(), m_current_char + " changed status to " + l_arg.toUpper(), "1"}), m_current_area);
} }
else { else {
const QStringList keys = AreaData::map_statuses.keys(); const QStringList keys = AreaData::map_statuses.keys();

View File

@ -19,7 +19,7 @@
#include "include/area_data.h" #include "include/area_data.h"
#include "include/config_manager.h" #include "include/config_manager.h"
#include "include/network/aopacket.h" #include "include/packet/packet_factory.h"
#include "include/server.h" #include "include/server.h"
// This file is for commands under the casing category in aoclient.h // This file is for commands under the casing category in aoclient.h
@ -130,8 +130,8 @@ void AOClient::cmdExamine(int argc, QStringList argv)
AreaData *l_area = server->getAreaById(m_current_area); AreaData *l_area = server->getAreaById(m_current_area);
if (l_area->testimony().size() - 1 > 0) { if (l_area->testimony().size() - 1 > 0) {
l_area->restartTestimony(); l_area->restartTestimony();
server->broadcast(AOPacket("RT", {"testimony2"}), m_current_area); server->broadcast(PacketFactory::createPacket("RT", {"testimony2"}), m_current_area);
server->broadcast(AOPacket("MS", {l_area->testimony()[0]}), m_current_area); server->broadcast(PacketFactory::createPacket("MS", {l_area->testimony()[0]}), m_current_area);
return; return;
} }
if (l_area->testimonyRecording() == AreaData::TestimonyRecording::PLAYBACK) if (l_area->testimonyRecording() == AreaData::TestimonyRecording::PLAYBACK)
@ -192,7 +192,7 @@ void AOClient::cmdPauseTestimony(int argc, QStringList argv)
AreaData *l_area = server->getAreaById(m_current_area); AreaData *l_area = server->getAreaById(m_current_area);
l_area->setTestimonyRecording(AreaData::TestimonyRecording::STOPPED); l_area->setTestimonyRecording(AreaData::TestimonyRecording::STOPPED);
server->broadcast(AOPacket("RT", {"testimony1", "1"}), m_current_area); server->broadcast(PacketFactory::createPacket("RT", {"testimony1", "1"}), m_current_area);
sendServerMessage("Testimony has been stopped."); sendServerMessage("Testimony has been stopped.");
} }

View File

@ -19,7 +19,7 @@
#include "include/area_data.h" #include "include/area_data.h"
#include "include/config_manager.h" #include "include/config_manager.h"
#include "include/network/aopacket.h" #include "include/packet/packet_factory.h"
#include "include/server.h" #include "include/server.h"
// This file is for functions used by various commands, defined in the command helper function category in aoclient.h // This file is for functions used by various commands, defined in the command helper function category in aoclient.h
@ -228,7 +228,7 @@ void AOClient::sendNotice(QString f_notice, bool f_global)
l_message += "server-wide "; l_message += "server-wide ";
l_message += "notice:\n\n" + f_notice; l_message += "notice:\n\n" + f_notice;
sendServerMessageArea(l_message); sendServerMessageArea(l_message);
AOPacket l_packet("BB", {l_message}); AOPacket *l_packet = PacketFactory::createPacket("BB", {l_message});
if (f_global) if (f_global)
server->broadcast(l_packet); server->broadcast(l_packet);
else else

View File

@ -18,7 +18,7 @@
#include "include/aoclient.h" #include "include/aoclient.h"
#include "include/area_data.h" #include "include/area_data.h"
#include "include/network/aopacket.h" #include "include/packet/packet_factory.h"
#include "include/server.h" #include "include/server.h"
// This file is for commands under the messaging category in aoclient.h // This file is for commands under the messaging category in aoclient.h
@ -77,8 +77,8 @@ void AOClient::cmdG(int argc, QStringList argv)
QString l_sender_area = server->getAreaName(m_current_area); QString l_sender_area = server->getAreaName(m_current_area);
QString l_sender_message = argv.join(" "); QString l_sender_message = argv.join(" ");
// Better readability thanks to AwesomeAim. // Better readability thanks to AwesomeAim.
AOPacket l_mod_packet = AOPacket("CT", {"[G][" + m_ipid + "][" + l_sender_area + "]" + l_sender_name, l_sender_message}); AOPacket *l_mod_packet = PacketFactory::createPacket("CT", {"[G][" + m_ipid + "][" + l_sender_area + "]" + l_sender_name, l_sender_message});
AOPacket l_user_packet = AOPacket("CT", {"[G][" + l_sender_area + "]" + l_sender_name, l_sender_message}); AOPacket *l_user_packet = PacketFactory::createPacket("CT", {"[G][" + l_sender_area + "]" + l_sender_name, l_sender_message});
server->broadcast(l_user_packet, l_mod_packet, Server::TARGET_TYPE::AUTHENTICATED); server->broadcast(l_user_packet, l_mod_packet, Server::TARGET_TYPE::AUTHENTICATED);
return; return;
} }
@ -89,7 +89,7 @@ void AOClient::cmdNeed(int argc, QStringList argv)
QString l_sender_area = server->getAreaName(m_current_area); QString l_sender_area = server->getAreaName(m_current_area);
QString l_sender_message = argv.join(" "); QString l_sender_message = argv.join(" ");
server->broadcast(AOPacket("CT", {"=== Advert ===\n[" + l_sender_area + "] needs " + l_sender_message + "."}), Server::TARGET_TYPE::ADVERT); server->broadcast(PacketFactory::createPacket("CT", {"=== Advert ===\n[" + l_sender_area + "] needs " + l_sender_message + "."}), Server::TARGET_TYPE::ADVERT);
} }
void AOClient::cmdSwitch(int argc, QStringList argv) void AOClient::cmdSwitch(int argc, QStringList argv)
@ -175,7 +175,7 @@ void AOClient::cmdM(int argc, QStringList argv)
QString l_sender_name = m_ooc_name; QString l_sender_name = m_ooc_name;
QString l_sender_message = argv.join(" "); QString l_sender_message = argv.join(" ");
server->broadcast(AOPacket("CT", {"[M]" + l_sender_name, l_sender_message}), Server::TARGET_TYPE::MODCHAT); server->broadcast(PacketFactory::createPacket("CT", {"[M]" + l_sender_name, l_sender_message}), Server::TARGET_TYPE::MODCHAT);
} }
void AOClient::cmdGM(int argc, QStringList argv) void AOClient::cmdGM(int argc, QStringList argv)
@ -185,7 +185,7 @@ void AOClient::cmdGM(int argc, QStringList argv)
QString l_sender_name = m_ooc_name; QString l_sender_name = m_ooc_name;
QString l_sender_area = server->getAreaName(m_current_area); QString l_sender_area = server->getAreaName(m_current_area);
QString l_sender_message = argv.join(" "); QString l_sender_message = argv.join(" ");
server->broadcast(AOPacket("CT", {"[G][" + l_sender_area + "]" + "[" + l_sender_name + "][M]", l_sender_message}), Server::TARGET_TYPE::MODCHAT); server->broadcast(PacketFactory::createPacket("CT", {"[G][" + l_sender_area + "]" + "[" + l_sender_name + "][M]", l_sender_message}), Server::TARGET_TYPE::MODCHAT);
} }
void AOClient::cmdLM(int argc, QStringList argv) void AOClient::cmdLM(int argc, QStringList argv)
@ -194,7 +194,7 @@ void AOClient::cmdLM(int argc, QStringList argv)
QString l_sender_name = m_ooc_name; QString l_sender_name = m_ooc_name;
QString l_sender_message = argv.join(" "); QString l_sender_message = argv.join(" ");
server->broadcast(AOPacket("CT", {"[" + l_sender_name + "][M]", l_sender_message}), m_current_area); server->broadcast(PacketFactory::createPacket("CT", {"[" + l_sender_name + "][M]", l_sender_message}), m_current_area);
} }
void AOClient::cmdGimp(int argc, QStringList argv) void AOClient::cmdGimp(int argc, QStringList argv)
@ -524,7 +524,7 @@ void AOClient::cmdA(int argc, QStringList argv)
argv.removeAt(0); argv.removeAt(0);
QString l_sender_name = m_ooc_name; QString l_sender_name = m_ooc_name;
QString l_ooc_message = argv.join(" "); QString l_ooc_message = argv.join(" ");
server->broadcast(AOPacket("CT", {"[CM]" + l_sender_name, l_ooc_message}), l_area_id); server->broadcast(PacketFactory::createPacket("CT", {"[CM]" + l_sender_name, l_ooc_message}), l_area_id);
} }
void AOClient::cmdS(int argc, QStringList argv) void AOClient::cmdS(int argc, QStringList argv)
@ -537,7 +537,7 @@ void AOClient::cmdS(int argc, QStringList argv)
for (int i = 0; i <= l_all_areas; i++) { for (int i = 0; i <= l_all_areas; i++) {
if (server->getAreaById(i)->owners().contains(m_id)) if (server->getAreaById(i)->owners().contains(m_id))
server->broadcast(AOPacket("CT", {"[CM]" + l_sender_name, l_ooc_message}), i); server->broadcast(PacketFactory::createPacket("CT", {"[CM]" + l_sender_name, l_ooc_message}), i);
} }
} }

View File

@ -19,7 +19,7 @@
#include "include/area_data.h" #include "include/area_data.h"
#include "include/music_manager.h" #include "include/music_manager.h"
#include "include/network/aopacket.h" #include "include/packet/packet_factory.h"
#include "include/server.h" #include "include/server.h"
// This file is for commands under the music category in aoclient.h // This file is for commands under the music category in aoclient.h
@ -41,7 +41,7 @@ void AOClient::cmdPlay(int argc, QStringList argv)
else { else {
l_area->changeMusic(m_showname, l_song); l_area->changeMusic(m_showname, l_song);
} }
AOPacket music_change("MC", {l_song, QString::number(server->getCharID(m_current_char)), m_showname, "1", "0"}); AOPacket *music_change = PacketFactory::createPacket("MC", {l_song, QString::number(server->getCharID(m_current_char)), m_showname, "1", "0"});
server->broadcast(music_change, m_current_area); server->broadcast(music_change, m_current_area);
} }

View File

@ -19,7 +19,7 @@
#include "include/area_data.h" #include "include/area_data.h"
#include "include/config_manager.h" #include "include/config_manager.h"
#include "include/network/aopacket.h" #include "include/packet/packet_factory.h"
#include "include/server.h" #include "include/server.h"
// This file is for commands under the roleplay category in aoclient.h // This file is for commands under the roleplay category in aoclient.h
@ -195,8 +195,8 @@ void AOClient::cmdTimer(int argc, QStringList argv)
else else
l_requested_timer = l_area->timers().at(l_timer_id - 1); l_requested_timer = l_area->timers().at(l_timer_id - 1);
AOPacket l_show_timer("TI", {QString::number(l_timer_id), "2"}); AOPacket *l_show_timer = PacketFactory::createPacket("TI", {QString::number(l_timer_id), "2"});
AOPacket l_hide_timer("TI", {QString::number(l_timer_id), "3"}); AOPacket *l_hide_timer = PacketFactory::createPacket("TI", {QString::number(l_timer_id), "3"});
bool l_is_global = l_timer_id == 0; bool l_is_global = l_timer_id == 0;
// Set the timer's time remaining if the second // Set the timer's time remaining if the second
@ -206,7 +206,7 @@ void AOClient::cmdTimer(int argc, QStringList argv)
l_requested_timer->setInterval(QTime(0, 0).msecsTo(l_requested_time)); l_requested_timer->setInterval(QTime(0, 0).msecsTo(l_requested_time));
l_requested_timer->start(); l_requested_timer->start();
sendServerMessage("Set timer " + QString::number(l_timer_id) + " to " + argv[1] + "."); sendServerMessage("Set timer " + QString::number(l_timer_id) + " to " + argv[1] + ".");
AOPacket l_update_timer("TI", {QString::number(l_timer_id), "0", QString::number(QTime(0, 0).msecsTo(l_requested_time))}); AOPacket *l_update_timer = PacketFactory::createPacket("TI", {QString::number(l_timer_id), "0", QString::number(QTime(0, 0).msecsTo(l_requested_time))});
l_is_global ? server->broadcast(l_show_timer) : server->broadcast(l_show_timer, m_current_area); // Show the timer l_is_global ? server->broadcast(l_show_timer) : server->broadcast(l_show_timer, m_current_area); // Show the timer
l_is_global ? server->broadcast(l_update_timer) : server->broadcast(l_update_timer, m_current_area); l_is_global ? server->broadcast(l_update_timer) : server->broadcast(l_update_timer, m_current_area);
return; return;
@ -216,7 +216,7 @@ void AOClient::cmdTimer(int argc, QStringList argv)
if (argv[1] == "start") { if (argv[1] == "start") {
l_requested_timer->start(); l_requested_timer->start();
sendServerMessage("Started timer " + QString::number(l_timer_id) + "."); sendServerMessage("Started timer " + QString::number(l_timer_id) + ".");
AOPacket l_update_timer("TI", {QString::number(l_timer_id), "0", QString::number(QTime(0, 0).msecsTo(QTime(0, 0).addMSecs(l_requested_timer->remainingTime())))}); AOPacket *l_update_timer = PacketFactory::createPacket("TI", {QString::number(l_timer_id), "0", QString::number(QTime(0, 0).msecsTo(QTime(0, 0).addMSecs(l_requested_timer->remainingTime())))});
l_is_global ? server->broadcast(l_show_timer) : server->broadcast(l_show_timer, m_current_area); l_is_global ? server->broadcast(l_show_timer) : server->broadcast(l_show_timer, m_current_area);
l_is_global ? server->broadcast(l_update_timer) : server->broadcast(l_update_timer, m_current_area); l_is_global ? server->broadcast(l_update_timer) : server->broadcast(l_update_timer, m_current_area);
} }
@ -224,7 +224,7 @@ void AOClient::cmdTimer(int argc, QStringList argv)
l_requested_timer->setInterval(l_requested_timer->remainingTime()); l_requested_timer->setInterval(l_requested_timer->remainingTime());
l_requested_timer->stop(); l_requested_timer->stop();
sendServerMessage("Stopped timer " + QString::number(l_timer_id) + "."); sendServerMessage("Stopped timer " + QString::number(l_timer_id) + ".");
AOPacket l_update_timer("TI", {QString::number(l_timer_id), "1", QString::number(QTime(0, 0).msecsTo(QTime(0, 0).addMSecs(l_requested_timer->interval())))}); AOPacket *l_update_timer = PacketFactory::createPacket("TI", {QString::number(l_timer_id), "1", QString::number(QTime(0, 0).msecsTo(QTime(0, 0).addMSecs(l_requested_timer->interval())))});
l_is_global ? server->broadcast(l_update_timer) : server->broadcast(l_update_timer, m_current_area); l_is_global ? server->broadcast(l_update_timer) : server->broadcast(l_update_timer, m_current_area);
} }
else if (argv[1] == "hide" || argv[1] == "unset") { else if (argv[1] == "hide" || argv[1] == "unset") {

View File

@ -1,7 +1,7 @@
#include "include/music_manager.h" #include "include/music_manager.h"
#include "include/config_manager.h" #include "include/config_manager.h"
#include "include/network/aopacket.h" #include "include/packet/packet_factory.h"
MusicManager::MusicManager(QStringList f_root_ordered, QStringList f_cdns, QMap<QString, QPair<QString, int>> f_root_list, QObject *parent) : MusicManager::MusicManager(QStringList f_root_ordered, QStringList f_cdns, QMap<QString, QPair<QString, int>> f_root_list, QObject *parent) :
QObject(parent), QObject(parent),
@ -120,7 +120,7 @@ bool MusicManager::addCustomSong(QString f_song_name, QString f_real_name, int f
l_custom_list.insert(l_song_name, {l_real_name, f_duration}); l_custom_list.insert(l_song_name, {l_real_name, f_duration});
m_custom_lists->insert(f_area_id, l_custom_list); m_custom_lists->insert(f_area_id, l_custom_list);
m_customs_ordered.insert(f_area_id, (QStringList{m_customs_ordered.value(f_area_id)} << l_song_name)); m_customs_ordered.insert(f_area_id, (QStringList{m_customs_ordered.value(f_area_id)} << l_song_name));
emit sendAreaFMPacket(AOPacket("FM", musiclist(f_area_id)), f_area_id); emit sendAreaFMPacket(PacketFactory::createPacket("FM", musiclist(f_area_id)), f_area_id);
return true; return true;
} }
@ -148,7 +148,7 @@ bool MusicManager::addCustomCategory(QString f_category_name, int f_area_id)
l_custom_list.insert(l_category_name, {l_category_name, 0}); l_custom_list.insert(l_category_name, {l_category_name, 0});
m_custom_lists->insert(f_area_id, l_custom_list); m_custom_lists->insert(f_area_id, l_custom_list);
m_customs_ordered.insert(f_area_id, (QStringList{m_customs_ordered.value(f_area_id)} << l_category_name)); m_customs_ordered.insert(f_area_id, (QStringList{m_customs_ordered.value(f_area_id)} << l_category_name));
emit sendAreaFMPacket(AOPacket("FM", musiclist(f_area_id)), f_area_id); emit sendAreaFMPacket(PacketFactory::createPacket("FM", musiclist(f_area_id)), f_area_id);
return true; return true;
} }
@ -165,7 +165,7 @@ bool MusicManager::removeCategorySong(QString f_songcategory_name, int f_area_id
l_customs_ordered.removeAll(f_songcategory_name); l_customs_ordered.removeAll(f_songcategory_name);
m_customs_ordered.insert(f_area_id, l_customs_ordered); m_customs_ordered.insert(f_area_id, l_customs_ordered);
emit sendAreaFMPacket(AOPacket("FM", musiclist(f_area_id)), f_area_id); emit sendAreaFMPacket(PacketFactory::createPacket("FM", musiclist(f_area_id)), f_area_id);
return true; return true;
} // Fallthrough } // Fallthrough
} }
@ -178,7 +178,7 @@ bool MusicManager::toggleRootEnabled(int f_area_id)
if (m_global_enabled.value(f_area_id)) { if (m_global_enabled.value(f_area_id)) {
sanitiseCustomList(f_area_id); sanitiseCustomList(f_area_id);
} }
emit sendAreaFMPacket(AOPacket("FM", musiclist(f_area_id)), f_area_id); emit sendAreaFMPacket(PacketFactory::createPacket("FM", musiclist(f_area_id)), f_area_id);
return m_global_enabled.value(f_area_id); return m_global_enabled.value(f_area_id);
} }
@ -234,5 +234,5 @@ void MusicManager::reloadRequest()
void MusicManager::userJoinedArea(int f_area_index, int f_user_id) void MusicManager::userJoinedArea(int f_area_index, int f_user_id)
{ {
emit sendFMPacket(AOPacket("FM", musiclist(f_area_index)), f_user_id); emit sendFMPacket(PacketFactory::createPacket("FM", musiclist(f_area_index)), f_user_id);
} }

View File

@ -17,50 +17,42 @@
////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////
#include "include/network/aopacket.h" #include "include/network/aopacket.h"
AOPacket::AOPacket(QString p_header, QStringList p_contents) : #include "include/packet/packet_askchaa.h"
m_header(p_header), #include "include/packet/packet_casea.h"
#include "include/packet/packet_cc.h"
#include "include/packet/packet_ch.h"
#include "include/packet/packet_ct.h"
#include "include/packet/packet_de.h"
#include "include/packet/packet_ee.h"
#include "include/packet/packet_factory.h"
#include "include/packet/packet_hi.h"
#include "include/packet/packet_hp.h"
#include "include/packet/packet_id.h"
#include "include/packet/packet_mc.h"
#include "include/packet/packet_ms.h"
#include "include/packet/packet_pe.h"
#include "include/packet/packet_pw.h"
#include "include/packet/packet_rc.h"
#include "include/packet/packet_rd.h"
#include "include/packet/packet_rm.h"
#include "include/packet/packet_rt.h"
#include "include/packet/packet_setcase.h"
#include "include/packet/packet_zz.h"
AOPacket::AOPacket(QStringList p_contents) :
m_content(p_contents), m_content(p_contents),
m_escaped(false) m_escaped(false)
{ {
} }
AOPacket::AOPacket(QString f_packet)
{
QString l_packet = f_packet;
if (l_packet.isEmpty() || l_packet.at(0) == '#' || l_packet.contains("%")) {
#if NET_DEBUG
qDebug() << "Invalid or fantacrypt packet received.";
#endif
m_header = "Unknown";
m_content = QStringList{"Unknown"};
return;
}
QStringList l_split_packet = l_packet.split("#");
m_header = l_split_packet.value(0);
// Remove header and trailing packetFinished
l_split_packet.removeFirst();
l_split_packet.removeLast();
m_content = l_split_packet;
// All incoming data has to be escaped after being split.
this->unescapeContent();
}
const QStringList AOPacket::getContent() const QStringList AOPacket::getContent()
{ {
return m_content; return m_content;
} }
QString AOPacket::getHeader()
{
return m_header;
}
QString AOPacket::toString() QString AOPacket::toString()
{ {
if (!isPacketEscaped() && !(m_header == "LE")) { if (!isPacketEscaped() && !(getPacketInfo().header == "LE")) {
// We will never send unescaped data to a client, unless its evidence. // We will never send unescaped data to a client, unless its evidence.
this->escapeContent(); this->escapeContent();
} }
@ -68,7 +60,7 @@ QString AOPacket::toString()
// Of course AO has SOME expection to the rule. // Of course AO has SOME expection to the rule.
this->escapeEvidence(); this->escapeEvidence();
} }
return QString("%1#%2#%3").arg(m_header, m_content.join("#"), packetFinished); return QString("%1#%2#%3").arg(getPacketInfo().header, m_content.join("#"), packetFinished);
} }
QByteArray AOPacket::toUtf8() QByteArray AOPacket::toUtf8()
@ -117,3 +109,27 @@ bool AOPacket::isPacketEscaped()
{ {
return m_escaped; return m_escaped;
} }
void AOPacket::registerPackets()
{
PacketFactory::registerClass<PacketAskchaa>("askchaa");
PacketFactory::registerClass<PacketCasea>("CASEA");
PacketFactory::registerClass<PacketCC>("CC");
PacketFactory::registerClass<PacketCH>("CH");
PacketFactory::registerClass<PacketCT>("CT");
PacketFactory::registerClass<PacketDE>("DE");
PacketFactory::registerClass<PacketEE>("EE");
PacketFactory::registerClass<PacketHI>("HI");
PacketFactory::registerClass<PacketHP>("HP");
PacketFactory::registerClass<PacketID>("ID");
PacketFactory::registerClass<PacketMC>("MC");
PacketFactory::registerClass<PacketMS>("MS");
PacketFactory::registerClass<PacketPE>("PE");
PacketFactory::registerClass<PacketPW>("PW");
PacketFactory::registerClass<PacketRC>("RC");
PacketFactory::registerClass<PacketRD>("RD");
PacketFactory::registerClass<PacketRM>("RM");
PacketFactory::registerClass<PacketRT>("RT");
PacketFactory::registerClass<PacketSetcase>("SETCASE");
PacketFactory::registerClass<PacketZZ>("ZZ");
}

View File

@ -16,6 +16,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // // along with this program. If not, see <https://www.gnu.org/licenses/>. //
////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////
#include "include/network/network_socket.h" #include "include/network/network_socket.h"
#include "include/packet/packet_factory.h"
NetworkSocket::NetworkSocket(QTcpSocket *f_socket, QObject *parent) : NetworkSocket::NetworkSocket(QTcpSocket *f_socket, QObject *parent) :
QObject(parent) QObject(parent)
@ -106,7 +107,12 @@ void NetworkSocket::readData()
} }
for (const QString &l_single_packet : qAsConst(l_all_packets)) { for (const QString &l_single_packet : qAsConst(l_all_packets)) {
AOPacket l_packet(l_single_packet); AOPacket *l_packet = PacketFactory::createPacket(l_single_packet);
if (!l_packet) {
qDebug() << "Unimplemented packet: " << l_single_packet;
continue;
}
emit handlePacket(l_packet); emit handlePacket(l_packet);
} }
} }
@ -127,19 +133,24 @@ void NetworkSocket::ws_readData(QString f_data)
} }
for (const QString &l_single_packet : qAsConst(l_all_packets)) { for (const QString &l_single_packet : qAsConst(l_all_packets)) {
AOPacket l_packet(l_single_packet); AOPacket *l_packet = PacketFactory::createPacket(l_single_packet);
if (!l_packet) {
qDebug() << "Unimplemented packet: " << l_single_packet;
continue;
}
emit handlePacket(l_packet); emit handlePacket(l_packet);
} }
} }
void NetworkSocket::write(AOPacket f_packet) void NetworkSocket::write(AOPacket *f_packet)
{ {
if (m_socket_type == TCP) { if (m_socket_type == TCP) {
m_client_socket.tcp->write(f_packet.toUtf8()); m_client_socket.tcp->write(f_packet->toUtf8());
m_client_socket.tcp->flush(); m_client_socket.tcp->flush();
} }
else { else {
m_client_socket.ws->sendTextMessage(f_packet.toString()); m_client_socket.ws->sendTextMessage(f_packet->toString());
m_client_socket.ws->flush(); m_client_socket.ws->flush();
} }
} }

View File

@ -0,0 +1,36 @@
#include "include/packet/packet_askchaa.h"
#include "include/config_manager.h"
#include "include/server.h"
#include <QDebug>
PacketAskchaa::PacketAskchaa(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketAskchaa::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 0,
.header = "askchaa"};
return info;
}
void PacketAskchaa::handlePacket(AreaData *area, AOClient &client) const
{
Q_UNUSED(area)
// Evidence isn't loaded during this part anymore
// As a result, we can always send "0" for evidence length
// Client only cares about what it gets from LE
client.sendPacket("SI", {QString::number(client.getServer()->getCharacterCount()), "0", QString::number(client.getServer()->getAreaCount() + client.getServer()->getMusicList().length())});
}
bool PacketAskchaa::validatePacket() const
{
if (m_content.size() > 0) { // Too many arguments.
return false;
}
return true;
}

View File

@ -0,0 +1,82 @@
#include "include/packet/packet_casea.h"
#include "include/akashiutils.h"
#include "include/packet/packet_factory.h"
#include "include/server.h"
#include <QDebug>
PacketCasea::PacketCasea(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketCasea::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 6,
.header = "Casea"};
return info;
}
void PacketCasea::handlePacket(AreaData *area, AOClient &client) const
{
Q_UNUSED(area)
QString l_case_title = m_content[0];
QStringList l_needed_roles;
QList<bool> l_needs_list;
for (int i = 1; i <= 5; i++) {
bool is_int = false;
bool need = m_content[i].toInt(&is_int);
if (!is_int)
return;
l_needs_list.append(need);
}
QStringList l_roles = {"defense attorney", "prosecutor", "judge", "jurors", "stenographer"};
for (int i = 0; i < 5; i++) {
if (l_needs_list[i])
l_needed_roles.append(l_roles[i]);
}
if (l_needed_roles.isEmpty())
return;
QString l_message = "=== Case Announcement ===\r\n" + (client.m_ooc_name == "" ? client.m_current_char : client.m_ooc_name) + " needs " + l_needed_roles.join(", ") + " for " + (l_case_title == "" ? "a case" : l_case_title) + "!";
QList<AOClient *> l_clients_to_alert;
// here lies morton, RIP
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QSet<bool> l_needs_set(l_needs_list.begin(), l_needs_list.end());
#else
QSet<bool> l_needs_set = l_needs_list.toSet();
#endif
const QVector<AOClient *> l_clients = client.getServer()->getClients();
for (AOClient *l_client : l_clients) {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QSet<bool> l_matches(l_client->m_casing_preferences.begin(), l_client->m_casing_preferences.end());
l_matches.intersect(l_needs_set);
#else
QSet<bool> l_matches = l_client->m_casing_preferences.toSet().intersect(l_needs_set);
#endif
if (!l_matches.isEmpty() && !l_clients_to_alert.contains(l_client))
l_clients_to_alert.append(l_client);
}
for (AOClient *l_client : l_clients_to_alert) {
l_client->sendPacket(PacketFactory::createPacket("CASEA", {l_message, m_content[1], m_content[2], m_content[3], m_content[4], m_content[5], "1"}));
// you may be thinking, "hey wait a minute the network protocol documentation doesn't mention that last argument!"
// if you are in fact thinking that, you are correct! it is not in the documentation!
// however for some inscrutable reason Attorney Online 2 will outright reject a CASEA packet that does not have
// at least 7 arguments despite only using the first 6. Cera, i kneel. you have truly broken me.
}
}
bool PacketCasea::validatePacket() const
{
for (int i = 1; i < m_content.size(); i++) {
if (!AkashiUtils::checkArgType<int>(m_content.at(i))) {
return false;
}
}
return true;
}

View File

@ -0,0 +1,48 @@
#include "include/packet/packet_cc.h"
#include "include/akashiutils.h"
#include "include/config_manager.h"
#include "include/server.h"
#include <QDebug>
PacketCC::PacketCC(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketCC::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 3,
.header = "CC"};
return info;
}
void PacketCC::handlePacket(AreaData *area, AOClient &client) const
{
Q_UNUSED(area)
bool argument_ok;
int l_selected_char_id = m_content[1].toInt(&argument_ok);
if (!argument_ok) {
l_selected_char_id = client.SPECTATOR_ID;
}
if (l_selected_char_id < -1 || l_selected_char_id > client.getServer()->getCharacters().size() - 1) {
client.sendPacket("KK", {"A protocol error has been encountered.Packet : CC\nCharacter ID out of range."});
client.m_socket->close();
}
if (client.changeCharacter(l_selected_char_id))
client.m_char_id = l_selected_char_id;
if (client.m_char_id > client.SPECTATOR_ID) {
client.setSpectator(false);
}
}
bool PacketCC::validatePacket() const
{
return AkashiUtils::checkArgType<int>(m_content.at(1));
}

View File

@ -0,0 +1,33 @@
#include "include/packet/packet_ch.h"
#include "include/akashiutils.h"
#include "include/server.h"
#include <QDebug>
PacketCH::PacketCH(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketCH::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 1,
.header = "CH"};
return info;
}
void PacketCH::handlePacket(AreaData *area, AOClient &client) const
{
Q_UNUSED(area)
// Why does this packet exist
// At least Crystal made it useful
// It is now used for ping measurement
client.sendPacket("CHECK");
}
bool PacketCH::validatePacket() const
{
return AkashiUtils::checkArgType<int>(m_content.at(0));
}

View File

@ -0,0 +1,69 @@
#include "include/packet/packet_ct.h"
#include "include/akashidefs.h"
#include "include/config_manager.h"
#include "include/packet/packet_factory.h"
#include "include/server.h"
#include <QDebug>
PacketCT::PacketCT(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketCT::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 2,
.header = "CT"};
return info;
}
void PacketCT::handlePacket(AreaData *area, AOClient &client) const
{
if (client.m_is_ooc_muted) {
client.sendServerMessage("You are OOC muted, and cannot speak.");
return;
}
client.m_ooc_name = client.dezalgo(m_content[0]).replace(QRegExp("\\[|\\]|\\{|\\}|\\#|\\$|\\%|\\&"), ""); // no fucky wucky shit here
if (client.m_ooc_name.isEmpty() || client.m_ooc_name == ConfigManager::serverName()) // impersonation & empty name protection
return;
if (client.m_ooc_name.length() > 30) {
client.sendServerMessage("Your name is too long! Please limit it to under 30 characters.");
return;
}
if (client.m_is_logging_in) {
client.loginAttempt(m_content[1]);
return;
}
QString l_message = client.dezalgo(m_content[1]);
if (l_message.length() == 0 || l_message.length() > ConfigManager::maxCharacters())
return;
AOPacket *final_packet = PacketFactory::createPacket("CT", {client.m_ooc_name, l_message, "0"});
if (l_message.at(0) == '/') {
QStringList l_cmd_argv = l_message.split(" ", akashi::SkipEmptyParts);
QString l_command = l_cmd_argv[0].trimmed().toLower();
l_command = l_command.right(l_command.length() - 1);
l_cmd_argv.removeFirst();
int l_cmd_argc = l_cmd_argv.length();
client.handleCommand(l_command, l_cmd_argc, l_cmd_argv);
emit client.logCMD((client.m_current_char + " " + client.m_showname), client.m_ipid, client.m_ooc_name, l_command, l_cmd_argv, client.getServer()->getAreaById(client.m_current_area)->name());
return;
}
else {
client.getServer()->broadcast(final_packet, client.m_current_area);
}
emit client.logOOC((client.m_current_char + " " + client.m_showname), client.m_ooc_name, client.m_ipid, area->name(), l_message);
}
bool PacketCT::validatePacket() const
{
// Nothing to validate.
return true;
}

View File

@ -0,0 +1,36 @@
#include "include/packet/packet_de.h"
#include "include/akashiutils.h"
#include "include/server.h"
#include <QDebug>
PacketDE::PacketDE(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketDE::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 1,
.header = "DE"};
return info;
}
void PacketDE::handlePacket(AreaData *area, AOClient &client) const
{
if (!client.checkEvidenceAccess(area))
return;
bool is_int = false;
int l_idx = m_content[0].toInt(&is_int);
if (is_int && l_idx < area->evidence().size() && l_idx >= 0) {
area->deleteEvidence(l_idx);
}
client.sendEvidenceList(area);
}
bool PacketDE::validatePacket() const
{
return AkashiUtils::checkArgType<int>(m_content.at(0));
}

View File

@ -0,0 +1,37 @@
#include "include/packet/packet_ee.h"
#include "include/akashiutils.h"
#include "include/server.h"
#include <QDebug>
PacketEE::PacketEE(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketEE::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 4,
.header = "EE"};
return info;
}
void PacketEE::handlePacket(AreaData *area, AOClient &client) const
{
if (!client.checkEvidenceAccess(area))
return;
bool is_int = false;
int l_idx = m_content[0].toInt(&is_int);
AreaData::Evidence l_evi = {m_content[1], m_content[2], m_content[3]};
if (is_int && l_idx < area->evidence().size() && l_idx >= 0) {
area->replaceEvidence(l_idx, l_evi);
}
client.sendEvidenceList(area);
}
bool PacketEE::validatePacket() const
{
return AkashiUtils::checkArgType<int>(m_content.at(0));
}

View File

@ -0,0 +1,34 @@
#include "include/packet/packet_factory.h"
#include "include/packet/packet_generic.h"
AOPacket *PacketFactory::createPacket(QString header, QStringList contents)
{
if (!class_map.count(header)) {
return createInstance<PacketGeneric>(header, contents);
}
return class_map[header](contents);
}
AOPacket *PacketFactory::createPacket(QString raw_packet)
{
QString header;
QStringList contents;
if (raw_packet.at(0) == '#' || raw_packet.contains("%") || raw_packet.isEmpty()) {
qDebug() << "FantaCrypt or otherwise invalid packet received";
return PacketFactory::createPacket("Unknown", {"Unknown"});
}
QStringList packet_contents = raw_packet.split("#");
header = packet_contents[0];
packet_contents.removeFirst(); // Remove header
packet_contents.removeLast(); // Remove anything trailing after delimiter
contents = packet_contents;
AOPacket *packet = PacketFactory::createPacket(header, contents);
packet->unescapeContent();
return packet;
}

View File

@ -0,0 +1,31 @@
#include "include/packet/packet_generic.h"
#include <QDebug>
PacketGeneric::PacketGeneric(QString header, QStringList contents) :
AOPacket(contents),
header(header)
{
}
PacketInfo PacketGeneric::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 0,
.header = header};
return info;
}
void PacketGeneric::handlePacket(AreaData *area, AOClient &client) const
{
Q_UNUSED(area)
Q_UNUSED(client)
qDebug() << "ERROR: Cannot handle generic packet: " << header;
qDebug() << "Packet is either unimplemented, or is meant to be sent to client";
}
bool PacketGeneric::validatePacket() const
{
return true;
}

View File

@ -0,0 +1,50 @@
#include "include/packet/packet_hi.h"
#include "include/akashiutils.h"
#include "include/db_manager.h"
#include "include/server.h"
#include <QDebug>
PacketHI::PacketHI(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketHI::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 1,
.header = "HI"};
return info;
}
void PacketHI::handlePacket(AreaData *area, AOClient &client) const
{
Q_UNUSED(area)
QString incoming_hwid = m_content[0];
if (incoming_hwid.isEmpty() || !client.m_hwid.isEmpty()) {
// No double sending or empty HWIDs!
client.sendPacket("BD", {"A protocol error has been encountered. Packet : HI"});
client.m_socket->close();
return;
}
client.m_hwid = incoming_hwid;
emit client.getServer()->logConnectionAttempt(client.m_remote_ip.toString(), client.m_ipid, client.m_hwid);
auto ban = client.getServer()->getDatabaseManager()->isHDIDBanned(client.m_hwid);
if (ban.first) {
client.sendPacket("BD", {ban.second + "\nBan ID: " + QString::number(client.getServer()->getDatabaseManager()->getBanID(client.m_hwid))});
client.m_socket->close();
return;
}
client.sendPacket("ID", {QString::number(client.m_id), "akashi", QCoreApplication::applicationVersion()});
}
bool PacketHI::validatePacket() const
{
// We can always convert a string to a string. No point in checking.
return true;
}

View File

@ -0,0 +1,50 @@
#include "include/packet/packet_hp.h"
#include "include/akashiutils.h"
#include "include/packet/packet_factory.h"
#include "include/server.h"
#include <QDebug>
PacketHP::PacketHP(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketHP::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 2,
.header = "HP"};
return info;
}
void PacketHP::handlePacket(AreaData *area, AOClient &client) const
{
if (client.m_is_wtce_blocked) {
client.sendServerMessage("You are blocked from using the judge controls.");
return;
}
int l_newValue = m_content.at(1).toInt();
if (m_content[0] == "1") {
area->changeHP(AreaData::Side::DEFENCE, l_newValue);
}
else if (m_content[0] == "2") {
area->changeHP(AreaData::Side::PROSECUTOR, l_newValue);
}
client.getServer()->broadcast(PacketFactory::createPacket("HP", {"1", QString::number(area->defHP())}), area->index());
client.getServer()->broadcast(PacketFactory::createPacket("HP", {"2", QString::number(area->proHP())}), area->index());
client.updateJudgeLog(area, &client, "updated the penalties");
}
bool PacketHP::validatePacket() const
{
if (!AkashiUtils::checkArgType<int>(m_content.at(0)))
return false;
if (!AkashiUtils::checkArgType<int>(m_content.at(1)))
return false;
return true;
}

View File

@ -0,0 +1,76 @@
#include "include/packet/packet_id.h"
#include "include/config_manager.h"
#include "include/server.h"
#include <QDebug>
PacketID::PacketID(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketID::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 2,
.header = "ID"};
return info;
}
void PacketID::handlePacket(AreaData *area, AOClient &client) const
{
Q_UNUSED(area)
if (client.m_version.major == 2) {
// No double sending of the ID packet!
client.sendPacket("BD", {"A protocol error has been encountered. Packet : ID"});
client.m_socket->close();
return;
}
// Full feature list as of AO 2.8.5
// The only ones that are critical to ensuring the server works are
// "noencryption" and "fastloading"
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"};
client.m_version.string = m_content[1];
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(client.m_version.string);
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 (m_content[0] == "webAO") {
client.m_version.release = 2;
client.m_version.major = 10;
client.m_version.minor = 0;
}
if (client.m_version.release != 2) {
// No valid ID packet resolution.
client.sendPacket("BD", {"A protocol error has been encountered. Packet : ID\nMajor version not recognised."});
client.m_socket->close();
return;
}
client.sendPacket("PN", {QString::number(client.getServer()->getPlayerCount()), QString::number(ConfigManager::maxPlayers()), ConfigManager::serverDescription()});
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});
}
}
bool PacketID::validatePacket() const
{
return true;
}

View File

@ -0,0 +1,98 @@
#include "include/packet/packet_mc.h"
#include "include/music_manager.h"
#include "include/packet/packet_factory.h"
#include "include/server.h"
#include <QDebug>
PacketMC::PacketMC(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketMC::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 2,
.header = "MC"};
return info;
}
void PacketMC::handlePacket(AreaData *area, AOClient &client) const
{
// 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 l_argument = m_content[0];
if (client.getServer()->getMusicList().contains(l_argument) || client.m_music_manager->isCustom(client.m_current_area, l_argument) || l_argument == "~stop.mp3") { // ~stop.mp3 is a dummy track used by 2.9+
// We have a song here
if (client.m_is_spectator) {
client.sendServerMessage("Spectator are blocked from changing the music.");
return;
}
if (client.m_is_dj_blocked) {
client.sendServerMessage("You are blocked from changing the music.");
return;
}
if (!area->isMusicAllowed() && !client.checkPermission(ACLRole::CM)) {
client.sendServerMessage("Music is disabled in this area.");
return;
}
QString l_effects;
if (m_content.length() >= 4)
l_effects = m_content[3];
else
l_effects = "0";
QString l_final_song;
// As categories can be used to stop music we need to check if it has a dot for the extension. If not, we assume its a category.
if (!l_argument.contains("."))
l_final_song = "~stop.mp3";
else
l_final_song = l_argument;
// Jukebox intercepts the direct playing of messages.
if (area->isjukeboxEnabled()) {
QString l_jukebox_reply = area->addJukeboxSong(l_final_song);
client.sendServerMessage(l_jukebox_reply);
return;
}
if (l_final_song != "~stop.mp3") {
// We might have an aliased song. We check for its real songname and send it to the clients.
QPair<QString, float> l_song = client.m_music_manager->songInformation(l_final_song, client.m_current_area);
l_final_song = l_song.first;
}
AOPacket *l_music_change = PacketFactory::createPacket("MC", {l_final_song, m_content[1], client.m_showname, "1", "0", l_effects});
client.getServer()->broadcast(l_music_change, client.m_current_area);
// Since we can't ensure a user has their showname set, we check if its empty to prevent
//"played by ." in /currentmusic.
if (client.m_showname.isEmpty()) {
area->changeMusic(client.m_current_char, l_final_song);
return;
}
area->changeMusic(client.m_showname, l_final_song);
return;
}
for (int i = 0; i < client.getServer()->getAreaCount(); i++) {
QString l_area = client.getServer()->getAreaName(i);
if (l_area == l_argument) {
client.changeArea(i);
break;
}
}
}
bool PacketMC::validatePacket() const
{
return true;
}

View File

@ -0,0 +1,436 @@
#include "include/packet/packet_ms.h"
#include "include/config_manager.h"
#include "include/packet/packet_factory.h"
#include "include/server.h"
#include <QDebug>
PacketMS::PacketMS(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketMS::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 15,
.header = "MS"};
return info;
}
void PacketMS::handlePacket(AreaData *area, AOClient &client) const
{
if (client.m_is_muted) {
client.sendServerMessage("You cannot speak while muted.");
return;
}
if (!area->isMessageAllowed() || !client.getServer()->isMessageAllowed()) {
return;
}
AOPacket *validated_packet = validateIcPacket(client);
if (validated_packet->getPacketInfo().header == "INVALID")
return;
if (client.m_pos != "")
validated_packet->setContentField(5, client.m_pos);
client.getServer()->broadcast(validated_packet, client.m_current_area);
emit client.logIC((client.m_current_char + " " + client.m_showname), client.m_ooc_name, client.m_ipid, client.getServer()->getAreaById(client.m_current_area)->name(), client.m_last_message);
area->updateLastICMessage(validated_packet->getContent());
area->startMessageFloodguard(ConfigManager::messageFloodguard());
client.getServer()->startMessageFloodguard(ConfigManager::globalMessageFloodguard());
}
AOPacket *PacketMS::validateIcPacket(AOClient &client) const
{
// Welcome to the super cursed server-side IC chat validation hell
// I wanted to use enums or #defines here to make the
// indicies of the args arrays more readable. But,
// in typical AO fasion, the indicies for the incoming
// and outgoing packets are different. Just RTFM.
// This packet can be sent with a minimum required args of 15.
// 2.6+ extensions raise this to 19, and 2.8 further raises this to 26.
AOPacket *l_invalid = PacketFactory::createPacket("INVALID", {});
QStringList l_args;
if (client.isSpectator() || client.m_current_char.isEmpty() || !client.m_joined)
// Spectators cannot use IC
return l_invalid;
AreaData *area = client.getServer()->getAreaById(client.m_current_area);
if (area->lockStatus() == AreaData::LockStatus::SPECTATABLE && !area->invited().contains(client.m_id) && !client.checkPermission(ACLRole::BYPASS_LOCKS))
// Non-invited players cannot speak in spectatable areas
return l_invalid;
QList<QVariant> l_incoming_args;
for (const QString &l_arg : m_content) {
l_incoming_args.append(QVariant(l_arg));
}
// desk modifier
QStringList allowed_desk_mods;
allowed_desk_mods << "chat"
<< "0"
<< "1"
<< "2"
<< "3"
<< "4"
<< "5";
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
// instead of overwriting correct netcode behaviour on the serverside.
if (l_incoming_deskmod == "chat") {
l_args.append("1");
}
else {
l_args.append(l_incoming_args[0].toString());
}
}
else
return l_invalid;
// preanim
l_args.append(l_incoming_args[1].toString());
// char name
if (client.m_current_char.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()) {
if (!client.getServer()->getCharacters().contains(l_incoming_args[2].toString(), Qt::CaseInsensitive))
return l_invalid;
}
qDebug() << "INI swap detected from " << client.getIpid();
}
client.m_current_iniswap = l_incoming_args[2].toString();
l_args.append(l_incoming_args[2].toString());
// emote
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[4].toString().size() > ConfigManager::maxCharacters())
return l_invalid;
QString l_incoming_msg = client.dezalgo(l_incoming_args[4].toString().trimmed());
if (!area->lastICMessage().isEmpty() && l_incoming_msg == area->lastICMessage()[4] && l_incoming_msg != "")
return l_invalid;
if (l_incoming_msg == "" && area->blankpostingAllowed() == false) {
client.sendServerMessage("Blankposting has been forbidden in this area.");
return l_invalid;
}
if (client.m_is_gimped) {
QString l_gimp_message = ConfigManager::gimpList().at((client.genRand(1, ConfigManager::gimpList().size() - 1)));
l_incoming_msg = l_gimp_message;
}
if (client.m_is_shaken) {
QStringList l_parts = l_incoming_msg.split(" ");
std::random_device rng;
std::mt19937 urng(rng());
std::shuffle(l_parts.begin(), l_parts.end(), urng);
l_incoming_msg = l_parts.join(" ");
}
if (client.m_is_disemvoweled) {
QString l_disemvoweled_message = l_incoming_msg.remove(QRegExp("[AEIOUaeiou]"));
l_incoming_msg = l_disemvoweled_message;
}
client.m_last_message = l_incoming_msg;
l_args.append(l_incoming_msg);
// side
// this is validated clientside so w/e
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.updateEvidenceList(client.getServer()->getAreaById(client.m_current_area));
}
// sfx name
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.
// If this value is a 4, it will crash the client. Why? Who knows, but it does.
// Now here is the kicker: in certain versions, the client would incorrectly send a 4 here
// For a long time, by configuring the client to do a zoom with a preanim, it would send 4
// 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[7].toInt();
if (emote_mod == 4)
emote_mod = 6;
if (emote_mod != 0 && emote_mod != 1 && emote_mod != 2 && emote_mod != 5 && emote_mod != 6)
return l_invalid;
l_args.append(QString::number(emote_mod));
// char id
if (l_incoming_args[8].toInt() != client.m_char_id)
return l_invalid;
l_args.append(l_incoming_args[8].toString());
// sfx delay
l_args.append(l_incoming_args[9].toString());
// objection modifier
if (area->isShoutAllowed()) {
if (l_incoming_args[10].toString().contains("4")) {
// custom shout includes text metadata
l_args.append(l_incoming_args[10].toString());
}
else {
int l_obj_mod = l_incoming_args[10].toInt();
if ((l_obj_mod < 0) || (l_obj_mod > 4)) {
return l_invalid;
}
l_args.append(QString::number(l_obj_mod));
}
}
else {
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[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[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[13].toInt();
if (realization != 0 && realization != 1)
return l_invalid;
l_args.append(QString::number(realization));
// text color
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));
// 2.6 packet extensions
if (l_incoming_args.length() >= 19) {
// showname
QString l_incoming_showname = client.dezalgo(l_incoming_args[15].toString().trimmed());
if (!(l_incoming_showname == client.m_current_char || l_incoming_showname.isEmpty()) && !area->shownameAllowed()) {
client.sendServerMessage("Shownames are not allowed in this area!");
return l_invalid;
}
if (l_incoming_showname.length() > 30) {
client.sendServerMessage("Your showname is too long! Please limit it to under 30 characters");
return l_invalid;
}
// if the raw input is not empty but the trimmed input is, use a single space
if (l_incoming_showname.isEmpty() && !l_incoming_args[15].toString().isEmpty())
l_incoming_showname = " ";
l_args.append(l_incoming_showname);
client.m_showname = l_incoming_showname;
// 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[16].toString().split("^");
client.m_pairing_with = l_pair_data[0].toInt();
QString l_front_back = "";
if (l_pair_data.length() > 1)
l_front_back = "^" + l_pair_data[1];
int l_other_charid = client.m_pairing_with;
bool l_pairing = false;
QString l_other_name = "0";
QString l_other_emote = "0";
QString l_other_offset = "0";
QString l_other_flip = "0";
for (int l_client_id : area->joinedIDs()) {
AOClient *l_client = client.getServer()->getClientByID(l_client_id);
if (l_client->m_pairing_with == client.m_char_id && l_other_charid != client.m_char_id && l_client->m_char_id == client.m_pairing_with && l_client->m_pos == client.m_pos) {
l_other_name = l_client->m_current_iniswap;
l_other_emote = l_client->m_emote;
l_other_offset = l_client->m_offset;
l_other_flip = l_client->m_flipping;
l_pairing = true;
}
}
if (!l_pairing) {
l_other_charid = -1;
l_front_back = "";
}
l_args.append(QString::number(l_other_charid) + l_front_back);
l_args.append(l_other_name);
l_args.append(l_other_emote);
// self offset
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);
}
l_args.append(l_other_flip);
// immediate text processing
int l_immediate = l_incoming_args[18].toInt();
if (area->forceImmediate()) {
if (l_args[7] == "1" || l_args[7] == "2") {
l_args[7] = "0";
l_immediate = 1;
}
else if (l_args[7] == "6") {
l_args[7] = "5";
l_immediate = 1;
}
}
if (l_immediate != 1 && l_immediate != 0)
return l_invalid;
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[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[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[21].toString());
// frames realization
l_args.append(l_incoming_args[22].toString());
// frames sfx
l_args.append(l_incoming_args[23].toString());
// additive
int l_additive = l_incoming_args[24].toInt();
if (l_additive != 0 && l_additive != 1)
return l_invalid;
else if (area->lastICMessage().isEmpty()) {
l_additive = 0;
}
else if (!(client.m_char_id == area->lastICMessage()[8].toInt())) {
l_additive = 0;
}
else if (l_additive == 1) {
l_args[4].insert(0, " ");
}
l_args.append(QString::number(l_additive));
// effect
l_args.append(l_incoming_args[25].toString());
}
// Testimony playback
if (area->testimonyRecording() == AreaData::TestimonyRecording::RECORDING || area->testimonyRecording() == AreaData::TestimonyRecording::ADD) {
if (l_args[5] != "wit")
return PacketFactory::createPacket("MS", l_args);
if (area->statement() == -1) {
l_args[4] = "~~\\n-- " + l_args[4] + " --";
l_args[14] = "3";
client.getServer()->broadcast(PacketFactory::createPacket("RT", {"testimony1"}), client.m_current_area);
}
client.addStatement(l_args);
}
else if (area->testimonyRecording() == AreaData::TestimonyRecording::UPDATE) {
l_args = client.updateStatement(l_args);
}
else if (area->testimonyRecording() == AreaData::TestimonyRecording::PLAYBACK) {
AreaData::TestimonyProgress l_progress;
if (l_args[4] == ">") {
client.m_pos = "wit";
auto l_statement = area->jumpToStatement(area->statement() + 1);
l_args = l_statement.first;
l_progress = l_statement.second;
if (l_progress == AreaData::TestimonyProgress::LOOPED) {
client.sendServerMessageArea("Last statement reached. Looping to first statement.");
}
}
if (l_args[4] == "<") {
client.m_pos = "wit";
auto l_statement = area->jumpToStatement(area->statement() - 1);
l_args = l_statement.first;
l_progress = l_statement.second;
if (l_progress == AreaData::TestimonyProgress::STAYED_AT_FIRST) {
client.sendServerMessage("First statement reached.");
}
}
QString l_decoded_message = client.decodeMessage(l_args[4]); // Get rid of that pesky encoding first.
QRegularExpression jump("(?<arrow>>)(?<int>[0,1,2,3,4,5,6,7,8,9]+)");
QRegularExpressionMatch match = jump.match(l_decoded_message);
if (match.hasMatch()) {
client.m_pos = "wit";
auto l_statement = area->jumpToStatement(match.captured("int").toInt());
l_args = l_statement.first;
l_progress = l_statement.second;
switch (l_progress) {
case AreaData::TestimonyProgress::LOOPED:
{
client.sendServerMessageArea("Last statement reached. Looping to first statement.");
break;
}
case AreaData::TestimonyProgress::STAYED_AT_FIRST:
{
client.sendServerMessage("First statement reached.");
Q_FALLTHROUGH();
}
case AreaData::TestimonyProgress::OK:
default:
// No need to handle.
break;
}
}
}
return PacketFactory::createPacket("MS", l_args);
}
bool PacketMS::validatePacket() const
{
return true;
}

View File

@ -0,0 +1,32 @@
#include "include/packet/packet_pe.h"
#include "include/server.h"
#include <QDebug>
PacketPE::PacketPE(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketPE::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 3,
.header = "PE"};
return info;
}
void PacketPE::handlePacket(AreaData *area, AOClient &client) const
{
if (!client.checkEvidenceAccess(area))
return;
AreaData::Evidence l_evi = {m_content[0], m_content[1], m_content[2]};
area->appendEvidence(l_evi);
client.sendEvidenceList(area);
}
bool PacketPE::validatePacket() const
{
return true;
}

View File

@ -0,0 +1,30 @@
#include "include/packet/packet_pw.h"
#include "include/server.h"
#include <QDebug>
PacketPW::PacketPW(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketPW::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 1,
.header = "PW"};
return info;
}
void PacketPW::handlePacket(AreaData *area, AOClient &client) const
{
Q_UNUSED(area)
client.m_password = m_content[0];
}
bool PacketPW::validatePacket() const
{
return true;
}

View File

@ -0,0 +1,30 @@
#include "include/packet/packet_rc.h"
#include "include/server.h"
#include <QDebug>
PacketRC::PacketRC(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketRC::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 0,
.header = "RC"};
return info;
}
void PacketRC::handlePacket(AreaData *area, AOClient &client) const
{
Q_UNUSED(area)
client.sendPacket("SC", client.getServer()->getCharacters());
}
bool PacketRC::validatePacket() const
{
return true;
}

View File

@ -0,0 +1,72 @@
#include "include/packet/packet_rd.h"
#include "include/config_manager.h"
#include "include/server.h"
#include <QDebug>
PacketRD::PacketRD(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketRD::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 0,
.header = "RD"};
return info;
}
void PacketRD::handlePacket(AreaData *area, AOClient &client) const
{
if (client.m_hwid == "") {
// No early connecting!
client.m_socket->close();
return;
}
if (client.m_joined) {
return;
}
client.m_joined = true;
client.getServer()->updateCharsTaken(area);
client.sendEvidenceList(area);
client.sendPacket("HP", {"1", QString::number(area->defHP())});
client.sendPacket("HP", {"2", QString::number(area->proHP())});
client.sendPacket("FA", client.getServer()->getAreaNames());
// Here lies OPPASS, the genius of FanatSors who send the modpass to everyone in plain text.
client.sendPacket("DONE");
client.sendPacket("BN", {area->background()});
client.sendServerMessage("=== MOTD ===\r\n" + ConfigManager::motd() + "\r\n=============");
client.fullArup(); // Give client all the area data
if (client.getServer()->timer->isActive()) {
client.sendPacket("TI", {"0", "2"});
client.sendPacket("TI", {"0", "0", QString::number(QTime(0, 0).msecsTo(QTime(0, 0).addMSecs(client.getServer()->timer->remainingTime())))});
}
else {
client.sendPacket("TI", {"0", "3"});
}
const QList<QTimer *> l_timers = area->timers();
for (QTimer *l_timer : l_timers) {
int l_timer_id = area->timers().indexOf(l_timer) + 1;
if (l_timer->isActive()) {
client.sendPacket("TI", {QString::number(l_timer_id), "2"});
client.sendPacket("TI", {QString::number(l_timer_id), "0", QString::number(QTime(0, 0).msecsTo(QTime(0, 0).addMSecs(l_timer->remainingTime())))});
}
else {
client.sendPacket("TI", {QString::number(l_timer_id), "3"});
}
}
emit client.joined();
area->clientJoinedArea(-1, client.m_id);
client.arup(client.ARUPType::PLAYER_COUNT, true); // Tell everyone there is a new player
}
bool PacketRD::validatePacket() const
{
return true;
}

View File

@ -0,0 +1,30 @@
#include "include/packet/packet_rm.h"
#include "include/server.h"
#include <QDebug>
PacketRM::PacketRM(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketRM::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 0,
.header = "RM"};
return info;
}
void PacketRM::handlePacket(AreaData *area, AOClient &client) const
{
Q_UNUSED(area)
client.sendPacket("SM", client.getServer()->getAreaNames() + client.getServer()->getMusicList());
}
bool PacketRM::validatePacket() const
{
return true;
}

View File

@ -0,0 +1,43 @@
#include "include/packet/packet_rt.h"
#include "include/packet/packet_factory.h"
#include "include/server.h"
#include <QDebug>
PacketRT::PacketRT(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketRT::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 1,
.header = "RT"};
return info;
}
void PacketRT::handlePacket(AreaData *area, AOClient &client) const
{
if (client.m_is_wtce_blocked) {
client.sendServerMessage("You are blocked from using the judge controls.");
return;
}
if (!area->isWtceAllowed()) {
client.sendServerMessage("WTCE animations have been disabled in this area.");
return;
}
if (QDateTime::currentDateTime().toSecsSinceEpoch() - client.m_last_wtce_time <= 5)
return;
client.m_last_wtce_time = QDateTime::currentDateTime().toSecsSinceEpoch();
client.getServer()->broadcast(PacketFactory::createPacket("RT", m_content), client.m_current_area);
client.updateJudgeLog(area, &client, "WT/CE");
}
bool PacketRT::validatePacket() const
{
return true;
}

View File

@ -0,0 +1,38 @@
#include "include/packet/packet_setcase.h"
#include "include/server.h"
#include <QDebug>
PacketSetcase::PacketSetcase(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketSetcase::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 7,
.header = "Setcase"};
return info;
}
void PacketSetcase::handlePacket(AreaData *area, AOClient &client) const
{
Q_UNUSED(area)
QList<bool> l_prefs_list;
for (int i = 2; i <= 6; i++) {
bool is_int = false;
bool pref = m_content[i].toInt(&is_int);
if (!is_int)
return;
l_prefs_list.append(pref);
}
client.m_casing_preferences = l_prefs_list;
}
bool PacketSetcase::validatePacket() const
{
return true;
}

View File

@ -0,0 +1,57 @@
#include "include/packet/packet_zz.h"
#include "include/config_manager.h"
#include "include/packet/packet_factory.h"
#include "include/server.h"
#include <QQueue>
PacketZZ::PacketZZ(QStringList &contents) :
AOPacket(contents)
{
}
PacketInfo PacketZZ::getPacketInfo() const
{
PacketInfo info{
.acl_permission = ACLRole::Permission::NONE,
.min_args = 0,
.header = "ZZ"};
return info;
}
void PacketZZ::handlePacket(AreaData *area, AOClient &client) const
{
QString l_name = client.m_ooc_name;
if (client.m_ooc_name.isEmpty())
l_name = client.m_current_char;
QString l_areaName = area->name();
QString l_modcallNotice = "!!!MODCALL!!!\nArea: " + l_areaName + "\nCaller: " + l_name + "\n";
if (!m_content[0].isEmpty())
l_modcallNotice.append("Reason: " + m_content[0]);
else
l_modcallNotice.append("No reason given.");
const QVector<AOClient *> l_clients = client.getServer()->getClients();
for (AOClient *l_client : l_clients) {
if (l_client->m_authenticated)
l_client->sendPacket(PacketFactory::createPacket("ZZ", {l_modcallNotice}));
}
emit client.logModcall((client.m_current_char + " " + client.m_showname), client.m_ipid, client.m_ooc_name, client.getServer()->getAreaById(client.m_current_area)->name());
if (ConfigManager::discordModcallWebhookEnabled()) {
QString l_name = client.m_ooc_name;
if (client.m_ooc_name.isEmpty())
l_name = client.m_current_char;
QString l_areaName = area->name();
emit client.getServer()->modcallWebhookRequest(l_name, l_areaName, m_content.value(0), client.getServer()->getAreaBuffer(l_areaName));
}
}
bool PacketZZ::validatePacket() const
{
return true;
}

View File

@ -24,577 +24,10 @@
#include "include/config_manager.h" #include "include/config_manager.h"
#include "include/db_manager.h" #include "include/db_manager.h"
#include "include/music_manager.h" #include "include/music_manager.h"
#include "include/network/aopacket.h" #include "include/packet/packet_factory.h"
#include "include/server.h" #include "include/server.h"
void AOClient::pktDefault(AreaData *area, int argc, QStringList argv, AOPacket packet) void AOClient::sendEvidenceList(AreaData *area) const
{
Q_UNUSED(area);
Q_UNUSED(argc);
Q_UNUSED(argv);
#ifdef NET_DEBUG
qDebug() << "Unimplemented packet:" << packet.getHeader() << packet.getContent();
#else
Q_UNUSED(packet);
#endif
}
void AOClient::pktHardwareId(AreaData *area, int argc, QStringList argv, AOPacket packet)
{
Q_UNUSED(area);
Q_UNUSED(argc);
Q_UNUSED(packet);
QString l_incoming_hwid = argv[0];
if (l_incoming_hwid.isEmpty() || !m_hwid.isEmpty()) {
// No double sending or empty HWIDs!
sendPacket(AOPacket("BD", {"A protocol error has been encountered. Packet : HI"}));
m_socket->close();
return;
}
m_hwid = l_incoming_hwid;
emit server->logConnectionAttempt(m_remote_ip.toString(), m_ipid, m_hwid);
auto l_ban = server->getDatabaseManager()->isHDIDBanned(m_hwid);
if (l_ban.first) {
sendPacket("BD", {l_ban.second + "\nBan ID: " + QString::number(server->getDatabaseManager()->getBanID(m_hwid))});
m_socket->close();
return;
}
sendPacket("ID", {QString::number(m_id), "akashi", QCoreApplication::applicationVersion()});
}
void AOClient::pktSoftwareId(AreaData *area, int argc, QStringList argv, AOPacket packet)
{
Q_UNUSED(area);
Q_UNUSED(argc);
Q_UNUSED(packet);
if (m_version.major == 2) {
// No double sending of the ID packet!
sendPacket(AOPacket("BD", {"A protocol error has been encountered. Packet : ID"}));
m_socket->close();
return;
}
// Full feature list as of AO 2.8.5
// The only ones that are critical to ensuring the server works are
// "noencryption" and "fastloading"
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"};
m_version.string = argv[1];
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_version.string);
if (l_match.hasMatch()) {
m_version.release = l_match.captured(1).toInt();
m_version.major = l_match.captured(2).toInt();
m_version.minor = l_match.captured(3).toInt();
}
if (argv[0] == "webAO") {
m_version.release = 2;
m_version.major = 10;
m_version.minor = 0;
}
if (m_version.release != 2) {
// No valid ID packet resolution.
sendPacket(AOPacket("BD", {"A protocol error has been encountered. Packet : ID\nMajor version not recognised."}));
m_socket->close();
return;
}
sendPacket("PN", {QString::number(server->getPlayerCount()), QString::number(ConfigManager::maxPlayers()), ConfigManager::serverDescription()});
sendPacket("FL", l_feature_list);
if (ConfigManager::assetUrl().isValid()) {
QByteArray l_asset_url = ConfigManager::assetUrl().toEncoded(QUrl::EncodeSpaces);
sendPacket("ASS", {l_asset_url});
}
}
void AOClient::pktBeginLoad(AreaData *area, int argc, QStringList argv, AOPacket packet)
{
Q_UNUSED(area);
Q_UNUSED(argc);
Q_UNUSED(argv);
Q_UNUSED(packet);
// Evidence isn't loaded during this part anymore
// As a result, we can always send "0" for evidence length
// Client only cares about what it gets from LE
sendPacket("SI", {QString::number(server->getCharacterCount()), "0", QString::number(server->getAreaCount() + server->getMusicList().length())});
}
void AOClient::pktRequestChars(AreaData *area, int argc, QStringList argv, AOPacket packet)
{
Q_UNUSED(area);
Q_UNUSED(argc);
Q_UNUSED(argv);
Q_UNUSED(packet);
sendPacket("SC", server->getCharacters());
}
void AOClient::pktRequestMusic(AreaData *area, int argc, QStringList argv, AOPacket packet)
{
Q_UNUSED(area);
Q_UNUSED(argc);
Q_UNUSED(argv);
Q_UNUSED(packet);
sendPacket("SM", server->getAreaNames() + server->getMusicList());
}
void AOClient::pktLoadingDone(AreaData *area, int argc, QStringList argv, AOPacket packet)
{
Q_UNUSED(argc);
Q_UNUSED(argv);
Q_UNUSED(packet);
if (m_hwid == "") {
// No early connecting!
m_socket->close();
return;
}
if (m_joined) {
return;
}
m_joined = true;
server->updateCharsTaken(area);
sendEvidenceList(area);
sendPacket("HP", {"1", QString::number(area->defHP())});
sendPacket("HP", {"2", QString::number(area->proHP())});
sendPacket("FA", server->getAreaNames());
// Here lies OPPASS, the genius of FanatSors who send the modpass to everyone in plain text.
sendPacket("DONE");
sendPacket("BN", {area->background()});
sendServerMessage("=== MOTD ===\r\n" + ConfigManager::motd() + "\r\n=============");
fullArup(); // Give client all the area data
if (server->timer->isActive()) {
sendPacket("TI", {"0", "2"});
sendPacket("TI", {"0", "0", QString::number(QTime(0, 0).msecsTo(QTime(0, 0).addMSecs(server->timer->remainingTime())))});
}
else {
sendPacket("TI", {"0", "3"});
}
const QList<QTimer *> l_timers = area->timers();
for (QTimer *l_timer : l_timers) {
int l_timer_id = area->timers().indexOf(l_timer) + 1;
if (l_timer->isActive()) {
sendPacket("TI", {QString::number(l_timer_id), "2"});
sendPacket("TI", {QString::number(l_timer_id), "0", QString::number(QTime(0, 0).msecsTo(QTime(0, 0).addMSecs(l_timer->remainingTime())))});
}
else {
sendPacket("TI", {QString::number(l_timer_id), "3"});
}
}
emit joined();
area->clientJoinedArea(-1, m_id);
arup(ARUPType::PLAYER_COUNT, true); // Tell everyone there is a new player
}
void AOClient::pktCharPassword(AreaData *area, int argc, QStringList argv, AOPacket packet)
{
Q_UNUSED(area);
Q_UNUSED(argc);
Q_UNUSED(packet);
m_password = argv[0];
}
void AOClient::pktSelectChar(AreaData *area, int argc, QStringList argv, AOPacket packet)
{
Q_UNUSED(area);
Q_UNUSED(argc);
Q_UNUSED(packet);
bool argument_ok;
int l_selected_char_id = argv[1].toInt(&argument_ok);
if (!argument_ok) {
l_selected_char_id = SPECTATOR_ID;
}
if (l_selected_char_id < -1 || l_selected_char_id > server->getCharacters().size() - 1) {
sendPacket(AOPacket("KK", {"A protocol error has been encountered.Packet : CC\nCharacter ID out of range."}));
m_socket->close();
}
if (changeCharacter(l_selected_char_id))
m_char_id = l_selected_char_id;
if (m_char_id > SPECTATOR_ID) {
setSpectator(false);
}
}
void AOClient::pktIcChat(AreaData *area, int argc, QStringList argv, AOPacket packet)
{
Q_UNUSED(argc);
Q_UNUSED(argv);
if (m_is_muted) {
sendServerMessage("You cannot speak while muted.");
return;
}
if (!area->isMessageAllowed() || !server->isMessageAllowed()) {
return;
}
AOPacket validated_packet = validateIcPacket(packet);
if (validated_packet.getHeader() == "INVALID")
return;
if (m_pos != "")
validated_packet.setContentField(5, m_pos);
server->broadcast(validated_packet, m_current_area);
emit logIC((m_current_char + " " + m_showname), m_ooc_name, m_ipid, server->getAreaById(m_current_area)->name(), m_last_message);
area->updateLastICMessage(validated_packet.getContent());
area->startMessageFloodguard(ConfigManager::messageFloodguard());
server->startMessageFloodguard(ConfigManager::globalMessageFloodguard());
}
void AOClient::pktOocChat(AreaData *area, int argc, QStringList argv, AOPacket packet)
{
Q_UNUSED(argc);
Q_UNUSED(packet);
if (m_is_ooc_muted) {
sendServerMessage("You are OOC muted, and cannot speak.");
return;
}
m_ooc_name = dezalgo(argv[0]).replace(QRegExp("\\[|\\]|\\{|\\}|\\#|\\$|\\%|\\&"), ""); // no fucky wucky shit here
if (m_ooc_name.isEmpty() || m_ooc_name == ConfigManager::serverName()) // impersonation & empty name protection
return;
if (m_ooc_name.length() > 30) {
sendServerMessage("Your name is too long! Please limit it to under 30 characters.");
return;
}
if (m_is_logging_in) {
loginAttempt(argv[1]);
return;
}
QString l_message = dezalgo(argv[1]);
if (l_message.length() == 0 || l_message.length() > ConfigManager::maxCharacters())
return;
AOPacket final_packet("CT", {m_ooc_name, l_message, "0"});
if (l_message.at(0) == '/') {
QStringList l_cmd_argv = l_message.split(" ", akashi::SkipEmptyParts);
QString l_command = l_cmd_argv[0].trimmed().toLower();
l_command = l_command.right(l_command.length() - 1);
l_cmd_argv.removeFirst();
int l_cmd_argc = l_cmd_argv.length();
handleCommand(l_command, l_cmd_argc, l_cmd_argv);
emit logCMD((m_current_char + " " + m_showname), m_ipid, m_ooc_name, l_command, l_cmd_argv, server->getAreaById(m_current_area)->name());
return;
}
else {
server->broadcast(final_packet, m_current_area);
}
emit logOOC((m_current_char + " " + m_showname), m_ooc_name, m_ipid, area->name(), l_message);
}
void AOClient::pktPing(AreaData *area, int argc, QStringList argv, AOPacket packet)
{
Q_UNUSED(area);
Q_UNUSED(argc);
Q_UNUSED(argv);
Q_UNUSED(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)
{
Q_UNUSED(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 l_argument = argv[0];
if (server->getMusicList().contains(l_argument) || m_music_manager->isCustom(m_current_area, l_argument) || l_argument == "~stop.mp3") { // ~stop.mp3 is a dummy track used by 2.9+
// We have a song here
if (m_is_spectator) {
sendServerMessage("Spectator are blocked from changing the music.");
return;
}
if (m_is_dj_blocked) {
sendServerMessage("You are blocked from changing the music.");
return;
}
if (!area->isMusicAllowed() && !checkPermission(ACLRole::CM)) {
sendServerMessage("Music is disabled in this area.");
return;
}
QString l_effects;
if (argc >= 4)
l_effects = argv[3];
else
l_effects = "0";
QString l_final_song;
// As categories can be used to stop music we need to check if it has a dot for the extension. If not, we assume its a category.
if (!l_argument.contains("."))
l_final_song = "~stop.mp3";
else
l_final_song = l_argument;
// Jukebox intercepts the direct playing of messages.
if (area->isjukeboxEnabled()) {
QString l_jukebox_reply = area->addJukeboxSong(l_final_song);
sendServerMessage(l_jukebox_reply);
return;
}
if (l_final_song != "~stop.mp3") {
// We might have an aliased song. We check for its real songname and send it to the clients.
QPair<QString, float> l_song = m_music_manager->songInformation(l_final_song, m_current_area);
l_final_song = l_song.first;
}
AOPacket l_music_change("MC", {l_final_song, argv[1], m_showname, "1", "0", l_effects});
server->broadcast(l_music_change, m_current_area);
// Since we can't ensure a user has their showname set, we check if its empty to prevent
//"played by ." in /currentmusic.
if (m_showname.isEmpty()) {
area->changeMusic(m_current_char, l_final_song);
return;
}
area->changeMusic(m_showname, l_final_song);
return;
}
for (int i = 0; i < server->getAreaCount(); i++) {
QString l_area = server->getAreaName(i);
if (l_area == l_argument) {
changeArea(i);
break;
}
}
}
void AOClient::pktWtCe(AreaData *area, int argc, QStringList argv, AOPacket packet)
{
Q_UNUSED(argc);
Q_UNUSED(argv);
if (m_is_wtce_blocked) {
sendServerMessage("You are blocked from using the judge controls.");
return;
}
if (!area->isWtceAllowed()) {
sendServerMessage("WTCE animations have been disabled in this area.");
return;
}
if (QDateTime::currentDateTime().toSecsSinceEpoch() - m_last_wtce_time <= 5)
return;
m_last_wtce_time = QDateTime::currentDateTime().toSecsSinceEpoch();
server->broadcast(packet, m_current_area);
updateJudgeLog(area, this, "WT/CE");
}
void AOClient::pktHpBar(AreaData *area, int argc, QStringList argv, AOPacket packet)
{
Q_UNUSED(argc);
Q_UNUSED(packet);
if (m_is_wtce_blocked) {
sendServerMessage("You are blocked from using the judge controls.");
return;
}
int l_newValue = argv.at(1).toInt();
if (argv[0] == "1") {
area->changeHP(AreaData::Side::DEFENCE, l_newValue);
}
else if (argv[0] == "2") {
area->changeHP(AreaData::Side::PROSECUTOR, l_newValue);
}
server->broadcast(AOPacket("HP", {"1", QString::number(area->defHP())}), area->index());
server->broadcast(AOPacket("HP", {"2", QString::number(area->proHP())}), area->index());
updateJudgeLog(area, this, "updated the penalties");
}
void AOClient::pktModCall(AreaData *area, int argc, QStringList argv, AOPacket packet)
{
Q_UNUSED(argc);
Q_UNUSED(argv);
QString l_name = m_ooc_name;
if (m_ooc_name.isEmpty())
l_name = m_current_char;
QString l_areaName = area->name();
QString l_modcallNotice = "!!!MODCALL!!!\nArea: " + l_areaName + "\nCaller: " + l_name + "\n";
if (!packet.getContent()[0].isEmpty())
l_modcallNotice.append("Reason: " + packet.getContent()[0]);
else
l_modcallNotice.append("No reason given.");
const QVector<AOClient *> l_clients = server->getClients();
for (AOClient *l_client : l_clients) {
if (l_client->m_authenticated)
l_client->sendPacket(AOPacket("ZZ", {l_modcallNotice}));
}
emit logModcall((m_current_char + " " + m_showname), m_ipid, m_ooc_name, server->getAreaById(m_current_area)->name());
if (ConfigManager::discordModcallWebhookEnabled()) {
QString l_name = m_ooc_name;
if (m_ooc_name.isEmpty())
l_name = m_current_char;
QString l_areaName = area->name();
emit server->modcallWebhookRequest(l_name, l_areaName, packet.getContent().value(0), server->getAreaBuffer(l_areaName));
}
}
void AOClient::pktAddEvidence(AreaData *area, int argc, QStringList argv, AOPacket packet)
{
Q_UNUSED(argc);
Q_UNUSED(packet);
if (!checkEvidenceAccess(area))
return;
AreaData::Evidence l_evi = {argv[0], argv[1], argv[2]};
area->appendEvidence(l_evi);
sendEvidenceList(area);
}
void AOClient::pktRemoveEvidence(AreaData *area, int argc, QStringList argv, AOPacket packet)
{
Q_UNUSED(argc);
Q_UNUSED(packet);
if (!checkEvidenceAccess(area))
return;
bool is_int = false;
int l_idx = argv[0].toInt(&is_int);
if (is_int && l_idx < area->evidence().size() && l_idx >= 0) {
area->deleteEvidence(l_idx);
}
sendEvidenceList(area);
}
void AOClient::pktEditEvidence(AreaData *area, int argc, QStringList argv, AOPacket packet)
{
Q_UNUSED(argc);
Q_UNUSED(packet);
if (!checkEvidenceAccess(area))
return;
bool is_int = false;
int l_idx = argv[0].toInt(&is_int);
AreaData::Evidence l_evi = {argv[1], argv[2], argv[3]};
if (is_int && l_idx < area->evidence().size() && l_idx >= 0) {
area->replaceEvidence(l_idx, l_evi);
}
sendEvidenceList(area);
}
void AOClient::pktSetCase(AreaData *area, int argc, QStringList argv, AOPacket packet)
{
Q_UNUSED(area);
Q_UNUSED(argc);
Q_UNUSED(packet);
QList<bool> l_prefs_list;
for (int i = 2; i <= 6; i++) {
bool is_int = false;
bool pref = argv[i].toInt(&is_int);
if (!is_int)
return;
l_prefs_list.append(pref);
}
m_casing_preferences = l_prefs_list;
}
void AOClient::pktAnnounceCase(AreaData *area, int argc, QStringList argv, AOPacket packet)
{
Q_UNUSED(area);
Q_UNUSED(argc);
Q_UNUSED(packet);
QString l_case_title = argv[0];
QStringList l_needed_roles;
QList<bool> l_needs_list;
for (int i = 1; i <= 5; i++) {
bool is_int = false;
bool need = argv[i].toInt(&is_int);
if (!is_int)
return;
l_needs_list.append(need);
}
QStringList l_roles = {"defense attorney", "prosecutor", "judge", "jurors", "stenographer"};
for (int i = 0; i < 5; i++) {
if (l_needs_list[i])
l_needed_roles.append(l_roles[i]);
}
if (l_needed_roles.isEmpty())
return;
QString l_message = "=== Case Announcement ===\r\n" + (m_ooc_name == "" ? m_current_char : m_ooc_name) + " needs " + l_needed_roles.join(", ") + " for " + (l_case_title == "" ? "a case" : l_case_title) + "!";
QList<AOClient *> l_clients_to_alert;
// here lies morton, RIP
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QSet<bool> l_needs_set(l_needs_list.begin(), l_needs_list.end());
#else
QSet<bool> l_needs_set = l_needs_list.toSet();
#endif
const QVector<AOClient *> l_clients = server->getClients();
for (AOClient *l_client : l_clients) {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QSet<bool> l_matches(l_client->m_casing_preferences.begin(), l_client->m_casing_preferences.end());
l_matches.intersect(l_needs_set);
#else
QSet<bool> l_matches = l_client->m_casing_preferences.toSet().intersect(l_needs_set);
#endif
if (!l_matches.isEmpty() && !l_clients_to_alert.contains(l_client))
l_clients_to_alert.append(l_client);
}
for (AOClient *l_client : l_clients_to_alert) {
l_client->sendPacket(AOPacket("CASEA", {l_message, argv[1], argv[2], argv[3], argv[4], argv[5], "1"}));
// you may be thinking, "hey wait a minute the network protocol documentation doesn't mention that last argument!"
// if you are in fact thinking that, you are correct! it is not in the documentation!
// however for some inscrutable reason Attorney Online 2 will outright reject a CASEA packet that does not have
// at least 7 arguments despite only using the first 6. Cera, i kneel. you have truly broken me.
}
}
void AOClient::sendEvidenceList(AreaData *area)
{ {
const QVector<AOClient *> l_clients = server->getClients(); const QVector<AOClient *> l_clients = server->getClients();
for (AOClient *l_client : l_clients) { for (AOClient *l_client : l_clients) {
@ -624,388 +57,7 @@ void AOClient::updateEvidenceList(AreaData *area)
l_evidence_list.append(l_evidence_format.arg(evidence.name, evidence.description, evidence.image)); l_evidence_list.append(l_evidence_format.arg(evidence.name, evidence.description, evidence.image));
} }
sendPacket(AOPacket("LE", l_evidence_list)); sendPacket(PacketFactory::createPacket("LE", l_evidence_list));
}
AOPacket AOClient::validateIcPacket(AOPacket packet)
{
// Welcome to the super cursed server-side IC chat validation hell
// I wanted to use enums or #defines here to make the
// indicies of the args arrays more readable. But,
// in typical AO fasion, the indicies for the incoming
// and outgoing packets are different. Just RTFM.
// This packet can be sent with a minimum required args of 15.
// 2.6+ extensions raise this to 19, and 2.8 further raises this to 26.
AOPacket l_invalid("INVALID", {});
QStringList l_args;
if (isSpectator() || m_current_char.isEmpty() || !m_joined)
// Spectators cannot use IC
return l_invalid;
AreaData *area = server->getAreaById(m_current_area);
if (area->lockStatus() == AreaData::LockStatus::SPECTATABLE && !area->invited().contains(m_id) && !checkPermission(ACLRole::BYPASS_LOCKS))
// Non-invited players cannot speak in spectatable areas
return l_invalid;
QList<QVariant> l_incoming_args;
for (const QString &l_arg : packet.getContent()) {
l_incoming_args.append(QVariant(l_arg));
}
// desk modifier
QStringList allowed_desk_mods;
allowed_desk_mods << "chat"
<< "0"
<< "1"
<< "2"
<< "3"
<< "4"
<< "5";
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
// instead of overwriting correct netcode behaviour on the serverside.
if (l_incoming_deskmod == "chat") {
l_args.append("1");
}
else {
l_args.append(l_incoming_args[0].toString());
}
}
else
return l_invalid;
// preanim
l_args.append(l_incoming_args[1].toString());
// char name
if (m_current_char.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()) {
if (!server->getCharacters().contains(l_incoming_args[2].toString(), Qt::CaseInsensitive))
return l_invalid;
}
qDebug() << "INI swap detected from " << getIpid();
}
m_current_iniswap = l_incoming_args[2].toString();
l_args.append(l_incoming_args[2].toString());
// emote
m_emote = l_incoming_args[3].toString();
if (m_first_person)
m_emote = "";
l_args.append(m_emote);
// message text
if (l_incoming_args[4].toString().size() > ConfigManager::maxCharacters())
return l_invalid;
QString l_incoming_msg = dezalgo(l_incoming_args[4].toString().trimmed());
if (!area->lastICMessage().isEmpty() && l_incoming_msg == area->lastICMessage()[4] && l_incoming_msg != "")
return l_invalid;
if (l_incoming_msg == "" && area->blankpostingAllowed() == false) {
sendServerMessage("Blankposting has been forbidden in this area.");
return l_invalid;
}
if (m_is_gimped) {
QString l_gimp_message = ConfigManager::gimpList().at((genRand(1, ConfigManager::gimpList().size() - 1)));
l_incoming_msg = l_gimp_message;
}
if (m_is_shaken) {
QStringList l_parts = l_incoming_msg.split(" ");
std::random_shuffle(l_parts.begin(), l_parts.end());
l_incoming_msg = l_parts.join(" ");
}
if (m_is_disemvoweled) {
QString l_disemvoweled_message = l_incoming_msg.remove(QRegExp("[AEIOUaeiou]"));
l_incoming_msg = l_disemvoweled_message;
}
m_last_message = l_incoming_msg;
l_args.append(l_incoming_msg);
// side
// this is validated clientside so w/e
l_args.append(l_incoming_args[5].toString());
if (m_pos != l_incoming_args[5].toString()) {
m_pos = l_incoming_args[5].toString();
updateEvidenceList(server->getAreaById(m_current_area));
}
// sfx name
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.
// If this value is a 4, it will crash the client. Why? Who knows, but it does.
// Now here is the kicker: in certain versions, the client would incorrectly send a 4 here
// For a long time, by configuring the client to do a zoom with a preanim, it would send 4
// 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[7].toInt();
if (emote_mod == 4)
emote_mod = 6;
if (emote_mod != 0 && emote_mod != 1 && emote_mod != 2 && emote_mod != 5 && emote_mod != 6)
return l_invalid;
l_args.append(QString::number(emote_mod));
// char id
if (l_incoming_args[8].toInt() != m_char_id)
return l_invalid;
l_args.append(l_incoming_args[8].toString());
// sfx delay
l_args.append(l_incoming_args[9].toString());
// objection modifier
if (area->isShoutAllowed()) {
if (l_incoming_args[10].toString().contains("4")) {
// custom shout includes text metadata
l_args.append(l_incoming_args[10].toString());
}
else {
int l_obj_mod = l_incoming_args[10].toInt();
if ((l_obj_mod < 0) || (l_obj_mod > 4)) {
return l_invalid;
}
l_args.append(QString::number(l_obj_mod));
}
}
else {
if (l_incoming_args[10].toString() != "0") {
sendServerMessage("Shouts have been disabled in this area.");
}
l_args.append("0");
}
// evidence
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[12].toInt();
if (l_flip != 0 && l_flip != 1)
return l_invalid;
m_flipping = QString::number(l_flip);
l_args.append(m_flipping);
// realization
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[14].toInt();
if (text_color < 0 || text_color > 11)
return l_invalid;
l_args.append(QString::number(text_color));
// 2.6 packet extensions
if (l_incoming_args.length() >= 19) {
// showname
QString l_incoming_showname = dezalgo(l_incoming_args[15].toString().trimmed());
if (!(l_incoming_showname == m_current_char || l_incoming_showname.isEmpty()) && !area->shownameAllowed()) {
sendServerMessage("Shownames are not allowed in this area!");
return l_invalid;
}
if (l_incoming_showname.length() > 30) {
sendServerMessage("Your showname is too long! Please limit it to under 30 characters");
return l_invalid;
}
// if the raw input is not empty but the trimmed input is, use a single space
if (l_incoming_showname.isEmpty() && !l_incoming_args[15].toString().isEmpty())
l_incoming_showname = " ";
l_args.append(l_incoming_showname);
m_showname = l_incoming_showname;
// 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[16].toString().split("^");
m_pairing_with = l_pair_data[0].toInt();
QString l_front_back = "";
if (l_pair_data.length() > 1)
l_front_back = "^" + l_pair_data[1];
int l_other_charid = m_pairing_with;
bool l_pairing = false;
QString l_other_name = "0";
QString l_other_emote = "0";
QString l_other_offset = "0";
QString l_other_flip = "0";
for (int l_client_id : area->joinedIDs()) {
AOClient *l_client = server->getClientByID(l_client_id);
if (l_client->m_pairing_with == m_char_id && l_other_charid != m_char_id && l_client->m_char_id == m_pairing_with && l_client->m_pos == m_pos) {
l_other_name = l_client->m_current_iniswap;
l_other_emote = l_client->m_emote;
l_other_offset = l_client->m_offset;
l_other_flip = l_client->m_flipping;
l_pairing = true;
}
}
if (!l_pairing) {
l_other_charid = -1;
l_front_back = "";
}
l_args.append(QString::number(l_other_charid) + l_front_back);
l_args.append(l_other_name);
l_args.append(l_other_emote);
// self offset
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 ((m_version.release == 2) && (m_version.major == 6 || m_version.major == 7 || m_version.major == 8)) {
QString l_x_offset = 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(m_offset);
l_args.append(l_other_offset);
}
l_args.append(l_other_flip);
// immediate text processing
int l_immediate = l_incoming_args[18].toInt();
if (area->forceImmediate()) {
if (l_args[7] == "1" || l_args[7] == "2") {
l_args[7] = "0";
l_immediate = 1;
}
else if (l_args[7] == "6") {
l_args[7] = "5";
l_immediate = 1;
}
}
if (l_immediate != 1 && l_immediate != 0)
return l_invalid;
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[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[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[21].toString());
// frames realization
l_args.append(l_incoming_args[22].toString());
// frames sfx
l_args.append(l_incoming_args[23].toString());
// additive
int l_additive = l_incoming_args[24].toInt();
if (l_additive != 0 && l_additive != 1)
return l_invalid;
else if (area->lastICMessage().isEmpty()) {
l_additive = 0;
}
else if (!(m_char_id == area->lastICMessage()[8].toInt())) {
l_additive = 0;
}
else if (l_additive == 1) {
l_args[4].insert(0, " ");
}
l_args.append(QString::number(l_additive));
// effect
l_args.append(l_incoming_args[25].toString());
}
// Testimony playback
if (area->testimonyRecording() == AreaData::TestimonyRecording::RECORDING || area->testimonyRecording() == AreaData::TestimonyRecording::ADD) {
if (l_args[5] != "wit")
return AOPacket("MS", l_args);
if (area->statement() == -1) {
l_args[4] = "~~\\n-- " + l_args[4] + " --";
l_args[14] = "3";
server->broadcast(AOPacket("RT", {"testimony1"}), m_current_area);
}
addStatement(l_args);
}
else if (area->testimonyRecording() == AreaData::TestimonyRecording::UPDATE) {
l_args = updateStatement(l_args);
}
else if (area->testimonyRecording() == AreaData::TestimonyRecording::PLAYBACK) {
AreaData::TestimonyProgress l_progress;
if (l_args[4] == ">") {
m_pos = "wit";
auto l_statement = area->jumpToStatement(area->statement() + 1);
l_args = l_statement.first;
l_progress = l_statement.second;
if (l_progress == AreaData::TestimonyProgress::LOOPED) {
sendServerMessageArea("Last statement reached. Looping to first statement.");
}
}
if (l_args[4] == "<") {
m_pos = "wit";
auto l_statement = area->jumpToStatement(area->statement() - 1);
l_args = l_statement.first;
l_progress = l_statement.second;
if (l_progress == AreaData::TestimonyProgress::STAYED_AT_FIRST) {
sendServerMessage("First statement reached.");
}
}
QString l_decoded_message = decodeMessage(l_args[4]); // Get rid of that pesky encoding first.
QRegularExpression jump("(?<arrow>>)(?<int>[0,1,2,3,4,5,6,7,8,9]+)");
QRegularExpressionMatch match = jump.match(l_decoded_message);
if (match.hasMatch()) {
m_pos = "wit";
auto l_statement = area->jumpToStatement(match.captured("int").toInt());
l_args = l_statement.first;
l_progress = l_statement.second;
switch (l_progress) {
case AreaData::TestimonyProgress::LOOPED:
{
sendServerMessageArea("Last statement reached. Looping to first statement.");
break;
}
case AreaData::TestimonyProgress::STAYED_AT_FIRST:
{
sendServerMessage("First statement reached.");
Q_FALLTHROUGH();
}
case AreaData::TestimonyProgress::OK:
default:
// No need to handle.
break;
}
}
}
return AOPacket("MS", l_args);
} }
QString AOClient::dezalgo(QString p_text) QString AOClient::dezalgo(QString p_text)

View File

@ -27,8 +27,8 @@
#include "include/discord.h" #include "include/discord.h"
#include "include/logger/u_logger.h" #include "include/logger/u_logger.h"
#include "include/music_manager.h" #include "include/music_manager.h"
#include "include/network/aopacket.h"
#include "include/network/network_socket.h" #include "include/network/network_socket.h"
#include "include/packet/packet_factory.h"
Server::Server(int p_port, int p_ws_port, QObject *parent) : Server::Server(int p_port, int p_ws_port, QObject *parent) :
QObject(parent), QObject(parent),
@ -56,6 +56,8 @@ Server::Server(int p_port, int p_ws_port, QObject *parent) :
logger = new ULogger(this); logger = new ULogger(this);
connect(this, &Server::logConnectionAttempt, logger, &ULogger::logConnectionAttempt); connect(this, &Server::logConnectionAttempt, logger, &ULogger::logConnectionAttempt);
AOPacket::registerPackets();
} }
void Server::start() void Server::start()
@ -115,7 +117,7 @@ void Server::start()
ConfigManager::musiclist(); ConfigManager::musiclist();
music_manager = new MusicManager(ConfigManager::ordered_songs(), ConfigManager::cdnList(), ConfigManager::musiclist(), this); music_manager = new MusicManager(ConfigManager::ordered_songs(), ConfigManager::cdnList(), ConfigManager::musiclist(), this);
connect(music_manager, &MusicManager::sendFMPacket, this, &Server::unicast); connect(music_manager, &MusicManager::sendFMPacket, this, &Server::unicast);
connect(music_manager, &MusicManager::sendAreaFMPacket, this, QOverload<AOPacket, int>::of(&Server::broadcast)); connect(music_manager, &MusicManager::sendAreaFMPacket, this, QOverload<AOPacket *, int>::of(&Server::broadcast));
// Get musiclist from config file // Get musiclist from config file
m_music_list = music_manager->rootMusiclist(); m_music_list = music_manager->rootMusiclist();
@ -127,7 +129,7 @@ void Server::start()
QString area_name = raw_area_names[i]; QString area_name = raw_area_names[i];
AreaData *l_area = new AreaData(area_name, i, music_manager); AreaData *l_area = new AreaData(area_name, i, music_manager);
m_areas.insert(i, l_area); m_areas.insert(i, l_area);
connect(l_area, &AreaData::sendAreaPacket, this, QOverload<AOPacket, int>::of(&Server::broadcast)); connect(l_area, &AreaData::sendAreaPacket, this, QOverload<AOPacket *, int>::of(&Server::broadcast));
connect(l_area, &AreaData::sendAreaPacketClient, this, &Server::unicast); connect(l_area, &AreaData::sendAreaPacketClient, this, &Server::unicast);
connect(l_area, &AreaData::userJoinedArea, music_manager, &MusicManager::userJoinedArea); connect(l_area, &AreaData::userJoinedArea, music_manager, &MusicManager::userJoinedArea);
music_manager->registerArea(i); music_manager->registerArea(i);
@ -162,8 +164,8 @@ void Server::clientConnected()
// Too many players. Reject connection! // Too many players. Reject connection!
// This also enforces the maximum playercount. // This also enforces the maximum playercount.
if (m_available_ids.empty()) { if (m_available_ids.empty()) {
AOPacket disconnect_reason("BD", {"Maximum playercount has been reached."}); AOPacket *disconnect_reason = PacketFactory::createPacket("BD", {"Maximum playercount has been reached."});
socket->write(disconnect_reason.toUtf8()); socket->write(disconnect_reason->toUtf8());
socket->flush(); socket->flush();
socket->close(); socket->close();
socket->deleteLater(); socket->deleteLater();
@ -192,8 +194,8 @@ void Server::clientConnected()
if (is_banned) { if (is_banned) {
QString reason = ban.second; QString reason = ban.second;
AOPacket ban_reason("BD", {reason}); AOPacket *ban_reason = PacketFactory::createPacket("BD", {reason});
socket->write(ban_reason.toUtf8()); socket->write(ban_reason->toUtf8());
} }
if (is_banned || is_at_multiclient_limit) { if (is_banned || is_at_multiclient_limit) {
socket->flush(); socket->flush();
@ -210,8 +212,8 @@ void Server::clientConnected()
if (isIPBanned(l_remote_ip)) { if (isIPBanned(l_remote_ip)) {
QString l_reason = "Your IP has been banned by a moderator."; QString l_reason = "Your IP has been banned by a moderator.";
AOPacket l_ban_reason("BD", {l_reason}); AOPacket *l_ban_reason = PacketFactory::createPacket("BD", {l_reason});
socket->write(l_ban_reason.toUtf8()); socket->write(l_ban_reason->toUtf8());
client->deleteLater(); client->deleteLater();
socket->close(); socket->close();
markIDFree(user_id); markIDFree(user_id);
@ -228,9 +230,10 @@ void Server::clientConnected()
}); });
connect(l_socket, &NetworkSocket::handlePacket, client, &AOClient::handlePacket); connect(l_socket, &NetworkSocket::handlePacket, client, &AOClient::handlePacket);
AOPacket decryptor("decryptor", {"NOENCRYPT"}); // This is the infamous workaround for // This is the infamous workaround for
// tsuserver4. It should disable fantacrypt // tsuserver4. It should disable fantacrypt
// completely in any client 2.4.3 or newer // completely in any client 2.4.3 or newer
AOPacket *decryptor = PacketFactory::createPacket("decryptor", {"NOENCRYPT"});
client->sendPacket(decryptor); client->sendPacket(decryptor);
hookupAOClient(client); hookupAOClient(client);
#ifdef NET_DEBUG #ifdef NET_DEBUG
@ -246,7 +249,7 @@ void Server::ws_clientConnected()
// Too many players. Reject connection! // Too many players. Reject connection!
// This also enforces the maximum playercount. // This also enforces the maximum playercount.
if (m_available_ids.empty()) { if (m_available_ids.empty()) {
AOPacket disconnect_reason("BD", {"Maximum playercount has been reached."}); AOPacket *disconnect_reason = PacketFactory::createPacket("BD", {"Maximum playercount has been reached."});
l_socket->write(disconnect_reason); l_socket->write(disconnect_reason);
l_socket->close(); l_socket->close();
l_socket->deleteLater(); l_socket->deleteLater();
@ -272,8 +275,8 @@ void Server::ws_clientConnected()
if (is_banned) { if (is_banned) {
QString reason = ban.second; QString reason = ban.second;
AOPacket ban_reason("BD", {reason}); AOPacket *ban_reason = PacketFactory::createPacket("BD", {reason});
socket->sendTextMessage(ban_reason.toUtf8()); socket->sendTextMessage(ban_reason->toUtf8());
} }
if (is_banned || is_at_multiclient_limit) { if (is_banned || is_at_multiclient_limit) {
client->deleteLater(); client->deleteLater();
@ -289,7 +292,7 @@ void Server::ws_clientConnected()
if (isIPBanned(l_remote_ip)) { if (isIPBanned(l_remote_ip)) {
QString l_reason = "Your IP has been banned by a moderator."; QString l_reason = "Your IP has been banned by a moderator.";
AOPacket l_ban_reason("BD", {l_reason}); AOPacket *l_ban_reason = PacketFactory::createPacket("BD", {l_reason});
l_socket->write(l_ban_reason); l_socket->write(l_ban_reason);
client->deleteLater(); client->deleteLater();
l_socket->close(QWebSocketProtocol::CloseCodeNormal); l_socket->close(QWebSocketProtocol::CloseCodeNormal);
@ -307,9 +310,10 @@ void Server::ws_clientConnected()
}); });
connect(l_socket, &NetworkSocket::handlePacket, client, &AOClient::handlePacket); connect(l_socket, &NetworkSocket::handlePacket, client, &AOClient::handlePacket);
AOPacket decryptor("decryptor", {"NOENCRYPT"}); // This is the infamous workaround for // This is the infamous workaround for
// tsuserver4. It should disable fantacrypt // tsuserver4. It should disable fantacrypt
// completely in any client 2.4.3 or newer // completely in any client 2.4.3 or newer
AOPacket *decryptor = PacketFactory::createPacket("decryptor", {"NOENCRYPT"});
client->sendPacket(decryptor); client->sendPacket(decryptor);
hookupAOClient(client); hookupAOClient(client);
} }
@ -323,7 +327,7 @@ void Server::updateCharsTaken(AreaData *area)
: QStringLiteral("0")); : QStringLiteral("0"));
} }
AOPacket response_cc("CharsCheck", chars_taken); AOPacket *response_cc = PacketFactory::createPacket("CharsCheck", chars_taken);
for (AOClient *l_client : qAsConst(m_clients)) { for (AOClient *l_client : qAsConst(m_clients)) {
if (l_client->m_current_area == area->index()) { if (l_client->m_current_area == area->index()) {
@ -331,7 +335,7 @@ void Server::updateCharsTaken(AreaData *area)
l_client->sendPacket(response_cc); l_client->sendPacket(response_cc);
else { else {
QStringList chars_taken_cursed = getCursedCharsTaken(l_client, chars_taken); QStringList chars_taken_cursed = getCursedCharsTaken(l_client, chars_taken);
AOPacket response_cc_cursed("CharsCheck", chars_taken_cursed); AOPacket *response_cc_cursed = PacketFactory::createPacket("CharsCheck", chars_taken_cursed);
l_client->sendPacket(response_cc_cursed); l_client->sendPacket(response_cc_cursed);
} }
} }
@ -384,7 +388,7 @@ void Server::reloadSettings()
command_extension_collection->loadFile("config/command_extensions.ini"); command_extension_collection->loadFile("config/command_extensions.ini");
} }
void Server::broadcast(AOPacket packet, int area_index) void Server::broadcast(AOPacket *packet, int area_index)
{ {
QVector<int> l_client_ids = m_areas.value(area_index)->joinedIDs(); QVector<int> l_client_ids = m_areas.value(area_index)->joinedIDs();
for (const int l_client_id : qAsConst(l_client_ids)) { for (const int l_client_id : qAsConst(l_client_ids)) {
@ -392,14 +396,14 @@ void Server::broadcast(AOPacket packet, int area_index)
} }
} }
void Server::broadcast(AOPacket packet) void Server::broadcast(AOPacket *packet)
{ {
for (AOClient *l_client : qAsConst(m_clients)) { for (AOClient *l_client : qAsConst(m_clients)) {
l_client->sendPacket(packet); l_client->sendPacket(packet);
} }
} }
void Server::broadcast(AOPacket packet, TARGET_TYPE target) void Server::broadcast(AOPacket *packet, TARGET_TYPE target)
{ {
for (AOClient *l_client : qAsConst(m_clients)) { for (AOClient *l_client : qAsConst(m_clients)) {
switch (target) { switch (target) {
@ -419,7 +423,7 @@ void Server::broadcast(AOPacket packet, TARGET_TYPE target)
} }
} }
void Server::broadcast(AOPacket packet, AOPacket other_packet, TARGET_TYPE target) void Server::broadcast(AOPacket *packet, AOPacket *other_packet, TARGET_TYPE target)
{ {
switch (target) { switch (target) {
case TARGET_TYPE::AUTHENTICATED: case TARGET_TYPE::AUTHENTICATED:
@ -437,7 +441,7 @@ void Server::broadcast(AOPacket packet, AOPacket other_packet, TARGET_TYPE targe
} }
} }
void Server::unicast(AOPacket f_packet, int f_client_id) void Server::unicast(AOPacket *f_packet, int f_client_id)
{ {
AOClient *l_client = getClientByID(f_client_id); AOClient *l_client = getClientByID(f_client_id);
if (l_client != nullptr) { // This should never happen, but safety first. if (l_client != nullptr) { // This should never happen, but safety first.

View File

@ -5,6 +5,7 @@ SUBDIRS += \
unittest_music_manager \ unittest_music_manager \
unittest_acl_roles_handler \ unittest_acl_roles_handler \
unittest_command_extension \ unittest_command_extension \
unittest_aopacket \
unittest_config_manager \ unittest_config_manager \
unittest_crypto unittest_crypto \
unittest_aopacket \
unittest_akashi_utils

View File

@ -2,6 +2,8 @@ QT += network websockets core sql testlib
CONFIG += qt console warn_on depend_includepath testcase no_testcase_installs CONFIG += qt console warn_on depend_includepath testcase no_testcase_installs
CONFIG -= app_bundle CONFIG -= app_bundle
unix: CONFIG += c++1z
win32: CONFIG += c++2a
coverage { coverage {
LIBS += -lgcov LIBS += -lgcov

View File

@ -0,0 +1,111 @@
#include "include/akashiutils.h"
#include <QTest>
namespace tests {
namespace unittests {
class tst_AkashiUtils : public QObject
{
Q_OBJECT
private slots:
void integer_data();
void integer();
void floating_data();
void floating();
void boolean_data();
void boolean();
void doublep_data();
void doublep();
};
void tst_AkashiUtils::integer_data()
{
QTest::addColumn<QString>("content");
QTest::addColumn<bool>("expected_result");
QTest::addRow("Integer (good)") << "one"
<< false;
QTest::addRow("Integer (bad)") << "1"
<< true;
}
void tst_AkashiUtils::integer()
{
QFETCH(QString, content);
QFETCH(bool, expected_result);
bool result = AkashiUtils::checkArgType<int>(content);
QCOMPARE(result, expected_result);
}
void tst_AkashiUtils::floating_data()
{
QTest::addColumn<QString>("content");
QTest::addColumn<bool>("expected_result");
QTest::addRow("Float (good)") << "test"
<< false;
QTest::addRow("Float (bad)") << "3.141"
<< true;
}
void tst_AkashiUtils::floating()
{
QFETCH(QString, content);
QFETCH(bool, expected_result);
bool result = AkashiUtils::checkArgType<float>(content);
QCOMPARE(result, expected_result);
}
void tst_AkashiUtils::boolean_data()
{
QTest::addColumn<QString>("content");
QTest::addColumn<bool>("expected_result");
QTest::addRow("Boolean (random string)") << "test"
<< true;
QTest::addRow("Boolean (true/false string)") << "true"
<< true;
}
void tst_AkashiUtils::boolean()
{
QFETCH(QString, content);
QFETCH(bool, expected_result);
bool result = AkashiUtils::checkArgType<bool>(content);
QCOMPARE(result, expected_result);
}
void tst_AkashiUtils::doublep_data()
{
QTest::addColumn<QString>("content");
QTest::addColumn<bool>("expected_result");
QTest::addRow("Double (good)") << "test"
<< false;
QTest::addRow("Double (bad)") << "3.141592653589793"
<< true;
}
void tst_AkashiUtils::doublep()
{
QFETCH(QString, content);
QFETCH(bool, expected_result);
bool result = AkashiUtils::checkArgType<double>(content);
QCOMPARE(result, expected_result);
}
}
};
QTEST_APPLESS_MAIN(tests::unittests::tst_AkashiUtils)
#include "tst_unittest_akashi_utils.moc"

View File

@ -0,0 +1,5 @@
QT -= gui
include(../tests_common.pri)
SOURCES += tst_unittest_akashi_utils.cpp

View File

@ -2,6 +2,7 @@
#include <QTest> #include <QTest>
#include "include/network/aopacket.h" #include "include/network/aopacket.h"
#include "include/packet/packet_factory.h"
namespace tests { namespace tests {
namespace unittests { namespace unittests {
@ -14,9 +15,12 @@ class Packet : public QObject
Q_OBJECT Q_OBJECT
public: public:
AOPacket m_packet = AOPacket{"", {}};
private slots: private slots:
/**
* @brief Initializes all tests
*/
void init();
/** /**
* @brief Creates a packet from a defined header and content. * @brief Creates a packet from a defined header and content.
*/ */
@ -33,11 +37,16 @@ class Packet : public QObject
void createPacketFromString(); void createPacketFromString();
}; };
void Packet::init()
{
AOPacket::registerPackets();
}
void Packet::createPacket() void Packet::createPacket()
{ {
AOPacket packet = AOPacket("HI", {"HDID"}); AOPacket *packet = PacketFactory::createPacket("HI", {"HDID"});
QCOMPARE(packet.getHeader(), "HI"); QCOMPARE(packet->getPacketInfo().header, "HI");
QCOMPARE(packet.getContent(), {"HDID"}); QCOMPARE(packet->getContent(), {"HDID"});
} }
void Packet::createPacketFromString_data() void Packet::createPacketFromString_data()
@ -64,7 +73,11 @@ void Packet::createPacketFromString_data()
QTest::newRow("Unescaped characters") << "MC#20% Cooler#" QTest::newRow("Unescaped characters") << "MC#20% Cooler#"
<< "Unknown" << "Unknown"
<< QStringList{"Unknown"}; // This should be impossible. << QStringList{"Unknown"};
QTest::newRow("Empty packet") << ""
<< "Unknown"
<< QStringList{"Unknown"};
} }
void Packet::createPacketFromString() void Packet::createPacketFromString()
@ -73,9 +86,9 @@ void Packet::createPacketFromString()
QFETCH(QString, expected_header); QFETCH(QString, expected_header);
QFETCH(QStringList, expected_content); QFETCH(QStringList, expected_content);
AOPacket packet = AOPacket(incoming_packet); AOPacket *packet = PacketFactory::createPacket(incoming_packet);
QCOMPARE(packet.getHeader(), expected_header); QCOMPARE(packet->getPacketInfo().header, expected_header);
QCOMPARE(packet.getContent(), expected_content); QCOMPARE(packet->getContent(), expected_content);
} }
} }