diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 3ba2b3a..5a00e63 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -145,8 +145,8 @@ jobs:
- name: Deploy OpenSSL
run: |
- curl https://mirror.firedaemon.com/OpenSSL/openssl-1.1.1o.zip --output openssl-1.1.1o.zip
- tar -xf 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.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\libssl-1_1-x64.dll .\bin\libssl-1_1-x64.dll
@@ -154,4 +154,4 @@ jobs:
uses: actions/upload-artifact@v2
with:
name: akashi-windows
- path: bin\
\ No newline at end of file
+ path: bin\
diff --git a/akashi/akashi.pro b/akashi/akashi.pro
index c7f2246..1d08e7b 100644
--- a/akashi/akashi.pro
+++ b/akashi/akashi.pro
@@ -2,7 +2,8 @@ QT += network websockets core sql
QT -= gui
TEMPLATE = app
-CONFIG += c++11 console
+unix:CONFIG += c++1z console
+win32: CONFIG+=c++2a console
coverage {
LIBS += -lgcov
diff --git a/core/core.pro b/core/core.pro
index 0052b3a..c3bc115 100644
--- a/core/core.pro
+++ b/core/core.pro
@@ -6,7 +6,8 @@ TEMPLATE = lib
# Apparently, Windows needs a static config to make a dynamic library?
# Look, I dunno.
# 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 {
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
# Enable this to print network messages to the console
-#DEFINES += NET_DEBUG
+DEFINES += NET_DEBUG
SOURCES += \
src/acl_roles_handler.cpp \
@@ -55,11 +56,34 @@ SOURCES += \
src/logger/u_logger.cpp \
src/logger/writer_modcall.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 \
include/acl_roles_handler.h \
include/akashidefs.h \
+ include/akashiutils.h \
include/network/aopacket.h \
include/network/network_socket.h \
include/area_data.h \
@@ -74,4 +98,27 @@ HEADERS += include/aoclient.h \
include/logger/u_logger.h \
include/logger/writer_modcall.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
diff --git a/core/include/akashiutils.h b/core/include/akashiutils.h
new file mode 100644
index 0000000..fa35939
--- /dev/null
+++ b/core/include/akashiutils.h
@@ -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 . //
+//////////////////////////////////////////////////////////////////////////////////////
+#ifndef AKASHI_UTILS_H
+#define AKASHI_UTILS_H
+
+#include
+#include
+
+class AkashiUtils
+{
+ private:
+ AkashiUtils(){};
+
+ public:
+ template
+ static inline bool checkArgType(QString arg)
+ {
+ QVariant qvar = arg;
+ if (!qvar.canConvert())
+ return false;
+
+ if (std::is_same()) {
+ bool ok;
+ qvar.toInt(&ok);
+ return ok;
+ }
+ else if (std::is_same()) {
+ bool ok;
+ float f = qvar.toFloat(&ok);
+ return ok && !isnan((float)f);
+ }
+ else if (std::is_same()) {
+ bool ok;
+ double d = qvar.toDouble(&ok);
+ return ok && !isnan((double)d);
+ }
+
+ return true;
+ };
+};
+
+#endif // AKASHI_UTILS_H
diff --git a/core/include/aoclient.h b/core/include/aoclient.h
index 242838b..88cf447 100644
--- a/core/include/aoclient.h
+++ b/core/include/aoclient.h
@@ -34,6 +34,8 @@ class AreaData;
class DBManager;
class MusicManager;
class Server;
+class NetworkSocket;
+class AOPacket;
/**
* @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;
+ /**
+ * @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.
*
@@ -341,109 +374,6 @@ class AOClient : public QObject
*/
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.
*
@@ -479,114 +409,12 @@ class AOClient : public QObject
*/
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.
*
* @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.
@@ -595,15 +423,6 @@ class AOClient : public QObject
*/
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.
*
@@ -624,7 +443,101 @@ class AOClient : public QObject
*/
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
@@ -641,15 +554,6 @@ class AOClient : public QObject
*/
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.
*
@@ -687,13 +591,85 @@ class AOClient : public QObject
///@}
- /// Describes a packet's interpretation details.
- struct PacketInfo
- {
- ACLRole::Permission acl_permission; //!< The permissions necessary for the packet.
- int minArgs; //!< The minimum arguments needed for the packet to be interpreted correctly / make sense.
- void (AOClient::*action)(AreaData *, int, QStringList, AOPacket);
- };
+ /**
+ * @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 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
@@ -706,38 +682,6 @@ class AOClient : public QObject
* @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 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
*/
@@ -2018,18 +1962,6 @@ class AOClient : public QObject
*/
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.
*
@@ -2066,15 +1998,6 @@ class AOClient : public QObject
long long parseTime(QString input);
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.
*
@@ -2083,13 +2006,6 @@ class AOClient : public QObject
*/
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.
* @return IC-Message stored in the QVector.
@@ -2150,71 +2066,11 @@ class AOClient : public QObject
*/
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.
*/
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:
/**
diff --git a/core/include/area_data.h b/core/include/area_data.h
index 902ee8f..87f10d7 100644
--- a/core/include/area_data.h
+++ b/core/include/area_data.h
@@ -31,6 +31,7 @@
class ConfigManager;
class Logger;
class MusicManager;
+class AOPacket;
/**
* @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.
*/
- 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.
@@ -936,7 +937,7 @@ class AreaData : public QObject
*
* @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.
diff --git a/core/include/music_manager.h b/core/include/music_manager.h
index a1b21d9..44379c4 100644
--- a/core/include/music_manager.h
+++ b/core/include/music_manager.h
@@ -168,7 +168,7 @@ class MusicManager : public QObject
*
* @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.
@@ -177,7 +177,7 @@ class MusicManager : public QObject
*
* @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:
/**
diff --git a/core/include/network/aopacket.h b/core/include/network/aopacket.h
index 3d0404e..7027e58 100644
--- a/core/include/network/aopacket.h
+++ b/core/include/network/aopacket.h
@@ -23,6 +23,12 @@
#include
#include
+#include "include/aoclient.h"
+#include "include/area_data.h"
+#include "include/packet/packet_info.h"
+
+class AOClient;
+
/**
* @brief An Attorney Online 2 compatible packet.
*
@@ -32,20 +38,7 @@
class AOPacket
{
public:
- /**
- * @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);
+ AOPacket(QStringList p_contents);
/**
* @brief Destructor for the AOPacket
@@ -59,13 +52,6 @@ class AOPacket
*/
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.
*
@@ -125,15 +111,13 @@ class AOPacket
*/
bool isPacketEscaped();
- private:
- /**
- * @brief The header of the packet.
- *
- * @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;
+ virtual PacketInfo getPacketInfo() const = 0;
+ virtual void handlePacket(AreaData *area, AOClient &client) const = 0;
+ virtual bool validatePacket() const = 0;
+ static void registerPackets();
+
+ protected:
/**
* @brief The contents of the packet.
*/
diff --git a/core/include/network/network_socket.h b/core/include/network/network_socket.h
index 1b7213f..f3c463c 100644
--- a/core/include/network/network_socket.h
+++ b/core/include/network/network_socket.h
@@ -25,6 +25,8 @@
#include "include/network/aopacket.h"
+class AOPacket;
+
class NetworkSocket : public QObject
{
Q_OBJECT
@@ -73,7 +75,7 @@ class NetworkSocket : public QObject
*
* @param Packet to be written to the socket.
*/
- void write(AOPacket f_packet);
+ void write(AOPacket *f_packet);
signals:
@@ -81,7 +83,7 @@ class NetworkSocket : public QObject
* @brief handlePacket
* @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.
diff --git a/core/include/packet/packet_askchaa.h b/core/include/packet/packet_askchaa.h
new file mode 100644
index 0000000..8db348f
--- /dev/null
+++ b/core/include/packet/packet_askchaa.h
@@ -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
diff --git a/core/include/packet/packet_casea.h b/core/include/packet/packet_casea.h
new file mode 100644
index 0000000..0212917
--- /dev/null
+++ b/core/include/packet/packet_casea.h
@@ -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
diff --git a/core/include/packet/packet_cc.h b/core/include/packet/packet_cc.h
new file mode 100644
index 0000000..51342ca
--- /dev/null
+++ b/core/include/packet/packet_cc.h
@@ -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
diff --git a/core/include/packet/packet_ch.h b/core/include/packet/packet_ch.h
new file mode 100644
index 0000000..815b802
--- /dev/null
+++ b/core/include/packet/packet_ch.h
@@ -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
diff --git a/core/include/packet/packet_ct.h b/core/include/packet/packet_ct.h
new file mode 100644
index 0000000..e1ffa5a
--- /dev/null
+++ b/core/include/packet/packet_ct.h
@@ -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
diff --git a/core/include/packet/packet_de.h b/core/include/packet/packet_de.h
new file mode 100644
index 0000000..9dc2739
--- /dev/null
+++ b/core/include/packet/packet_de.h
@@ -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
diff --git a/core/include/packet/packet_ee.h b/core/include/packet/packet_ee.h
new file mode 100644
index 0000000..71d2aa5
--- /dev/null
+++ b/core/include/packet/packet_ee.h
@@ -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
diff --git a/core/include/packet/packet_factory.h b/core/include/packet/packet_factory.h
new file mode 100644
index 0000000..3ef76d6
--- /dev/null
+++ b/core/include/packet/packet_factory.h
@@ -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
+ static void registerClass(QString header) { class_map[header] = &createInstance; };
+
+ private:
+ template
+ static AOPacket *createInstance(QStringList contents) { return new T(contents); };
+ template
+ static AOPacket *createInstance(QString header, QStringList contents) { return new T(header, contents); };
+ typedef std::map type_map;
+
+ static inline type_map class_map;
+};
diff --git a/core/include/packet/packet_generic.h b/core/include/packet/packet_generic.h
new file mode 100644
index 0000000..d7ac4db
--- /dev/null
+++ b/core/include/packet/packet_generic.h
@@ -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
diff --git a/core/include/packet/packet_hi.h b/core/include/packet/packet_hi.h
new file mode 100644
index 0000000..01291bd
--- /dev/null
+++ b/core/include/packet/packet_hi.h
@@ -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
diff --git a/core/include/packet/packet_hp.h b/core/include/packet/packet_hp.h
new file mode 100644
index 0000000..21b8ccd
--- /dev/null
+++ b/core/include/packet/packet_hp.h
@@ -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
diff --git a/core/include/packet/packet_id.h b/core/include/packet/packet_id.h
new file mode 100644
index 0000000..bb50547
--- /dev/null
+++ b/core/include/packet/packet_id.h
@@ -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
diff --git a/core/include/packet/packet_info.h b/core/include/packet/packet_info.h
new file mode 100644
index 0000000..9581b24
--- /dev/null
+++ b/core/include/packet/packet_info.h
@@ -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
diff --git a/core/include/packet/packet_mc.h b/core/include/packet/packet_mc.h
new file mode 100644
index 0000000..d3c271c
--- /dev/null
+++ b/core/include/packet/packet_mc.h
@@ -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
diff --git a/core/include/packet/packet_ms.h b/core/include/packet/packet_ms.h
new file mode 100644
index 0000000..4086b60
--- /dev/null
+++ b/core/include/packet/packet_ms.h
@@ -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
diff --git a/core/include/packet/packet_pe.h b/core/include/packet/packet_pe.h
new file mode 100644
index 0000000..f35dc7b
--- /dev/null
+++ b/core/include/packet/packet_pe.h
@@ -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
diff --git a/core/include/packet/packet_pw.h b/core/include/packet/packet_pw.h
new file mode 100644
index 0000000..7a9ddba
--- /dev/null
+++ b/core/include/packet/packet_pw.h
@@ -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
diff --git a/core/include/packet/packet_rc.h b/core/include/packet/packet_rc.h
new file mode 100644
index 0000000..f7fb1bf
--- /dev/null
+++ b/core/include/packet/packet_rc.h
@@ -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
diff --git a/core/include/packet/packet_rd.h b/core/include/packet/packet_rd.h
new file mode 100644
index 0000000..4f51b87
--- /dev/null
+++ b/core/include/packet/packet_rd.h
@@ -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
diff --git a/core/include/packet/packet_rm.h b/core/include/packet/packet_rm.h
new file mode 100644
index 0000000..91b4b8e
--- /dev/null
+++ b/core/include/packet/packet_rm.h
@@ -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
diff --git a/core/include/packet/packet_rt.h b/core/include/packet/packet_rt.h
new file mode 100644
index 0000000..604fd8d
--- /dev/null
+++ b/core/include/packet/packet_rt.h
@@ -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
diff --git a/core/include/packet/packet_setcase.h b/core/include/packet/packet_setcase.h
new file mode 100644
index 0000000..1df3b13
--- /dev/null
+++ b/core/include/packet/packet_setcase.h
@@ -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
diff --git a/core/include/packet/packet_zz.h b/core/include/packet/packet_zz.h
new file mode 100644
index 0000000..8920863
--- /dev/null
+++ b/core/include/packet/packet_zz.h
@@ -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
diff --git a/core/include/server.h b/core/include/server.h
index 7b690e6..9485441 100644
--- a/core/include/server.h
+++ b/core/include/server.h
@@ -182,14 +182,14 @@ class Server : public QObject
*
* @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.
*
* @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..
@@ -198,7 +198,7 @@ class Server : public QObject
*
* @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.
@@ -209,7 +209,7 @@ class Server : public QObject
*
* @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.
@@ -218,7 +218,7 @@ class Server : public QObject
*
* @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).
diff --git a/core/src/aoclient.cpp b/core/src/aoclient.cpp
index 9866b55..8ac9e60 100644
--- a/core/src/aoclient.cpp
+++ b/core/src/aoclient.cpp
@@ -21,7 +21,7 @@
#include "include/command_extension.h"
#include "include/config_manager.h"
#include "include/db_manager.h"
-#include "include/network/aopacket.h"
+#include "include/packet/packet_factory.h"
#include "include/server.h"
const QMap AOClient::COMMANDS{
@@ -176,37 +176,36 @@ void AOClient::clientDisconnected()
emit clientSuccessfullyDisconnected(m_id);
}
-void AOClient::handlePacket(AOPacket packet)
+void AOClient::handlePacket(AOPacket *packet)
{
#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
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;
}
- if (!checkPermission(l_info.acl_permission)) {
+ if (!checkPermission(packet->getPacketInfo().acl_permission)) {
return;
}
- if (packet.getHeader() != "CH" && m_joined) {
+ if (packet->getPacketInfo().header != "CH" && m_joined) {
if (m_is_afk)
sendServerMessage("You are no longer AFK.");
m_is_afk = false;
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
- 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
return;
}
- (this->*(l_info.action))(l_area, packet.getContent().length(), packet.getContent(), packet);
+ packet->handlePacket(l_area, *this);
}
void AOClient::changeArea(int new_area)
@@ -385,7 +384,7 @@ void AOClient::arup(ARUPType type, bool broadcast)
}
}
if (broadcast)
- server->broadcast(AOPacket("ARUP", l_arup_data));
+ server->broadcast(PacketFactory::createPacket("ARUP", l_arup_data));
else
sendPacket("ARUP", l_arup_data);
}
@@ -398,22 +397,22 @@ void AOClient::fullArup()
arup(ARUPType::LOCKED, false);
}
-void AOClient::sendPacket(AOPacket packet)
+void AOClient::sendPacket(AOPacket *packet)
{
#ifdef NET_DEBUG
- qDebug() << "Sent packet:" << packet.getHeader() << ":" << packet.getContent();
+ qDebug() << "Sent packet:" << packet->getPacketInfo().header << ":" << packet->getContent();
#endif
m_socket->write(packet);
}
void AOClient::sendPacket(QString header, QStringList contents)
{
- sendPacket(AOPacket(header, contents));
+ sendPacket(PacketFactory::createPacket(header, contents));
}
void AOClient::sendPacket(QString header)
{
- sendPacket(AOPacket(header, {}));
+ sendPacket(PacketFactory::createPacket(header, {}));
}
void AOClient::calculateIpid()
@@ -438,12 +437,12 @@ void AOClient::sendServerMessage(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)
{
- 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
@@ -516,10 +515,10 @@ AOClient::AOClient(Server *p_server, NetworkSocket *socket, QObject *parent, int
m_current_area(0),
m_current_char(""),
m_socket(socket),
- server(p_server),
- is_partial(false),
+ m_music_manager(p_manager),
m_last_wtce_time(0),
- m_music_manager(p_manager)
+ server(p_server),
+ is_partial(false)
{
m_afk_timer = new QTimer;
m_afk_timer->setSingleShot(true);
diff --git a/core/src/area_data.cpp b/core/src/area_data.cpp
index 0bbf8a0..061bc68 100644
--- a/core/src/area_data.cpp
+++ b/core/src/area_data.cpp
@@ -21,7 +21,7 @@
#include "include/area_data.h"
#include "include/config_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) :
m_index(p_index),
@@ -108,7 +108,7 @@ void AreaData::clientJoinedArea(int f_charId, int 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.
// 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 AreaData::owners() const
@@ -624,7 +624,7 @@ QString AreaData::addJukeboxSong(QString f_song)
if (l_song.second > 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);
setCurrentMusic(f_song);
setMusicPlayedBy("Jukebox");
@@ -650,7 +650,7 @@ void AreaData::switchJukeboxSong()
if (m_jukebox_queue.size() == 1) {
l_song_name = m_jukebox_queue[0];
QPair 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);
}
else {
@@ -658,7 +658,7 @@ void AreaData::switchJukeboxSong()
l_song_name = m_jukebox_queue[l_random_index];
QPair 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_queue.remove(l_random_index);
diff --git a/core/src/commands/area.cpp b/core/src/commands/area.cpp
index 731cff8..c751561 100644
--- a/core/src/commands/area.cpp
+++ b/core/src/commands/area.cpp
@@ -19,7 +19,7 @@
#include "include/area_data.h"
#include "include/config_manager.h"
-#include "include/network/aopacket.h"
+#include "include/packet/packet_factory.h"
#include "include/server.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 (server->getBackgrounds().contains(f_background, Qt::CaseInsensitive) || area->ignoreBgList() == true) {
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);
}
else {
@@ -321,7 +321,7 @@ void AOClient::cmdBgLock(int argc, QStringList argv)
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)
@@ -335,7 +335,7 @@ void AOClient::cmdBgUnlock(int argc, QStringList argv)
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)
@@ -347,7 +347,7 @@ void AOClient::cmdStatus(int argc, QStringList argv)
if (l_area->changeStatus(l_arg)) {
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 {
const QStringList keys = AreaData::map_statuses.keys();
diff --git a/core/src/commands/casing.cpp b/core/src/commands/casing.cpp
index ead5865..00f151d 100644
--- a/core/src/commands/casing.cpp
+++ b/core/src/commands/casing.cpp
@@ -19,7 +19,7 @@
#include "include/area_data.h"
#include "include/config_manager.h"
-#include "include/network/aopacket.h"
+#include "include/packet/packet_factory.h"
#include "include/server.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);
if (l_area->testimony().size() - 1 > 0) {
l_area->restartTestimony();
- server->broadcast(AOPacket("RT", {"testimony2"}), m_current_area);
- server->broadcast(AOPacket("MS", {l_area->testimony()[0]}), m_current_area);
+ server->broadcast(PacketFactory::createPacket("RT", {"testimony2"}), m_current_area);
+ server->broadcast(PacketFactory::createPacket("MS", {l_area->testimony()[0]}), m_current_area);
return;
}
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);
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.");
}
diff --git a/core/src/commands/command_helper.cpp b/core/src/commands/command_helper.cpp
index dd44181..b985591 100644
--- a/core/src/commands/command_helper.cpp
+++ b/core/src/commands/command_helper.cpp
@@ -19,7 +19,7 @@
#include "include/area_data.h"
#include "include/config_manager.h"
-#include "include/network/aopacket.h"
+#include "include/packet/packet_factory.h"
#include "include/server.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 += "notice:\n\n" + f_notice;
sendServerMessageArea(l_message);
- AOPacket l_packet("BB", {l_message});
+ AOPacket *l_packet = PacketFactory::createPacket("BB", {l_message});
if (f_global)
server->broadcast(l_packet);
else
diff --git a/core/src/commands/messaging.cpp b/core/src/commands/messaging.cpp
index 9100fd0..bfb351a 100644
--- a/core/src/commands/messaging.cpp
+++ b/core/src/commands/messaging.cpp
@@ -18,7 +18,7 @@
#include "include/aoclient.h"
#include "include/area_data.h"
-#include "include/network/aopacket.h"
+#include "include/packet/packet_factory.h"
#include "include/server.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_message = argv.join(" ");
// 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_user_packet = AOPacket("CT", {"[G][" + 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 = 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);
return;
}
@@ -89,7 +89,7 @@ void AOClient::cmdNeed(int argc, QStringList argv)
QString l_sender_area = server->getAreaName(m_current_area);
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)
@@ -175,7 +175,7 @@ void AOClient::cmdM(int argc, QStringList argv)
QString l_sender_name = m_ooc_name;
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)
@@ -185,7 +185,7 @@ void AOClient::cmdGM(int argc, QStringList argv)
QString l_sender_name = m_ooc_name;
QString l_sender_area = server->getAreaName(m_current_area);
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)
@@ -194,7 +194,7 @@ void AOClient::cmdLM(int argc, QStringList argv)
QString l_sender_name = m_ooc_name;
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)
@@ -524,7 +524,7 @@ void AOClient::cmdA(int argc, QStringList argv)
argv.removeAt(0);
QString l_sender_name = m_ooc_name;
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)
@@ -537,7 +537,7 @@ void AOClient::cmdS(int argc, QStringList argv)
for (int i = 0; i <= l_all_areas; i++) {
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);
}
}
diff --git a/core/src/commands/music.cpp b/core/src/commands/music.cpp
index 0752473..a3693b7 100644
--- a/core/src/commands/music.cpp
+++ b/core/src/commands/music.cpp
@@ -19,7 +19,7 @@
#include "include/area_data.h"
#include "include/music_manager.h"
-#include "include/network/aopacket.h"
+#include "include/packet/packet_factory.h"
#include "include/server.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 {
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);
}
diff --git a/core/src/commands/roleplay.cpp b/core/src/commands/roleplay.cpp
index e3dd245..3175f1b 100644
--- a/core/src/commands/roleplay.cpp
+++ b/core/src/commands/roleplay.cpp
@@ -19,7 +19,7 @@
#include "include/area_data.h"
#include "include/config_manager.h"
-#include "include/network/aopacket.h"
+#include "include/packet/packet_factory.h"
#include "include/server.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
l_requested_timer = l_area->timers().at(l_timer_id - 1);
- AOPacket l_show_timer("TI", {QString::number(l_timer_id), "2"});
- AOPacket l_hide_timer("TI", {QString::number(l_timer_id), "3"});
+ AOPacket *l_show_timer = PacketFactory::createPacket("TI", {QString::number(l_timer_id), "2"});
+ AOPacket *l_hide_timer = PacketFactory::createPacket("TI", {QString::number(l_timer_id), "3"});
bool l_is_global = l_timer_id == 0;
// 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->start();
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_update_timer) : server->broadcast(l_update_timer, m_current_area);
return;
@@ -216,7 +216,7 @@ void AOClient::cmdTimer(int argc, QStringList argv)
if (argv[1] == "start") {
l_requested_timer->start();
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_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->stop();
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);
}
else if (argv[1] == "hide" || argv[1] == "unset") {
diff --git a/core/src/music_manager.cpp b/core/src/music_manager.cpp
index 28d63e1..310d35a 100644
--- a/core/src/music_manager.cpp
+++ b/core/src/music_manager.cpp
@@ -1,7 +1,7 @@
#include "include/music_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> f_root_list, 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});
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));
- 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;
}
@@ -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});
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));
- 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;
}
@@ -165,7 +165,7 @@ bool MusicManager::removeCategorySong(QString f_songcategory_name, int f_area_id
l_customs_ordered.removeAll(f_songcategory_name);
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;
} // Fallthrough
}
@@ -178,7 +178,7 @@ bool MusicManager::toggleRootEnabled(int f_area_id)
if (m_global_enabled.value(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);
}
@@ -234,5 +234,5 @@ void MusicManager::reloadRequest()
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);
}
diff --git a/core/src/network/aopacket.cpp b/core/src/network/aopacket.cpp
index a82d7b1..43afe5f 100644
--- a/core/src/network/aopacket.cpp
+++ b/core/src/network/aopacket.cpp
@@ -17,50 +17,42 @@
//////////////////////////////////////////////////////////////////////////////////////
#include "include/network/aopacket.h"
-AOPacket::AOPacket(QString p_header, QStringList p_contents) :
- m_header(p_header),
+#include "include/packet/packet_askchaa.h"
+#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_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()
{
return m_content;
}
-QString AOPacket::getHeader()
-{
- return m_header;
-}
-
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.
this->escapeContent();
}
@@ -68,7 +60,7 @@ QString AOPacket::toString()
// Of course AO has SOME expection to the rule.
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()
@@ -117,3 +109,27 @@ bool AOPacket::isPacketEscaped()
{
return m_escaped;
}
+
+void AOPacket::registerPackets()
+{
+ PacketFactory::registerClass("askchaa");
+ PacketFactory::registerClass("CASEA");
+ PacketFactory::registerClass("CC");
+ PacketFactory::registerClass("CH");
+ PacketFactory::registerClass("CT");
+ PacketFactory::registerClass("DE");
+ PacketFactory::registerClass("EE");
+ PacketFactory::registerClass("HI");
+ PacketFactory::registerClass("HP");
+ PacketFactory::registerClass("ID");
+ PacketFactory::registerClass("MC");
+ PacketFactory::registerClass("MS");
+ PacketFactory::registerClass("PE");
+ PacketFactory::registerClass("PW");
+ PacketFactory::registerClass("RC");
+ PacketFactory::registerClass("RD");
+ PacketFactory::registerClass("RM");
+ PacketFactory::registerClass("RT");
+ PacketFactory::registerClass("SETCASE");
+ PacketFactory::registerClass("ZZ");
+}
diff --git a/core/src/network/network_socket.cpp b/core/src/network/network_socket.cpp
index fc4d875..ceeddc6 100644
--- a/core/src/network/network_socket.cpp
+++ b/core/src/network/network_socket.cpp
@@ -16,6 +16,7 @@
// along with this program. If not, see . //
//////////////////////////////////////////////////////////////////////////////////////
#include "include/network/network_socket.h"
+#include "include/packet/packet_factory.h"
NetworkSocket::NetworkSocket(QTcpSocket *f_socket, QObject *parent) :
QObject(parent)
@@ -106,7 +107,12 @@ void NetworkSocket::readData()
}
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);
}
}
@@ -127,19 +133,24 @@ void NetworkSocket::ws_readData(QString f_data)
}
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);
}
}
-void NetworkSocket::write(AOPacket f_packet)
+void NetworkSocket::write(AOPacket *f_packet)
{
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();
}
else {
- m_client_socket.ws->sendTextMessage(f_packet.toString());
+ m_client_socket.ws->sendTextMessage(f_packet->toString());
m_client_socket.ws->flush();
}
}
diff --git a/core/src/packet/packet_askchaa.cpp b/core/src/packet/packet_askchaa.cpp
new file mode 100644
index 0000000..4e71e16
--- /dev/null
+++ b/core/src/packet/packet_askchaa.cpp
@@ -0,0 +1,36 @@
+#include "include/packet/packet_askchaa.h"
+#include "include/config_manager.h"
+#include "include/server.h"
+
+#include
+
+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;
+}
diff --git a/core/src/packet/packet_casea.cpp b/core/src/packet/packet_casea.cpp
new file mode 100644
index 0000000..2dd7452
--- /dev/null
+++ b/core/src/packet/packet_casea.cpp
@@ -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
+
+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 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 l_clients_to_alert;
+ // here lies morton, RIP
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
+ QSet l_needs_set(l_needs_list.begin(), l_needs_list.end());
+#else
+ QSet l_needs_set = l_needs_list.toSet();
+#endif
+ const QVector l_clients = client.getServer()->getClients();
+ for (AOClient *l_client : l_clients) {
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
+ QSet l_matches(l_client->m_casing_preferences.begin(), l_client->m_casing_preferences.end());
+ l_matches.intersect(l_needs_set);
+#else
+ QSet 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(m_content.at(i))) {
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/core/src/packet/packet_cc.cpp b/core/src/packet/packet_cc.cpp
new file mode 100644
index 0000000..823ab5e
--- /dev/null
+++ b/core/src/packet/packet_cc.cpp
@@ -0,0 +1,48 @@
+#include "include/packet/packet_cc.h"
+#include "include/akashiutils.h"
+#include "include/config_manager.h"
+#include "include/server.h"
+
+#include
+
+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(m_content.at(1));
+}
diff --git a/core/src/packet/packet_ch.cpp b/core/src/packet/packet_ch.cpp
new file mode 100644
index 0000000..f583f15
--- /dev/null
+++ b/core/src/packet/packet_ch.cpp
@@ -0,0 +1,33 @@
+#include "include/packet/packet_ch.h"
+#include "include/akashiutils.h"
+#include "include/server.h"
+
+#include
+
+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(m_content.at(0));
+}
diff --git a/core/src/packet/packet_ct.cpp b/core/src/packet/packet_ct.cpp
new file mode 100644
index 0000000..924fee6
--- /dev/null
+++ b/core/src/packet/packet_ct.cpp
@@ -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
+
+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;
+}
diff --git a/core/src/packet/packet_de.cpp b/core/src/packet/packet_de.cpp
new file mode 100644
index 0000000..e8064af
--- /dev/null
+++ b/core/src/packet/packet_de.cpp
@@ -0,0 +1,36 @@
+#include "include/packet/packet_de.h"
+#include "include/akashiutils.h"
+#include "include/server.h"
+
+#include
+
+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(m_content.at(0));
+}
diff --git a/core/src/packet/packet_ee.cpp b/core/src/packet/packet_ee.cpp
new file mode 100644
index 0000000..5d5f69f
--- /dev/null
+++ b/core/src/packet/packet_ee.cpp
@@ -0,0 +1,37 @@
+#include "include/packet/packet_ee.h"
+#include "include/akashiutils.h"
+#include "include/server.h"
+
+#include
+
+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(m_content.at(0));
+}
diff --git a/core/src/packet/packet_factory.cpp b/core/src/packet/packet_factory.cpp
new file mode 100644
index 0000000..cf2f098
--- /dev/null
+++ b/core/src/packet/packet_factory.cpp
@@ -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(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;
+}
diff --git a/core/src/packet/packet_generic.cpp b/core/src/packet/packet_generic.cpp
new file mode 100644
index 0000000..8a6e77b
--- /dev/null
+++ b/core/src/packet/packet_generic.cpp
@@ -0,0 +1,31 @@
+#include "include/packet/packet_generic.h"
+
+#include
+
+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;
+}
diff --git a/core/src/packet/packet_hi.cpp b/core/src/packet/packet_hi.cpp
new file mode 100644
index 0000000..565ddf8
--- /dev/null
+++ b/core/src/packet/packet_hi.cpp
@@ -0,0 +1,50 @@
+#include "include/packet/packet_hi.h"
+#include "include/akashiutils.h"
+#include "include/db_manager.h"
+#include "include/server.h"
+
+#include
+
+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;
+}
diff --git a/core/src/packet/packet_hp.cpp b/core/src/packet/packet_hp.cpp
new file mode 100644
index 0000000..f27c8f9
--- /dev/null
+++ b/core/src/packet/packet_hp.cpp
@@ -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
+
+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(m_content.at(0)))
+ return false;
+ if (!AkashiUtils::checkArgType(m_content.at(1)))
+ return false;
+ return true;
+}
diff --git a/core/src/packet/packet_id.cpp b/core/src/packet/packet_id.cpp
new file mode 100644
index 0000000..517f08d
--- /dev/null
+++ b/core/src/packet/packet_id.cpp
@@ -0,0 +1,76 @@
+#include "include/packet/packet_id.h"
+#include "include/config_manager.h"
+#include "include/server.h"
+
+#include
+
+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;
+}
diff --git a/core/src/packet/packet_mc.cpp b/core/src/packet/packet_mc.cpp
new file mode 100644
index 0000000..710b916
--- /dev/null
+++ b/core/src/packet/packet_mc.cpp
@@ -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
+
+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 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;
+}
diff --git a/core/src/packet/packet_ms.cpp b/core/src/packet/packet_ms.cpp
new file mode 100644
index 0000000..dbdbda2
--- /dev/null
+++ b/core/src/packet/packet_ms.cpp
@@ -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
+
+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 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("(?>)(?[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;
+}
diff --git a/core/src/packet/packet_pe.cpp b/core/src/packet/packet_pe.cpp
new file mode 100644
index 0000000..272c052
--- /dev/null
+++ b/core/src/packet/packet_pe.cpp
@@ -0,0 +1,32 @@
+#include "include/packet/packet_pe.h"
+#include "include/server.h"
+
+#include
+
+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;
+}
diff --git a/core/src/packet/packet_pw.cpp b/core/src/packet/packet_pw.cpp
new file mode 100644
index 0000000..dcaf84a
--- /dev/null
+++ b/core/src/packet/packet_pw.cpp
@@ -0,0 +1,30 @@
+#include "include/packet/packet_pw.h"
+#include "include/server.h"
+
+#include
+
+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;
+}
diff --git a/core/src/packet/packet_rc.cpp b/core/src/packet/packet_rc.cpp
new file mode 100644
index 0000000..02d429c
--- /dev/null
+++ b/core/src/packet/packet_rc.cpp
@@ -0,0 +1,30 @@
+#include "include/packet/packet_rc.h"
+#include "include/server.h"
+
+#include
+
+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;
+}
diff --git a/core/src/packet/packet_rd.cpp b/core/src/packet/packet_rd.cpp
new file mode 100644
index 0000000..6756488
--- /dev/null
+++ b/core/src/packet/packet_rd.cpp
@@ -0,0 +1,72 @@
+#include "include/packet/packet_rd.h"
+#include "include/config_manager.h"
+#include "include/server.h"
+
+#include
+
+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 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;
+}
diff --git a/core/src/packet/packet_rm.cpp b/core/src/packet/packet_rm.cpp
new file mode 100644
index 0000000..99f2ce2
--- /dev/null
+++ b/core/src/packet/packet_rm.cpp
@@ -0,0 +1,30 @@
+#include "include/packet/packet_rm.h"
+#include "include/server.h"
+
+#include
+
+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;
+}
diff --git a/core/src/packet/packet_rt.cpp b/core/src/packet/packet_rt.cpp
new file mode 100644
index 0000000..104e3d4
--- /dev/null
+++ b/core/src/packet/packet_rt.cpp
@@ -0,0 +1,43 @@
+#include "include/packet/packet_rt.h"
+#include "include/packet/packet_factory.h"
+#include "include/server.h"
+
+#include
+
+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;
+}
diff --git a/core/src/packet/packet_setcase.cpp b/core/src/packet/packet_setcase.cpp
new file mode 100644
index 0000000..1453b35
--- /dev/null
+++ b/core/src/packet/packet_setcase.cpp
@@ -0,0 +1,38 @@
+#include "include/packet/packet_setcase.h"
+#include "include/server.h"
+
+#include
+
+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 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;
+}
diff --git a/core/src/packet/packet_zz.cpp b/core/src/packet/packet_zz.cpp
new file mode 100644
index 0000000..c04b098
--- /dev/null
+++ b/core/src/packet/packet_zz.cpp
@@ -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
+
+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 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;
+}
diff --git a/core/src/packets.cpp b/core/src/packets.cpp
index 3a44235..2bef526 100644
--- a/core/src/packets.cpp
+++ b/core/src/packets.cpp
@@ -24,577 +24,10 @@
#include "include/config_manager.h"
#include "include/db_manager.h"
#include "include/music_manager.h"
-#include "include/network/aopacket.h"
+#include "include/packet/packet_factory.h"
#include "include/server.h"
-void AOClient::pktDefault(AreaData *area, int argc, QStringList argv, AOPacket packet)
-{
- 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 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 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 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 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 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 l_clients_to_alert;
- // here lies morton, RIP
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
- QSet l_needs_set(l_needs_list.begin(), l_needs_list.end());
-#else
- QSet l_needs_set = l_needs_list.toSet();
-#endif
- const QVector l_clients = server->getClients();
- for (AOClient *l_client : l_clients) {
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
- QSet l_matches(l_client->m_casing_preferences.begin(), l_client->m_casing_preferences.end());
- l_matches.intersect(l_needs_set);
-#else
- QSet 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)
+void AOClient::sendEvidenceList(AreaData *area) const
{
const QVector l_clients = server->getClients();
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));
}
- sendPacket(AOPacket("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 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("(?>)(?[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);
+ sendPacket(PacketFactory::createPacket("LE", l_evidence_list));
}
QString AOClient::dezalgo(QString p_text)
diff --git a/core/src/server.cpp b/core/src/server.cpp
index f0fc972..e8b56d4 100644
--- a/core/src/server.cpp
+++ b/core/src/server.cpp
@@ -27,8 +27,8 @@
#include "include/discord.h"
#include "include/logger/u_logger.h"
#include "include/music_manager.h"
-#include "include/network/aopacket.h"
#include "include/network/network_socket.h"
+#include "include/packet/packet_factory.h"
Server::Server(int p_port, int p_ws_port, QObject *parent) :
QObject(parent),
@@ -56,6 +56,8 @@ Server::Server(int p_port, int p_ws_port, QObject *parent) :
logger = new ULogger(this);
connect(this, &Server::logConnectionAttempt, logger, &ULogger::logConnectionAttempt);
+
+ AOPacket::registerPackets();
}
void Server::start()
@@ -115,7 +117,7 @@ void Server::start()
ConfigManager::musiclist();
music_manager = new MusicManager(ConfigManager::ordered_songs(), ConfigManager::cdnList(), ConfigManager::musiclist(), this);
connect(music_manager, &MusicManager::sendFMPacket, this, &Server::unicast);
- connect(music_manager, &MusicManager::sendAreaFMPacket, this, QOverload::of(&Server::broadcast));
+ connect(music_manager, &MusicManager::sendAreaFMPacket, this, QOverload::of(&Server::broadcast));
// Get musiclist from config file
m_music_list = music_manager->rootMusiclist();
@@ -127,7 +129,7 @@ void Server::start()
QString area_name = raw_area_names[i];
AreaData *l_area = new AreaData(area_name, i, music_manager);
m_areas.insert(i, l_area);
- connect(l_area, &AreaData::sendAreaPacket, this, QOverload::of(&Server::broadcast));
+ connect(l_area, &AreaData::sendAreaPacket, this, QOverload::of(&Server::broadcast));
connect(l_area, &AreaData::sendAreaPacketClient, this, &Server::unicast);
connect(l_area, &AreaData::userJoinedArea, music_manager, &MusicManager::userJoinedArea);
music_manager->registerArea(i);
@@ -162,8 +164,8 @@ void Server::clientConnected()
// Too many players. Reject connection!
// This also enforces the maximum playercount.
if (m_available_ids.empty()) {
- AOPacket disconnect_reason("BD", {"Maximum playercount has been reached."});
- socket->write(disconnect_reason.toUtf8());
+ AOPacket *disconnect_reason = PacketFactory::createPacket("BD", {"Maximum playercount has been reached."});
+ socket->write(disconnect_reason->toUtf8());
socket->flush();
socket->close();
socket->deleteLater();
@@ -192,8 +194,8 @@ void Server::clientConnected()
if (is_banned) {
QString reason = ban.second;
- AOPacket ban_reason("BD", {reason});
- socket->write(ban_reason.toUtf8());
+ AOPacket *ban_reason = PacketFactory::createPacket("BD", {reason});
+ socket->write(ban_reason->toUtf8());
}
if (is_banned || is_at_multiclient_limit) {
socket->flush();
@@ -210,8 +212,8 @@ void Server::clientConnected()
if (isIPBanned(l_remote_ip)) {
QString l_reason = "Your IP has been banned by a moderator.";
- AOPacket l_ban_reason("BD", {l_reason});
- socket->write(l_ban_reason.toUtf8());
+ AOPacket *l_ban_reason = PacketFactory::createPacket("BD", {l_reason});
+ socket->write(l_ban_reason->toUtf8());
client->deleteLater();
socket->close();
markIDFree(user_id);
@@ -228,9 +230,10 @@ void Server::clientConnected()
});
connect(l_socket, &NetworkSocket::handlePacket, client, &AOClient::handlePacket);
- AOPacket decryptor("decryptor", {"NOENCRYPT"}); // This is the infamous workaround for
- // tsuserver4. It should disable fantacrypt
- // completely in any client 2.4.3 or newer
+ // This is the infamous workaround for
+ // tsuserver4. It should disable fantacrypt
+ // completely in any client 2.4.3 or newer
+ AOPacket *decryptor = PacketFactory::createPacket("decryptor", {"NOENCRYPT"});
client->sendPacket(decryptor);
hookupAOClient(client);
#ifdef NET_DEBUG
@@ -246,7 +249,7 @@ void Server::ws_clientConnected()
// Too many players. Reject connection!
// This also enforces the maximum playercount.
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->close();
l_socket->deleteLater();
@@ -272,8 +275,8 @@ void Server::ws_clientConnected()
if (is_banned) {
QString reason = ban.second;
- AOPacket ban_reason("BD", {reason});
- socket->sendTextMessage(ban_reason.toUtf8());
+ AOPacket *ban_reason = PacketFactory::createPacket("BD", {reason});
+ socket->sendTextMessage(ban_reason->toUtf8());
}
if (is_banned || is_at_multiclient_limit) {
client->deleteLater();
@@ -289,7 +292,7 @@ void Server::ws_clientConnected()
if (isIPBanned(l_remote_ip)) {
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);
client->deleteLater();
l_socket->close(QWebSocketProtocol::CloseCodeNormal);
@@ -307,9 +310,10 @@ void Server::ws_clientConnected()
});
connect(l_socket, &NetworkSocket::handlePacket, client, &AOClient::handlePacket);
- AOPacket decryptor("decryptor", {"NOENCRYPT"}); // This is the infamous workaround for
- // tsuserver4. It should disable fantacrypt
- // completely in any client 2.4.3 or newer
+ // This is the infamous workaround for
+ // tsuserver4. It should disable fantacrypt
+ // completely in any client 2.4.3 or newer
+ AOPacket *decryptor = PacketFactory::createPacket("decryptor", {"NOENCRYPT"});
client->sendPacket(decryptor);
hookupAOClient(client);
}
@@ -323,7 +327,7 @@ void Server::updateCharsTaken(AreaData *area)
: QStringLiteral("0"));
}
- AOPacket response_cc("CharsCheck", chars_taken);
+ AOPacket *response_cc = PacketFactory::createPacket("CharsCheck", chars_taken);
for (AOClient *l_client : qAsConst(m_clients)) {
if (l_client->m_current_area == area->index()) {
@@ -331,7 +335,7 @@ void Server::updateCharsTaken(AreaData *area)
l_client->sendPacket(response_cc);
else {
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);
}
}
@@ -384,7 +388,7 @@ void Server::reloadSettings()
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 l_client_ids = m_areas.value(area_index)->joinedIDs();
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)) {
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)) {
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) {
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);
if (l_client != nullptr) { // This should never happen, but safety first.
diff --git a/tests/tests.pro b/tests/tests.pro
index 0d78c02..159abf1 100644
--- a/tests/tests.pro
+++ b/tests/tests.pro
@@ -5,6 +5,7 @@ SUBDIRS += \
unittest_music_manager \
unittest_acl_roles_handler \
unittest_command_extension \
- unittest_aopacket \
unittest_config_manager \
- unittest_crypto
+ unittest_crypto \
+ unittest_aopacket \
+ unittest_akashi_utils
diff --git a/tests/tests_common.pri b/tests/tests_common.pri
index 2f19985..f50decc 100644
--- a/tests/tests_common.pri
+++ b/tests/tests_common.pri
@@ -2,6 +2,8 @@ QT += network websockets core sql testlib
CONFIG += qt console warn_on depend_includepath testcase no_testcase_installs
CONFIG -= app_bundle
+unix: CONFIG += c++1z
+win32: CONFIG += c++2a
coverage {
LIBS += -lgcov
diff --git a/tests/unittest_akashi_utils/tst_unittest_akashi_utils.cpp b/tests/unittest_akashi_utils/tst_unittest_akashi_utils.cpp
new file mode 100644
index 0000000..2225e0c
--- /dev/null
+++ b/tests/unittest_akashi_utils/tst_unittest_akashi_utils.cpp
@@ -0,0 +1,111 @@
+#include "include/akashiutils.h"
+#include
+
+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("content");
+ QTest::addColumn("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(content);
+ QCOMPARE(result, expected_result);
+}
+
+void tst_AkashiUtils::floating_data()
+{
+ QTest::addColumn("content");
+ QTest::addColumn("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(content);
+ QCOMPARE(result, expected_result);
+}
+
+void tst_AkashiUtils::boolean_data()
+{
+ QTest::addColumn("content");
+ QTest::addColumn("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(content);
+ QCOMPARE(result, expected_result);
+}
+
+void tst_AkashiUtils::doublep_data()
+{
+ QTest::addColumn("content");
+ QTest::addColumn("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(content);
+ QCOMPARE(result, expected_result);
+}
+
+}
+};
+
+QTEST_APPLESS_MAIN(tests::unittests::tst_AkashiUtils)
+
+#include "tst_unittest_akashi_utils.moc"
diff --git a/tests/unittest_akashi_utils/unittest_akashi_utils.pro b/tests/unittest_akashi_utils/unittest_akashi_utils.pro
new file mode 100644
index 0000000..66c7980
--- /dev/null
+++ b/tests/unittest_akashi_utils/unittest_akashi_utils.pro
@@ -0,0 +1,5 @@
+QT -= gui
+
+include(../tests_common.pri)
+
+SOURCES += tst_unittest_akashi_utils.cpp
diff --git a/tests/unittest_aopacket/tst_unittest_aopacket.cpp b/tests/unittest_aopacket/tst_unittest_aopacket.cpp
index 0fe8a6a..3232610 100644
--- a/tests/unittest_aopacket/tst_unittest_aopacket.cpp
+++ b/tests/unittest_aopacket/tst_unittest_aopacket.cpp
@@ -2,6 +2,7 @@
#include
#include "include/network/aopacket.h"
+#include "include/packet/packet_factory.h"
namespace tests {
namespace unittests {
@@ -14,9 +15,12 @@ class Packet : public QObject
Q_OBJECT
public:
- AOPacket m_packet = AOPacket{"", {}};
-
private slots:
+ /**
+ * @brief Initializes all tests
+ */
+ void init();
+
/**
* @brief Creates a packet from a defined header and content.
*/
@@ -33,11 +37,16 @@ class Packet : public QObject
void createPacketFromString();
};
+void Packet::init()
+{
+ AOPacket::registerPackets();
+}
+
void Packet::createPacket()
{
- AOPacket packet = AOPacket("HI", {"HDID"});
- QCOMPARE(packet.getHeader(), "HI");
- QCOMPARE(packet.getContent(), {"HDID"});
+ AOPacket *packet = PacketFactory::createPacket("HI", {"HDID"});
+ QCOMPARE(packet->getPacketInfo().header, "HI");
+ QCOMPARE(packet->getContent(), {"HDID"});
}
void Packet::createPacketFromString_data()
@@ -64,7 +73,11 @@ void Packet::createPacketFromString_data()
QTest::newRow("Unescaped characters") << "MC#20% Cooler#"
<< "Unknown"
- << QStringList{"Unknown"}; // This should be impossible.
+ << QStringList{"Unknown"};
+
+ QTest::newRow("Empty packet") << ""
+ << "Unknown"
+ << QStringList{"Unknown"};
}
void Packet::createPacketFromString()
@@ -73,9 +86,9 @@ void Packet::createPacketFromString()
QFETCH(QString, expected_header);
QFETCH(QStringList, expected_content);
- AOPacket packet = AOPacket(incoming_packet);
- QCOMPARE(packet.getHeader(), expected_header);
- QCOMPARE(packet.getContent(), expected_content);
+ AOPacket *packet = PacketFactory::createPacket(incoming_packet);
+ QCOMPARE(packet->getPacketInfo().header, expected_header);
+ QCOMPARE(packet->getContent(), expected_content);
}
}