Replace packet handler with templates (#300)
* Replace packet handler with templates * Move all packet functions into classes * Liberal use of Q_UNSUED * Add correct argument count to packets * Fix windows build issues * Partially implement argument type validation * Where applicable. * Checking if we can convert from a string to a string would be useless * Fix unit tests for AOPacket Co-authored-by: scatterflower <marisa@scatterflower.online> Co-authored-by: Salanto <62221668+Salanto@users.noreply.github.com>
This commit is contained in:
parent
66b58b77bf
commit
ca84b54597
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
57
core/include/akashiutils.h
Normal file
57
core/include/akashiutils.h
Normal file
@ -0,0 +1,57 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// akashi - a server for Attorney Online 2 //
|
||||
// Copyright (C) 2020 scatterflower //
|
||||
// //
|
||||
// This program is free software: you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU Affero General Public License as //
|
||||
// published by the Free Software Foundation, either version 3 of the //
|
||||
// License, or (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU Affero General Public License for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU Affero General Public License //
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef AKASHI_UTILS_H
|
||||
#define AKASHI_UTILS_H
|
||||
|
||||
#include <QVariant>
|
||||
#include <math.h>
|
||||
|
||||
class AkashiUtils
|
||||
{
|
||||
private:
|
||||
AkashiUtils(){};
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
static inline bool checkArgType(QString arg)
|
||||
{
|
||||
QVariant qvar = arg;
|
||||
if (!qvar.canConvert<T>())
|
||||
return false;
|
||||
|
||||
if (std::is_same<T, int>()) {
|
||||
bool ok;
|
||||
qvar.toInt(&ok);
|
||||
return ok;
|
||||
}
|
||||
else if (std::is_same<T, float>()) {
|
||||
bool ok;
|
||||
float f = qvar.toFloat(&ok);
|
||||
return ok && !isnan((float)f);
|
||||
}
|
||||
else if (std::is_same<T, double>()) {
|
||||
bool ok;
|
||||
double d = qvar.toDouble(&ok);
|
||||
return ok && !isnan((double)d);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
||||
#endif // AKASHI_UTILS_H
|
@ -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<QString, PacketInfo> packets{
|
||||
{"HI", {ACLRole::NONE, 1, &AOClient::pktHardwareId}},
|
||||
{"ID", {ACLRole::NONE, 2, &AOClient::pktSoftwareId}},
|
||||
{"askchaa", {ACLRole::NONE, 0, &AOClient::pktBeginLoad}},
|
||||
{"RC", {ACLRole::NONE, 0, &AOClient::pktRequestChars}},
|
||||
{"RM", {ACLRole::NONE, 0, &AOClient::pktRequestMusic}},
|
||||
{"RD", {ACLRole::NONE, 0, &AOClient::pktLoadingDone}},
|
||||
{"PW", {ACLRole::NONE, 1, &AOClient::pktCharPassword}},
|
||||
{"CC", {ACLRole::NONE, 3, &AOClient::pktSelectChar}},
|
||||
{"MS", {ACLRole::NONE, 15, &AOClient::pktIcChat}},
|
||||
{"CT", {ACLRole::NONE, 2, &AOClient::pktOocChat}},
|
||||
{"CH", {ACLRole::NONE, 1, &AOClient::pktPing}},
|
||||
{"MC", {ACLRole::NONE, 2, &AOClient::pktChangeMusic}},
|
||||
{"RT", {ACLRole::NONE, 1, &AOClient::pktWtCe}},
|
||||
{"HP", {ACLRole::NONE, 2, &AOClient::pktHpBar}},
|
||||
{"ZZ", {ACLRole::NONE, 0, &AOClient::pktModCall}},
|
||||
{"PE", {ACLRole::NONE, 3, &AOClient::pktAddEvidence}},
|
||||
{"DE", {ACLRole::NONE, 1, &AOClient::pktRemoveEvidence}},
|
||||
{"EE", {ACLRole::NONE, 4, &AOClient::pktEditEvidence}},
|
||||
{"SETCASE", {ACLRole::NONE, 7, &AOClient::pktSetCase}},
|
||||
{"CASEA", {ACLRole::NONE, 6, &AOClient::pktAnnounceCase}},
|
||||
};
|
||||
|
||||
/**
|
||||
* @name Authentication
|
||||
*/
|
||||
@ -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:
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
|
@ -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:
|
||||
/**
|
||||
|
@ -23,6 +23,12 @@
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include "include/aoclient.h"
|
||||
#include "include/area_data.h"
|
||||
#include "include/packet/packet_info.h"
|
||||
|
||||
class AOClient;
|
||||
|
||||
/**
|
||||
* @brief An Attorney Online 2 compatible packet.
|
||||
*
|
||||
@ -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.
|
||||
*/
|
||||
|
@ -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.
|
||||
|
14
core/include/packet/packet_askchaa.h
Normal file
14
core/include/packet/packet_askchaa.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PACKET_ASKCHAA_H
|
||||
#define PACKET_ASKCHAA_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketAskchaa : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketAskchaa(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
};
|
||||
#endif
|
14
core/include/packet/packet_casea.h
Normal file
14
core/include/packet/packet_casea.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PACKET_CASEA_H
|
||||
#define PACKET_CASEA_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketCasea : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketCasea(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
};
|
||||
#endif
|
14
core/include/packet/packet_cc.h
Normal file
14
core/include/packet/packet_cc.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PACKET_CC_H
|
||||
#define PACKET_CC_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketCC : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketCC(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
};
|
||||
#endif
|
14
core/include/packet/packet_ch.h
Normal file
14
core/include/packet/packet_ch.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PACKET_CH_H
|
||||
#define PACKET_CH_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketCH : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketCH(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
};
|
||||
#endif
|
14
core/include/packet/packet_ct.h
Normal file
14
core/include/packet/packet_ct.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PACKET_CT_H
|
||||
#define PACKET_CT_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketCT : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketCT(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
};
|
||||
#endif
|
14
core/include/packet/packet_de.h
Normal file
14
core/include/packet/packet_de.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PACKET_DE_H
|
||||
#define PACKET_DE_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketDE : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketDE(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
};
|
||||
#endif
|
14
core/include/packet/packet_ee.h
Normal file
14
core/include/packet/packet_ee.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PACKET_EE_H
|
||||
#define PACKET_EE_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketEE : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketEE(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
};
|
||||
#endif
|
20
core/include/packet/packet_factory.h
Normal file
20
core/include/packet/packet_factory.h
Normal file
@ -0,0 +1,20 @@
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketFactory
|
||||
{
|
||||
public:
|
||||
// thingy here to register/map strings to constructors
|
||||
static AOPacket *createPacket(QString header, QStringList contents);
|
||||
static AOPacket *createPacket(QString raw_packet);
|
||||
template <typename T>
|
||||
static void registerClass(QString header) { class_map[header] = &createInstance<T>; };
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
static AOPacket *createInstance(QStringList contents) { return new T(contents); };
|
||||
template <typename T>
|
||||
static AOPacket *createInstance(QString header, QStringList contents) { return new T(header, contents); };
|
||||
typedef std::map<QString, AOPacket *(*)(QStringList)> type_map;
|
||||
|
||||
static inline type_map class_map;
|
||||
};
|
17
core/include/packet/packet_generic.h
Normal file
17
core/include/packet/packet_generic.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef PACKET_GENERIC_H
|
||||
#define PACKET_GENERIC_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketGeneric : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketGeneric(QString header, QStringList contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
|
||||
private:
|
||||
QString header;
|
||||
};
|
||||
#endif
|
17
core/include/packet/packet_hi.h
Normal file
17
core/include/packet/packet_hi.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef PACKET_HI_H
|
||||
#define PACKET_HI_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketHI : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketHI(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
|
||||
private:
|
||||
QString header;
|
||||
};
|
||||
#endif
|
14
core/include/packet/packet_hp.h
Normal file
14
core/include/packet/packet_hp.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PACKET_HP_H
|
||||
#define PACKET_HP_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketHP : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketHP(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
};
|
||||
#endif
|
14
core/include/packet/packet_id.h
Normal file
14
core/include/packet/packet_id.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PACKET_ID_H
|
||||
#define PACKET_ID_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketID : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketID(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
};
|
||||
#endif
|
14
core/include/packet/packet_info.h
Normal file
14
core/include/packet/packet_info.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PACKET_INFO_H
|
||||
#define PACKET_INFO_H
|
||||
|
||||
#include "include/acl_roles_handler.h"
|
||||
|
||||
/// Describes a packet's interpretation details.
|
||||
class PacketInfo
|
||||
{
|
||||
public:
|
||||
ACLRole::Permission acl_permission; //!< The permissions necessary for the packet.
|
||||
int min_args; //!< The minimum arguments needed for the packet to be interpreted correctly / make sense.
|
||||
QString header;
|
||||
};
|
||||
#endif
|
14
core/include/packet/packet_mc.h
Normal file
14
core/include/packet/packet_mc.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PACKET_MC_H
|
||||
#define PACKET_MC_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketMC : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketMC(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
};
|
||||
#endif
|
17
core/include/packet/packet_ms.h
Normal file
17
core/include/packet/packet_ms.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef PACKET_MS_H
|
||||
#define PACKET_MS_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketMS : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketMS(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
|
||||
private:
|
||||
AOPacket *validateIcPacket(AOClient &client) const;
|
||||
};
|
||||
#endif
|
14
core/include/packet/packet_pe.h
Normal file
14
core/include/packet/packet_pe.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PACKET_PE_H
|
||||
#define PACKET_PE_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketPE : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketPE(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
};
|
||||
#endif
|
14
core/include/packet/packet_pw.h
Normal file
14
core/include/packet/packet_pw.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PACKET_PW_H
|
||||
#define PACKET_PW_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketPW : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketPW(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
};
|
||||
#endif
|
14
core/include/packet/packet_rc.h
Normal file
14
core/include/packet/packet_rc.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PACKET_RC_H
|
||||
#define PACKET_RC_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketRC : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketRC(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
};
|
||||
#endif
|
14
core/include/packet/packet_rd.h
Normal file
14
core/include/packet/packet_rd.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PACKET_RD_H
|
||||
#define PACKET_RD_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketRD : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketRD(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
};
|
||||
#endif
|
14
core/include/packet/packet_rm.h
Normal file
14
core/include/packet/packet_rm.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PACKET_RM_H
|
||||
#define PACKET_RM_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketRM : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketRM(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
};
|
||||
#endif
|
14
core/include/packet/packet_rt.h
Normal file
14
core/include/packet/packet_rt.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PACKET_RT_H
|
||||
#define PACKET_RT_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketRT : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketRT(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
};
|
||||
#endif
|
14
core/include/packet/packet_setcase.h
Normal file
14
core/include/packet/packet_setcase.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PACKET_SETCASE_H
|
||||
#define PACKET_SETCASE_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketSetcase : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketSetcase(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
};
|
||||
#endif
|
14
core/include/packet/packet_zz.h
Normal file
14
core/include/packet/packet_zz.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PACKET_ZZ_H
|
||||
#define PACKET_ZZ_H
|
||||
|
||||
#include "include/network/aopacket.h"
|
||||
|
||||
class PacketZZ : public AOPacket
|
||||
{
|
||||
public:
|
||||
PacketZZ(QStringList &contents);
|
||||
virtual PacketInfo getPacketInfo() const;
|
||||
virtual void handlePacket(AreaData *area, AOClient &client) const;
|
||||
virtual bool validatePacket() const;
|
||||
};
|
||||
#endif
|
@ -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).
|
||||
|
@ -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<QString, AOClient::CommandInfo> 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);
|
||||
|
@ -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<int> 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<QString, float> l_song = m_music_manager->songInformation(l_song_name, index());
|
||||
emit sendAreaPacket(AOPacket("MC", {l_song.first, "-1"}), m_index);
|
||||
emit sendAreaPacket(PacketFactory::createPacket("MC", {l_song.first, "-1"}), m_index);
|
||||
m_jukebox_timer->start(l_song.second * 1000);
|
||||
}
|
||||
else {
|
||||
@ -658,7 +658,7 @@ void AreaData::switchJukeboxSong()
|
||||
l_song_name = m_jukebox_queue[l_random_index];
|
||||
|
||||
QPair<QString, float> l_song = m_music_manager->songInformation(l_song_name, index());
|
||||
emit sendAreaPacket(AOPacket("MC", {l_song.first, "-1"}), m_index);
|
||||
emit sendAreaPacket(PacketFactory::createPacket("MC", {l_song.first, "-1"}), m_index);
|
||||
m_jukebox_timer->start(l_song.second * 1000);
|
||||
|
||||
m_jukebox_queue.remove(l_random_index);
|
||||
|
@ -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();
|
||||
|
@ -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.");
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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") {
|
||||
|
@ -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<QString, QPair<QString, int>> 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);
|
||||
}
|
||||
|
@ -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<PacketAskchaa>("askchaa");
|
||||
PacketFactory::registerClass<PacketCasea>("CASEA");
|
||||
PacketFactory::registerClass<PacketCC>("CC");
|
||||
PacketFactory::registerClass<PacketCH>("CH");
|
||||
PacketFactory::registerClass<PacketCT>("CT");
|
||||
PacketFactory::registerClass<PacketDE>("DE");
|
||||
PacketFactory::registerClass<PacketEE>("EE");
|
||||
PacketFactory::registerClass<PacketHI>("HI");
|
||||
PacketFactory::registerClass<PacketHP>("HP");
|
||||
PacketFactory::registerClass<PacketID>("ID");
|
||||
PacketFactory::registerClass<PacketMC>("MC");
|
||||
PacketFactory::registerClass<PacketMS>("MS");
|
||||
PacketFactory::registerClass<PacketPE>("PE");
|
||||
PacketFactory::registerClass<PacketPW>("PW");
|
||||
PacketFactory::registerClass<PacketRC>("RC");
|
||||
PacketFactory::registerClass<PacketRD>("RD");
|
||||
PacketFactory::registerClass<PacketRM>("RM");
|
||||
PacketFactory::registerClass<PacketRT>("RT");
|
||||
PacketFactory::registerClass<PacketSetcase>("SETCASE");
|
||||
PacketFactory::registerClass<PacketZZ>("ZZ");
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
#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();
|
||||
}
|
||||
}
|
||||
|
36
core/src/packet/packet_askchaa.cpp
Normal file
36
core/src/packet/packet_askchaa.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
#include "include/packet/packet_askchaa.h"
|
||||
#include "include/config_manager.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketAskchaa::PacketAskchaa(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketAskchaa::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 0,
|
||||
.header = "askchaa"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketAskchaa::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
Q_UNUSED(area)
|
||||
// Evidence isn't loaded during this part anymore
|
||||
// As a result, we can always send "0" for evidence length
|
||||
// Client only cares about what it gets from LE
|
||||
client.sendPacket("SI", {QString::number(client.getServer()->getCharacterCount()), "0", QString::number(client.getServer()->getAreaCount() + client.getServer()->getMusicList().length())});
|
||||
}
|
||||
|
||||
bool PacketAskchaa::validatePacket() const
|
||||
{
|
||||
if (m_content.size() > 0) { // Too many arguments.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
82
core/src/packet/packet_casea.cpp
Normal file
82
core/src/packet/packet_casea.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
#include "include/packet/packet_casea.h"
|
||||
#include "include/akashiutils.h"
|
||||
#include "include/packet/packet_factory.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketCasea::PacketCasea(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketCasea::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 6,
|
||||
.header = "Casea"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketCasea::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
Q_UNUSED(area)
|
||||
|
||||
QString l_case_title = m_content[0];
|
||||
QStringList l_needed_roles;
|
||||
QList<bool> l_needs_list;
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
bool is_int = false;
|
||||
bool need = m_content[i].toInt(&is_int);
|
||||
if (!is_int)
|
||||
return;
|
||||
l_needs_list.append(need);
|
||||
}
|
||||
QStringList l_roles = {"defense attorney", "prosecutor", "judge", "jurors", "stenographer"};
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (l_needs_list[i])
|
||||
l_needed_roles.append(l_roles[i]);
|
||||
}
|
||||
if (l_needed_roles.isEmpty())
|
||||
return;
|
||||
|
||||
QString l_message = "=== Case Announcement ===\r\n" + (client.m_ooc_name == "" ? client.m_current_char : client.m_ooc_name) + " needs " + l_needed_roles.join(", ") + " for " + (l_case_title == "" ? "a case" : l_case_title) + "!";
|
||||
|
||||
QList<AOClient *> l_clients_to_alert;
|
||||
// here lies morton, RIP
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
QSet<bool> l_needs_set(l_needs_list.begin(), l_needs_list.end());
|
||||
#else
|
||||
QSet<bool> l_needs_set = l_needs_list.toSet();
|
||||
#endif
|
||||
const QVector<AOClient *> l_clients = client.getServer()->getClients();
|
||||
for (AOClient *l_client : l_clients) {
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
QSet<bool> l_matches(l_client->m_casing_preferences.begin(), l_client->m_casing_preferences.end());
|
||||
l_matches.intersect(l_needs_set);
|
||||
#else
|
||||
QSet<bool> l_matches = l_client->m_casing_preferences.toSet().intersect(l_needs_set);
|
||||
#endif
|
||||
if (!l_matches.isEmpty() && !l_clients_to_alert.contains(l_client))
|
||||
l_clients_to_alert.append(l_client);
|
||||
}
|
||||
|
||||
for (AOClient *l_client : l_clients_to_alert) {
|
||||
l_client->sendPacket(PacketFactory::createPacket("CASEA", {l_message, m_content[1], m_content[2], m_content[3], m_content[4], m_content[5], "1"}));
|
||||
// you may be thinking, "hey wait a minute the network protocol documentation doesn't mention that last argument!"
|
||||
// if you are in fact thinking that, you are correct! it is not in the documentation!
|
||||
// however for some inscrutable reason Attorney Online 2 will outright reject a CASEA packet that does not have
|
||||
// at least 7 arguments despite only using the first 6. Cera, i kneel. you have truly broken me.
|
||||
}
|
||||
}
|
||||
|
||||
bool PacketCasea::validatePacket() const
|
||||
{
|
||||
for (int i = 1; i < m_content.size(); i++) {
|
||||
if (!AkashiUtils::checkArgType<int>(m_content.at(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
48
core/src/packet/packet_cc.cpp
Normal file
48
core/src/packet/packet_cc.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
#include "include/packet/packet_cc.h"
|
||||
#include "include/akashiutils.h"
|
||||
#include "include/config_manager.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketCC::PacketCC(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketCC::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 3,
|
||||
.header = "CC"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketCC::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
Q_UNUSED(area)
|
||||
|
||||
bool argument_ok;
|
||||
int l_selected_char_id = m_content[1].toInt(&argument_ok);
|
||||
if (!argument_ok) {
|
||||
l_selected_char_id = client.SPECTATOR_ID;
|
||||
}
|
||||
|
||||
if (l_selected_char_id < -1 || l_selected_char_id > client.getServer()->getCharacters().size() - 1) {
|
||||
client.sendPacket("KK", {"A protocol error has been encountered.Packet : CC\nCharacter ID out of range."});
|
||||
client.m_socket->close();
|
||||
}
|
||||
|
||||
if (client.changeCharacter(l_selected_char_id))
|
||||
client.m_char_id = l_selected_char_id;
|
||||
|
||||
if (client.m_char_id > client.SPECTATOR_ID) {
|
||||
client.setSpectator(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool PacketCC::validatePacket() const
|
||||
{
|
||||
return AkashiUtils::checkArgType<int>(m_content.at(1));
|
||||
}
|
33
core/src/packet/packet_ch.cpp
Normal file
33
core/src/packet/packet_ch.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include "include/packet/packet_ch.h"
|
||||
#include "include/akashiutils.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketCH::PacketCH(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketCH::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 1,
|
||||
.header = "CH"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketCH::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
Q_UNUSED(area)
|
||||
// Why does this packet exist
|
||||
// At least Crystal made it useful
|
||||
// It is now used for ping measurement
|
||||
client.sendPacket("CHECK");
|
||||
}
|
||||
|
||||
bool PacketCH::validatePacket() const
|
||||
{
|
||||
return AkashiUtils::checkArgType<int>(m_content.at(0));
|
||||
}
|
69
core/src/packet/packet_ct.cpp
Normal file
69
core/src/packet/packet_ct.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
#include "include/packet/packet_ct.h"
|
||||
#include "include/akashidefs.h"
|
||||
#include "include/config_manager.h"
|
||||
#include "include/packet/packet_factory.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketCT::PacketCT(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketCT::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 2,
|
||||
.header = "CT"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketCT::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
if (client.m_is_ooc_muted) {
|
||||
client.sendServerMessage("You are OOC muted, and cannot speak.");
|
||||
return;
|
||||
}
|
||||
|
||||
client.m_ooc_name = client.dezalgo(m_content[0]).replace(QRegExp("\\[|\\]|\\{|\\}|\\#|\\$|\\%|\\&"), ""); // no fucky wucky shit here
|
||||
if (client.m_ooc_name.isEmpty() || client.m_ooc_name == ConfigManager::serverName()) // impersonation & empty name protection
|
||||
return;
|
||||
|
||||
if (client.m_ooc_name.length() > 30) {
|
||||
client.sendServerMessage("Your name is too long! Please limit it to under 30 characters.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (client.m_is_logging_in) {
|
||||
client.loginAttempt(m_content[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
QString l_message = client.dezalgo(m_content[1]);
|
||||
if (l_message.length() == 0 || l_message.length() > ConfigManager::maxCharacters())
|
||||
return;
|
||||
AOPacket *final_packet = PacketFactory::createPacket("CT", {client.m_ooc_name, l_message, "0"});
|
||||
if (l_message.at(0) == '/') {
|
||||
QStringList l_cmd_argv = l_message.split(" ", akashi::SkipEmptyParts);
|
||||
QString l_command = l_cmd_argv[0].trimmed().toLower();
|
||||
l_command = l_command.right(l_command.length() - 1);
|
||||
l_cmd_argv.removeFirst();
|
||||
int l_cmd_argc = l_cmd_argv.length();
|
||||
|
||||
client.handleCommand(l_command, l_cmd_argc, l_cmd_argv);
|
||||
emit client.logCMD((client.m_current_char + " " + client.m_showname), client.m_ipid, client.m_ooc_name, l_command, l_cmd_argv, client.getServer()->getAreaById(client.m_current_area)->name());
|
||||
return;
|
||||
}
|
||||
else {
|
||||
client.getServer()->broadcast(final_packet, client.m_current_area);
|
||||
}
|
||||
emit client.logOOC((client.m_current_char + " " + client.m_showname), client.m_ooc_name, client.m_ipid, area->name(), l_message);
|
||||
}
|
||||
|
||||
bool PacketCT::validatePacket() const
|
||||
{
|
||||
// Nothing to validate.
|
||||
return true;
|
||||
}
|
36
core/src/packet/packet_de.cpp
Normal file
36
core/src/packet/packet_de.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
#include "include/packet/packet_de.h"
|
||||
#include "include/akashiutils.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketDE::PacketDE(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketDE::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 1,
|
||||
.header = "DE"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketDE::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
if (!client.checkEvidenceAccess(area))
|
||||
return;
|
||||
bool is_int = false;
|
||||
int l_idx = m_content[0].toInt(&is_int);
|
||||
if (is_int && l_idx < area->evidence().size() && l_idx >= 0) {
|
||||
area->deleteEvidence(l_idx);
|
||||
}
|
||||
client.sendEvidenceList(area);
|
||||
}
|
||||
|
||||
bool PacketDE::validatePacket() const
|
||||
{
|
||||
return AkashiUtils::checkArgType<int>(m_content.at(0));
|
||||
}
|
37
core/src/packet/packet_ee.cpp
Normal file
37
core/src/packet/packet_ee.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
#include "include/packet/packet_ee.h"
|
||||
#include "include/akashiutils.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketEE::PacketEE(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketEE::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 4,
|
||||
.header = "EE"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketEE::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
if (!client.checkEvidenceAccess(area))
|
||||
return;
|
||||
bool is_int = false;
|
||||
int l_idx = m_content[0].toInt(&is_int);
|
||||
AreaData::Evidence l_evi = {m_content[1], m_content[2], m_content[3]};
|
||||
if (is_int && l_idx < area->evidence().size() && l_idx >= 0) {
|
||||
area->replaceEvidence(l_idx, l_evi);
|
||||
}
|
||||
client.sendEvidenceList(area);
|
||||
}
|
||||
|
||||
bool PacketEE::validatePacket() const
|
||||
{
|
||||
return AkashiUtils::checkArgType<int>(m_content.at(0));
|
||||
}
|
34
core/src/packet/packet_factory.cpp
Normal file
34
core/src/packet/packet_factory.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
#include "include/packet/packet_factory.h"
|
||||
#include "include/packet/packet_generic.h"
|
||||
|
||||
AOPacket *PacketFactory::createPacket(QString header, QStringList contents)
|
||||
{
|
||||
if (!class_map.count(header)) {
|
||||
return createInstance<PacketGeneric>(header, contents);
|
||||
}
|
||||
|
||||
return class_map[header](contents);
|
||||
}
|
||||
|
||||
AOPacket *PacketFactory::createPacket(QString raw_packet)
|
||||
{
|
||||
QString header;
|
||||
QStringList contents;
|
||||
|
||||
if (raw_packet.at(0) == '#' || raw_packet.contains("%") || raw_packet.isEmpty()) {
|
||||
qDebug() << "FantaCrypt or otherwise invalid packet received";
|
||||
return PacketFactory::createPacket("Unknown", {"Unknown"});
|
||||
}
|
||||
|
||||
QStringList packet_contents = raw_packet.split("#");
|
||||
header = packet_contents[0];
|
||||
|
||||
packet_contents.removeFirst(); // Remove header
|
||||
packet_contents.removeLast(); // Remove anything trailing after delimiter
|
||||
contents = packet_contents;
|
||||
|
||||
AOPacket *packet = PacketFactory::createPacket(header, contents);
|
||||
packet->unescapeContent();
|
||||
|
||||
return packet;
|
||||
}
|
31
core/src/packet/packet_generic.cpp
Normal file
31
core/src/packet/packet_generic.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
#include "include/packet/packet_generic.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketGeneric::PacketGeneric(QString header, QStringList contents) :
|
||||
AOPacket(contents),
|
||||
header(header)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketGeneric::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 0,
|
||||
.header = header};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketGeneric::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
Q_UNUSED(area)
|
||||
Q_UNUSED(client)
|
||||
qDebug() << "ERROR: Cannot handle generic packet: " << header;
|
||||
qDebug() << "Packet is either unimplemented, or is meant to be sent to client";
|
||||
}
|
||||
|
||||
bool PacketGeneric::validatePacket() const
|
||||
{
|
||||
return true;
|
||||
}
|
50
core/src/packet/packet_hi.cpp
Normal file
50
core/src/packet/packet_hi.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include "include/packet/packet_hi.h"
|
||||
#include "include/akashiutils.h"
|
||||
#include "include/db_manager.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketHI::PacketHI(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketHI::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 1,
|
||||
.header = "HI"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketHI::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
Q_UNUSED(area)
|
||||
|
||||
QString incoming_hwid = m_content[0];
|
||||
if (incoming_hwid.isEmpty() || !client.m_hwid.isEmpty()) {
|
||||
// No double sending or empty HWIDs!
|
||||
client.sendPacket("BD", {"A protocol error has been encountered. Packet : HI"});
|
||||
client.m_socket->close();
|
||||
return;
|
||||
}
|
||||
|
||||
client.m_hwid = incoming_hwid;
|
||||
emit client.getServer()->logConnectionAttempt(client.m_remote_ip.toString(), client.m_ipid, client.m_hwid);
|
||||
auto ban = client.getServer()->getDatabaseManager()->isHDIDBanned(client.m_hwid);
|
||||
if (ban.first) {
|
||||
client.sendPacket("BD", {ban.second + "\nBan ID: " + QString::number(client.getServer()->getDatabaseManager()->getBanID(client.m_hwid))});
|
||||
client.m_socket->close();
|
||||
return;
|
||||
}
|
||||
|
||||
client.sendPacket("ID", {QString::number(client.m_id), "akashi", QCoreApplication::applicationVersion()});
|
||||
}
|
||||
|
||||
bool PacketHI::validatePacket() const
|
||||
{
|
||||
// We can always convert a string to a string. No point in checking.
|
||||
return true;
|
||||
}
|
50
core/src/packet/packet_hp.cpp
Normal file
50
core/src/packet/packet_hp.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include "include/packet/packet_hp.h"
|
||||
#include "include/akashiutils.h"
|
||||
#include "include/packet/packet_factory.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketHP::PacketHP(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketHP::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 2,
|
||||
.header = "HP"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketHP::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
if (client.m_is_wtce_blocked) {
|
||||
client.sendServerMessage("You are blocked from using the judge controls.");
|
||||
return;
|
||||
}
|
||||
int l_newValue = m_content.at(1).toInt();
|
||||
|
||||
if (m_content[0] == "1") {
|
||||
area->changeHP(AreaData::Side::DEFENCE, l_newValue);
|
||||
}
|
||||
else if (m_content[0] == "2") {
|
||||
area->changeHP(AreaData::Side::PROSECUTOR, l_newValue);
|
||||
}
|
||||
|
||||
client.getServer()->broadcast(PacketFactory::createPacket("HP", {"1", QString::number(area->defHP())}), area->index());
|
||||
client.getServer()->broadcast(PacketFactory::createPacket("HP", {"2", QString::number(area->proHP())}), area->index());
|
||||
|
||||
client.updateJudgeLog(area, &client, "updated the penalties");
|
||||
}
|
||||
|
||||
bool PacketHP::validatePacket() const
|
||||
{
|
||||
if (!AkashiUtils::checkArgType<int>(m_content.at(0)))
|
||||
return false;
|
||||
if (!AkashiUtils::checkArgType<int>(m_content.at(1)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
76
core/src/packet/packet_id.cpp
Normal file
76
core/src/packet/packet_id.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
#include "include/packet/packet_id.h"
|
||||
#include "include/config_manager.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketID::PacketID(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketID::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 2,
|
||||
.header = "ID"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketID::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
Q_UNUSED(area)
|
||||
|
||||
if (client.m_version.major == 2) {
|
||||
// No double sending of the ID packet!
|
||||
client.sendPacket("BD", {"A protocol error has been encountered. Packet : ID"});
|
||||
client.m_socket->close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Full feature list as of AO 2.8.5
|
||||
// The only ones that are critical to ensuring the server works are
|
||||
// "noencryption" and "fastloading"
|
||||
QStringList l_feature_list = {
|
||||
"noencryption", "yellowtext", "prezoom",
|
||||
"flipping", "customobjections", "fastloading",
|
||||
"deskmod", "evidence", "cccc_ic_support",
|
||||
"arup", "casing_alerts", "modcall_reason",
|
||||
"looping_sfx", "additive", "effects",
|
||||
"y_offset", "expanded_desk_mods", "auth_packet"};
|
||||
|
||||
client.m_version.string = m_content[1];
|
||||
QRegularExpression rx("\\b(\\d+)\\.(\\d+)\\.(\\d+)\\b"); // matches X.X.X (e.g. 2.9.0, 2.4.10, etc.)
|
||||
QRegularExpressionMatch l_match = rx.match(client.m_version.string);
|
||||
if (l_match.hasMatch()) {
|
||||
client.m_version.release = l_match.captured(1).toInt();
|
||||
client.m_version.major = l_match.captured(2).toInt();
|
||||
client.m_version.minor = l_match.captured(3).toInt();
|
||||
}
|
||||
if (m_content[0] == "webAO") {
|
||||
client.m_version.release = 2;
|
||||
client.m_version.major = 10;
|
||||
client.m_version.minor = 0;
|
||||
}
|
||||
|
||||
if (client.m_version.release != 2) {
|
||||
// No valid ID packet resolution.
|
||||
client.sendPacket("BD", {"A protocol error has been encountered. Packet : ID\nMajor version not recognised."});
|
||||
client.m_socket->close();
|
||||
return;
|
||||
}
|
||||
|
||||
client.sendPacket("PN", {QString::number(client.getServer()->getPlayerCount()), QString::number(ConfigManager::maxPlayers()), ConfigManager::serverDescription()});
|
||||
client.sendPacket("FL", l_feature_list);
|
||||
|
||||
if (ConfigManager::assetUrl().isValid()) {
|
||||
QByteArray l_asset_url = ConfigManager::assetUrl().toEncoded(QUrl::EncodeSpaces);
|
||||
client.sendPacket("ASS", {l_asset_url});
|
||||
}
|
||||
}
|
||||
|
||||
bool PacketID::validatePacket() const
|
||||
{
|
||||
return true;
|
||||
}
|
98
core/src/packet/packet_mc.cpp
Normal file
98
core/src/packet/packet_mc.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
#include "include/packet/packet_mc.h"
|
||||
#include "include/music_manager.h"
|
||||
#include "include/packet/packet_factory.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketMC::PacketMC(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketMC::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 2,
|
||||
.header = "MC"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketMC::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
// Due to historical reasons, this
|
||||
// packet has two functions:
|
||||
// Change area, and set music.
|
||||
|
||||
// First, we check if the provided
|
||||
// argument is a valid song
|
||||
QString l_argument = m_content[0];
|
||||
|
||||
if (client.getServer()->getMusicList().contains(l_argument) || client.m_music_manager->isCustom(client.m_current_area, l_argument) || l_argument == "~stop.mp3") { // ~stop.mp3 is a dummy track used by 2.9+
|
||||
// We have a song here
|
||||
|
||||
if (client.m_is_spectator) {
|
||||
client.sendServerMessage("Spectator are blocked from changing the music.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (client.m_is_dj_blocked) {
|
||||
client.sendServerMessage("You are blocked from changing the music.");
|
||||
return;
|
||||
}
|
||||
if (!area->isMusicAllowed() && !client.checkPermission(ACLRole::CM)) {
|
||||
client.sendServerMessage("Music is disabled in this area.");
|
||||
return;
|
||||
}
|
||||
QString l_effects;
|
||||
if (m_content.length() >= 4)
|
||||
l_effects = m_content[3];
|
||||
else
|
||||
l_effects = "0";
|
||||
QString l_final_song;
|
||||
|
||||
// As categories can be used to stop music we need to check if it has a dot for the extension. If not, we assume its a category.
|
||||
if (!l_argument.contains("."))
|
||||
l_final_song = "~stop.mp3";
|
||||
else
|
||||
l_final_song = l_argument;
|
||||
|
||||
// Jukebox intercepts the direct playing of messages.
|
||||
if (area->isjukeboxEnabled()) {
|
||||
QString l_jukebox_reply = area->addJukeboxSong(l_final_song);
|
||||
client.sendServerMessage(l_jukebox_reply);
|
||||
return;
|
||||
}
|
||||
|
||||
if (l_final_song != "~stop.mp3") {
|
||||
// We might have an aliased song. We check for its real songname and send it to the clients.
|
||||
QPair<QString, float> l_song = client.m_music_manager->songInformation(l_final_song, client.m_current_area);
|
||||
l_final_song = l_song.first;
|
||||
}
|
||||
AOPacket *l_music_change = PacketFactory::createPacket("MC", {l_final_song, m_content[1], client.m_showname, "1", "0", l_effects});
|
||||
client.getServer()->broadcast(l_music_change, client.m_current_area);
|
||||
|
||||
// Since we can't ensure a user has their showname set, we check if its empty to prevent
|
||||
//"played by ." in /currentmusic.
|
||||
if (client.m_showname.isEmpty()) {
|
||||
area->changeMusic(client.m_current_char, l_final_song);
|
||||
return;
|
||||
}
|
||||
area->changeMusic(client.m_showname, l_final_song);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < client.getServer()->getAreaCount(); i++) {
|
||||
QString l_area = client.getServer()->getAreaName(i);
|
||||
if (l_area == l_argument) {
|
||||
client.changeArea(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PacketMC::validatePacket() const
|
||||
{
|
||||
return true;
|
||||
}
|
436
core/src/packet/packet_ms.cpp
Normal file
436
core/src/packet/packet_ms.cpp
Normal file
@ -0,0 +1,436 @@
|
||||
#include "include/packet/packet_ms.h"
|
||||
#include "include/config_manager.h"
|
||||
#include "include/packet/packet_factory.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketMS::PacketMS(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketMS::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 15,
|
||||
.header = "MS"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketMS::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
if (client.m_is_muted) {
|
||||
client.sendServerMessage("You cannot speak while muted.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!area->isMessageAllowed() || !client.getServer()->isMessageAllowed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
AOPacket *validated_packet = validateIcPacket(client);
|
||||
if (validated_packet->getPacketInfo().header == "INVALID")
|
||||
return;
|
||||
|
||||
if (client.m_pos != "")
|
||||
validated_packet->setContentField(5, client.m_pos);
|
||||
|
||||
client.getServer()->broadcast(validated_packet, client.m_current_area);
|
||||
emit client.logIC((client.m_current_char + " " + client.m_showname), client.m_ooc_name, client.m_ipid, client.getServer()->getAreaById(client.m_current_area)->name(), client.m_last_message);
|
||||
area->updateLastICMessage(validated_packet->getContent());
|
||||
|
||||
area->startMessageFloodguard(ConfigManager::messageFloodguard());
|
||||
client.getServer()->startMessageFloodguard(ConfigManager::globalMessageFloodguard());
|
||||
}
|
||||
|
||||
AOPacket *PacketMS::validateIcPacket(AOClient &client) const
|
||||
{
|
||||
// Welcome to the super cursed server-side IC chat validation hell
|
||||
|
||||
// I wanted to use enums or #defines here to make the
|
||||
// indicies of the args arrays more readable. But,
|
||||
// in typical AO fasion, the indicies for the incoming
|
||||
// and outgoing packets are different. Just RTFM.
|
||||
|
||||
// This packet can be sent with a minimum required args of 15.
|
||||
// 2.6+ extensions raise this to 19, and 2.8 further raises this to 26.
|
||||
|
||||
AOPacket *l_invalid = PacketFactory::createPacket("INVALID", {});
|
||||
QStringList l_args;
|
||||
if (client.isSpectator() || client.m_current_char.isEmpty() || !client.m_joined)
|
||||
// Spectators cannot use IC
|
||||
return l_invalid;
|
||||
AreaData *area = client.getServer()->getAreaById(client.m_current_area);
|
||||
if (area->lockStatus() == AreaData::LockStatus::SPECTATABLE && !area->invited().contains(client.m_id) && !client.checkPermission(ACLRole::BYPASS_LOCKS))
|
||||
// Non-invited players cannot speak in spectatable areas
|
||||
return l_invalid;
|
||||
|
||||
QList<QVariant> l_incoming_args;
|
||||
for (const QString &l_arg : m_content) {
|
||||
l_incoming_args.append(QVariant(l_arg));
|
||||
}
|
||||
|
||||
// desk modifier
|
||||
QStringList allowed_desk_mods;
|
||||
allowed_desk_mods << "chat"
|
||||
<< "0"
|
||||
<< "1"
|
||||
<< "2"
|
||||
<< "3"
|
||||
<< "4"
|
||||
<< "5";
|
||||
QString l_incoming_deskmod = l_incoming_args[0].toString();
|
||||
if (allowed_desk_mods.contains(l_incoming_deskmod)) {
|
||||
// **WARNING : THIS IS A HACK!**
|
||||
// A proper solution would be to deprecate chat as an argument on the clientside
|
||||
// instead of overwriting correct netcode behaviour on the serverside.
|
||||
if (l_incoming_deskmod == "chat") {
|
||||
l_args.append("1");
|
||||
}
|
||||
else {
|
||||
l_args.append(l_incoming_args[0].toString());
|
||||
}
|
||||
}
|
||||
else
|
||||
return l_invalid;
|
||||
|
||||
// preanim
|
||||
l_args.append(l_incoming_args[1].toString());
|
||||
|
||||
// char name
|
||||
if (client.m_current_char.toLower() != l_incoming_args[2].toString().toLower()) {
|
||||
// Selected char is different from supplied folder name
|
||||
// This means the user is INI-swapped
|
||||
if (!area->iniswapAllowed()) {
|
||||
if (!client.getServer()->getCharacters().contains(l_incoming_args[2].toString(), Qt::CaseInsensitive))
|
||||
return l_invalid;
|
||||
}
|
||||
qDebug() << "INI swap detected from " << client.getIpid();
|
||||
}
|
||||
client.m_current_iniswap = l_incoming_args[2].toString();
|
||||
l_args.append(l_incoming_args[2].toString());
|
||||
|
||||
// emote
|
||||
client.m_emote = l_incoming_args[3].toString();
|
||||
if (client.m_first_person)
|
||||
client.m_emote = "";
|
||||
l_args.append(client.m_emote);
|
||||
|
||||
// message text
|
||||
if (l_incoming_args[4].toString().size() > ConfigManager::maxCharacters())
|
||||
return l_invalid;
|
||||
|
||||
QString l_incoming_msg = client.dezalgo(l_incoming_args[4].toString().trimmed());
|
||||
if (!area->lastICMessage().isEmpty() && l_incoming_msg == area->lastICMessage()[4] && l_incoming_msg != "")
|
||||
return l_invalid;
|
||||
|
||||
if (l_incoming_msg == "" && area->blankpostingAllowed() == false) {
|
||||
client.sendServerMessage("Blankposting has been forbidden in this area.");
|
||||
return l_invalid;
|
||||
}
|
||||
|
||||
if (client.m_is_gimped) {
|
||||
QString l_gimp_message = ConfigManager::gimpList().at((client.genRand(1, ConfigManager::gimpList().size() - 1)));
|
||||
l_incoming_msg = l_gimp_message;
|
||||
}
|
||||
|
||||
if (client.m_is_shaken) {
|
||||
QStringList l_parts = l_incoming_msg.split(" ");
|
||||
|
||||
std::random_device rng;
|
||||
std::mt19937 urng(rng());
|
||||
std::shuffle(l_parts.begin(), l_parts.end(), urng);
|
||||
|
||||
l_incoming_msg = l_parts.join(" ");
|
||||
}
|
||||
|
||||
if (client.m_is_disemvoweled) {
|
||||
QString l_disemvoweled_message = l_incoming_msg.remove(QRegExp("[AEIOUaeiou]"));
|
||||
l_incoming_msg = l_disemvoweled_message;
|
||||
}
|
||||
|
||||
client.m_last_message = l_incoming_msg;
|
||||
l_args.append(l_incoming_msg);
|
||||
|
||||
// side
|
||||
// this is validated clientside so w/e
|
||||
l_args.append(l_incoming_args[5].toString());
|
||||
if (client.m_pos != l_incoming_args[5].toString()) {
|
||||
client.m_pos = l_incoming_args[5].toString();
|
||||
client.updateEvidenceList(client.getServer()->getAreaById(client.m_current_area));
|
||||
}
|
||||
|
||||
// sfx name
|
||||
l_args.append(l_incoming_args[6].toString());
|
||||
|
||||
// emote modifier
|
||||
// Now, gather round, y'all. Here is a story that is truly a microcosm of the AO dev experience.
|
||||
// If this value is a 4, it will crash the client. Why? Who knows, but it does.
|
||||
// Now here is the kicker: in certain versions, the client would incorrectly send a 4 here
|
||||
// For a long time, by configuring the client to do a zoom with a preanim, it would send 4
|
||||
// This would crash everyone else's client, and the feature had to be disabled
|
||||
// But, for some reason, nobody traced the cause of this issue for many many years.
|
||||
// The serverside fix is needed to ensure invalid values are not sent, because the client sucks
|
||||
int emote_mod = l_incoming_args[7].toInt();
|
||||
|
||||
if (emote_mod == 4)
|
||||
emote_mod = 6;
|
||||
if (emote_mod != 0 && emote_mod != 1 && emote_mod != 2 && emote_mod != 5 && emote_mod != 6)
|
||||
return l_invalid;
|
||||
l_args.append(QString::number(emote_mod));
|
||||
|
||||
// char id
|
||||
if (l_incoming_args[8].toInt() != client.m_char_id)
|
||||
return l_invalid;
|
||||
l_args.append(l_incoming_args[8].toString());
|
||||
|
||||
// sfx delay
|
||||
l_args.append(l_incoming_args[9].toString());
|
||||
|
||||
// objection modifier
|
||||
if (area->isShoutAllowed()) {
|
||||
if (l_incoming_args[10].toString().contains("4")) {
|
||||
// custom shout includes text metadata
|
||||
l_args.append(l_incoming_args[10].toString());
|
||||
}
|
||||
else {
|
||||
int l_obj_mod = l_incoming_args[10].toInt();
|
||||
if ((l_obj_mod < 0) || (l_obj_mod > 4)) {
|
||||
return l_invalid;
|
||||
}
|
||||
l_args.append(QString::number(l_obj_mod));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (l_incoming_args[10].toString() != "0") {
|
||||
client.sendServerMessage("Shouts have been disabled in this area.");
|
||||
}
|
||||
l_args.append("0");
|
||||
}
|
||||
|
||||
// evidence
|
||||
int evi_idx = l_incoming_args[11].toInt();
|
||||
if (evi_idx > area->evidence().length())
|
||||
return l_invalid;
|
||||
l_args.append(QString::number(evi_idx));
|
||||
|
||||
// flipping
|
||||
int l_flip = l_incoming_args[12].toInt();
|
||||
if (l_flip != 0 && l_flip != 1)
|
||||
return l_invalid;
|
||||
client.m_flipping = QString::number(l_flip);
|
||||
l_args.append(client.m_flipping);
|
||||
|
||||
// realization
|
||||
int realization = l_incoming_args[13].toInt();
|
||||
if (realization != 0 && realization != 1)
|
||||
return l_invalid;
|
||||
l_args.append(QString::number(realization));
|
||||
|
||||
// text color
|
||||
int text_color = l_incoming_args[14].toInt();
|
||||
if (text_color < 0 || text_color > 11)
|
||||
return l_invalid;
|
||||
l_args.append(QString::number(text_color));
|
||||
|
||||
// 2.6 packet extensions
|
||||
if (l_incoming_args.length() >= 19) {
|
||||
// showname
|
||||
QString l_incoming_showname = client.dezalgo(l_incoming_args[15].toString().trimmed());
|
||||
if (!(l_incoming_showname == client.m_current_char || l_incoming_showname.isEmpty()) && !area->shownameAllowed()) {
|
||||
client.sendServerMessage("Shownames are not allowed in this area!");
|
||||
return l_invalid;
|
||||
}
|
||||
if (l_incoming_showname.length() > 30) {
|
||||
client.sendServerMessage("Your showname is too long! Please limit it to under 30 characters");
|
||||
return l_invalid;
|
||||
}
|
||||
|
||||
// if the raw input is not empty but the trimmed input is, use a single space
|
||||
if (l_incoming_showname.isEmpty() && !l_incoming_args[15].toString().isEmpty())
|
||||
l_incoming_showname = " ";
|
||||
l_args.append(l_incoming_showname);
|
||||
client.m_showname = l_incoming_showname;
|
||||
|
||||
// other char id
|
||||
// things get a bit hairy here
|
||||
// don't ask me how this works, because i don't know either
|
||||
QStringList l_pair_data = l_incoming_args[16].toString().split("^");
|
||||
client.m_pairing_with = l_pair_data[0].toInt();
|
||||
QString l_front_back = "";
|
||||
if (l_pair_data.length() > 1)
|
||||
l_front_back = "^" + l_pair_data[1];
|
||||
int l_other_charid = client.m_pairing_with;
|
||||
bool l_pairing = false;
|
||||
QString l_other_name = "0";
|
||||
QString l_other_emote = "0";
|
||||
QString l_other_offset = "0";
|
||||
QString l_other_flip = "0";
|
||||
for (int l_client_id : area->joinedIDs()) {
|
||||
AOClient *l_client = client.getServer()->getClientByID(l_client_id);
|
||||
if (l_client->m_pairing_with == client.m_char_id && l_other_charid != client.m_char_id && l_client->m_char_id == client.m_pairing_with && l_client->m_pos == client.m_pos) {
|
||||
l_other_name = l_client->m_current_iniswap;
|
||||
l_other_emote = l_client->m_emote;
|
||||
l_other_offset = l_client->m_offset;
|
||||
l_other_flip = l_client->m_flipping;
|
||||
l_pairing = true;
|
||||
}
|
||||
}
|
||||
if (!l_pairing) {
|
||||
l_other_charid = -1;
|
||||
l_front_back = "";
|
||||
}
|
||||
l_args.append(QString::number(l_other_charid) + l_front_back);
|
||||
l_args.append(l_other_name);
|
||||
l_args.append(l_other_emote);
|
||||
|
||||
// self offset
|
||||
client.m_offset = l_incoming_args[17].toString();
|
||||
// versions 2.6-2.8 cannot validate y-offset so we send them just the x-offset
|
||||
if ((client.m_version.release == 2) && (client.m_version.major == 6 || client.m_version.major == 7 || client.m_version.major == 8)) {
|
||||
QString l_x_offset = client.m_offset.split("&")[0];
|
||||
l_args.append(l_x_offset);
|
||||
QString l_other_x_offset = l_other_offset.split("&")[0];
|
||||
l_args.append(l_other_x_offset);
|
||||
}
|
||||
else {
|
||||
l_args.append(client.m_offset);
|
||||
l_args.append(l_other_offset);
|
||||
}
|
||||
l_args.append(l_other_flip);
|
||||
|
||||
// immediate text processing
|
||||
int l_immediate = l_incoming_args[18].toInt();
|
||||
if (area->forceImmediate()) {
|
||||
if (l_args[7] == "1" || l_args[7] == "2") {
|
||||
l_args[7] = "0";
|
||||
l_immediate = 1;
|
||||
}
|
||||
else if (l_args[7] == "6") {
|
||||
l_args[7] = "5";
|
||||
l_immediate = 1;
|
||||
}
|
||||
}
|
||||
if (l_immediate != 1 && l_immediate != 0)
|
||||
return l_invalid;
|
||||
l_args.append(QString::number(l_immediate));
|
||||
}
|
||||
|
||||
// 2.8 packet extensions
|
||||
if (l_incoming_args.length() >= 26) {
|
||||
// sfx looping
|
||||
int l_sfx_loop = l_incoming_args[19].toInt();
|
||||
if (l_sfx_loop != 0 && l_sfx_loop != 1)
|
||||
return l_invalid;
|
||||
l_args.append(QString::number(l_sfx_loop));
|
||||
|
||||
// screenshake
|
||||
int l_screenshake = l_incoming_args[20].toInt();
|
||||
if (l_screenshake != 0 && l_screenshake != 1)
|
||||
return l_invalid;
|
||||
l_args.append(QString::number(l_screenshake));
|
||||
|
||||
// frames shake
|
||||
l_args.append(l_incoming_args[21].toString());
|
||||
|
||||
// frames realization
|
||||
l_args.append(l_incoming_args[22].toString());
|
||||
|
||||
// frames sfx
|
||||
l_args.append(l_incoming_args[23].toString());
|
||||
|
||||
// additive
|
||||
int l_additive = l_incoming_args[24].toInt();
|
||||
if (l_additive != 0 && l_additive != 1)
|
||||
return l_invalid;
|
||||
else if (area->lastICMessage().isEmpty()) {
|
||||
l_additive = 0;
|
||||
}
|
||||
else if (!(client.m_char_id == area->lastICMessage()[8].toInt())) {
|
||||
l_additive = 0;
|
||||
}
|
||||
else if (l_additive == 1) {
|
||||
l_args[4].insert(0, " ");
|
||||
}
|
||||
l_args.append(QString::number(l_additive));
|
||||
|
||||
// effect
|
||||
l_args.append(l_incoming_args[25].toString());
|
||||
}
|
||||
|
||||
// Testimony playback
|
||||
if (area->testimonyRecording() == AreaData::TestimonyRecording::RECORDING || area->testimonyRecording() == AreaData::TestimonyRecording::ADD) {
|
||||
if (l_args[5] != "wit")
|
||||
return PacketFactory::createPacket("MS", l_args);
|
||||
|
||||
if (area->statement() == -1) {
|
||||
l_args[4] = "~~\\n-- " + l_args[4] + " --";
|
||||
l_args[14] = "3";
|
||||
client.getServer()->broadcast(PacketFactory::createPacket("RT", {"testimony1"}), client.m_current_area);
|
||||
}
|
||||
client.addStatement(l_args);
|
||||
}
|
||||
else if (area->testimonyRecording() == AreaData::TestimonyRecording::UPDATE) {
|
||||
l_args = client.updateStatement(l_args);
|
||||
}
|
||||
else if (area->testimonyRecording() == AreaData::TestimonyRecording::PLAYBACK) {
|
||||
AreaData::TestimonyProgress l_progress;
|
||||
|
||||
if (l_args[4] == ">") {
|
||||
client.m_pos = "wit";
|
||||
auto l_statement = area->jumpToStatement(area->statement() + 1);
|
||||
l_args = l_statement.first;
|
||||
l_progress = l_statement.second;
|
||||
|
||||
if (l_progress == AreaData::TestimonyProgress::LOOPED) {
|
||||
client.sendServerMessageArea("Last statement reached. Looping to first statement.");
|
||||
}
|
||||
}
|
||||
if (l_args[4] == "<") {
|
||||
client.m_pos = "wit";
|
||||
auto l_statement = area->jumpToStatement(area->statement() - 1);
|
||||
l_args = l_statement.first;
|
||||
l_progress = l_statement.second;
|
||||
|
||||
if (l_progress == AreaData::TestimonyProgress::STAYED_AT_FIRST) {
|
||||
client.sendServerMessage("First statement reached.");
|
||||
}
|
||||
}
|
||||
|
||||
QString l_decoded_message = client.decodeMessage(l_args[4]); // Get rid of that pesky encoding first.
|
||||
QRegularExpression jump("(?<arrow>>)(?<int>[0,1,2,3,4,5,6,7,8,9]+)");
|
||||
QRegularExpressionMatch match = jump.match(l_decoded_message);
|
||||
if (match.hasMatch()) {
|
||||
client.m_pos = "wit";
|
||||
auto l_statement = area->jumpToStatement(match.captured("int").toInt());
|
||||
l_args = l_statement.first;
|
||||
l_progress = l_statement.second;
|
||||
|
||||
switch (l_progress) {
|
||||
case AreaData::TestimonyProgress::LOOPED:
|
||||
{
|
||||
client.sendServerMessageArea("Last statement reached. Looping to first statement.");
|
||||
break;
|
||||
}
|
||||
case AreaData::TestimonyProgress::STAYED_AT_FIRST:
|
||||
{
|
||||
client.sendServerMessage("First statement reached.");
|
||||
Q_FALLTHROUGH();
|
||||
}
|
||||
case AreaData::TestimonyProgress::OK:
|
||||
default:
|
||||
// No need to handle.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return PacketFactory::createPacket("MS", l_args);
|
||||
}
|
||||
|
||||
bool PacketMS::validatePacket() const
|
||||
{
|
||||
return true;
|
||||
}
|
32
core/src/packet/packet_pe.cpp
Normal file
32
core/src/packet/packet_pe.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include "include/packet/packet_pe.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketPE::PacketPE(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketPE::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 3,
|
||||
.header = "PE"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketPE::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
if (!client.checkEvidenceAccess(area))
|
||||
return;
|
||||
AreaData::Evidence l_evi = {m_content[0], m_content[1], m_content[2]};
|
||||
area->appendEvidence(l_evi);
|
||||
client.sendEvidenceList(area);
|
||||
}
|
||||
|
||||
bool PacketPE::validatePacket() const
|
||||
{
|
||||
return true;
|
||||
}
|
30
core/src/packet/packet_pw.cpp
Normal file
30
core/src/packet/packet_pw.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include "include/packet/packet_pw.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketPW::PacketPW(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketPW::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 1,
|
||||
.header = "PW"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketPW::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
Q_UNUSED(area)
|
||||
|
||||
client.m_password = m_content[0];
|
||||
}
|
||||
|
||||
bool PacketPW::validatePacket() const
|
||||
{
|
||||
return true;
|
||||
}
|
30
core/src/packet/packet_rc.cpp
Normal file
30
core/src/packet/packet_rc.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include "include/packet/packet_rc.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketRC::PacketRC(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketRC::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 0,
|
||||
.header = "RC"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketRC::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
Q_UNUSED(area)
|
||||
|
||||
client.sendPacket("SC", client.getServer()->getCharacters());
|
||||
}
|
||||
|
||||
bool PacketRC::validatePacket() const
|
||||
{
|
||||
return true;
|
||||
}
|
72
core/src/packet/packet_rd.cpp
Normal file
72
core/src/packet/packet_rd.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
#include "include/packet/packet_rd.h"
|
||||
#include "include/config_manager.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketRD::PacketRD(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketRD::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 0,
|
||||
.header = "RD"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketRD::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
if (client.m_hwid == "") {
|
||||
// No early connecting!
|
||||
client.m_socket->close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (client.m_joined) {
|
||||
return;
|
||||
}
|
||||
|
||||
client.m_joined = true;
|
||||
client.getServer()->updateCharsTaken(area);
|
||||
client.sendEvidenceList(area);
|
||||
client.sendPacket("HP", {"1", QString::number(area->defHP())});
|
||||
client.sendPacket("HP", {"2", QString::number(area->proHP())});
|
||||
client.sendPacket("FA", client.getServer()->getAreaNames());
|
||||
// Here lies OPPASS, the genius of FanatSors who send the modpass to everyone in plain text.
|
||||
client.sendPacket("DONE");
|
||||
client.sendPacket("BN", {area->background()});
|
||||
|
||||
client.sendServerMessage("=== MOTD ===\r\n" + ConfigManager::motd() + "\r\n=============");
|
||||
|
||||
client.fullArup(); // Give client all the area data
|
||||
if (client.getServer()->timer->isActive()) {
|
||||
client.sendPacket("TI", {"0", "2"});
|
||||
client.sendPacket("TI", {"0", "0", QString::number(QTime(0, 0).msecsTo(QTime(0, 0).addMSecs(client.getServer()->timer->remainingTime())))});
|
||||
}
|
||||
else {
|
||||
client.sendPacket("TI", {"0", "3"});
|
||||
}
|
||||
const QList<QTimer *> l_timers = area->timers();
|
||||
for (QTimer *l_timer : l_timers) {
|
||||
int l_timer_id = area->timers().indexOf(l_timer) + 1;
|
||||
if (l_timer->isActive()) {
|
||||
client.sendPacket("TI", {QString::number(l_timer_id), "2"});
|
||||
client.sendPacket("TI", {QString::number(l_timer_id), "0", QString::number(QTime(0, 0).msecsTo(QTime(0, 0).addMSecs(l_timer->remainingTime())))});
|
||||
}
|
||||
else {
|
||||
client.sendPacket("TI", {QString::number(l_timer_id), "3"});
|
||||
}
|
||||
}
|
||||
emit client.joined();
|
||||
area->clientJoinedArea(-1, client.m_id);
|
||||
client.arup(client.ARUPType::PLAYER_COUNT, true); // Tell everyone there is a new player
|
||||
}
|
||||
|
||||
bool PacketRD::validatePacket() const
|
||||
{
|
||||
return true;
|
||||
}
|
30
core/src/packet/packet_rm.cpp
Normal file
30
core/src/packet/packet_rm.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include "include/packet/packet_rm.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketRM::PacketRM(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketRM::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 0,
|
||||
.header = "RM"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketRM::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
Q_UNUSED(area)
|
||||
|
||||
client.sendPacket("SM", client.getServer()->getAreaNames() + client.getServer()->getMusicList());
|
||||
}
|
||||
|
||||
bool PacketRM::validatePacket() const
|
||||
{
|
||||
return true;
|
||||
}
|
43
core/src/packet/packet_rt.cpp
Normal file
43
core/src/packet/packet_rt.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
#include "include/packet/packet_rt.h"
|
||||
#include "include/packet/packet_factory.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketRT::PacketRT(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketRT::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 1,
|
||||
.header = "RT"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketRT::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
if (client.m_is_wtce_blocked) {
|
||||
client.sendServerMessage("You are blocked from using the judge controls.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!area->isWtceAllowed()) {
|
||||
client.sendServerMessage("WTCE animations have been disabled in this area.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (QDateTime::currentDateTime().toSecsSinceEpoch() - client.m_last_wtce_time <= 5)
|
||||
return;
|
||||
client.m_last_wtce_time = QDateTime::currentDateTime().toSecsSinceEpoch();
|
||||
client.getServer()->broadcast(PacketFactory::createPacket("RT", m_content), client.m_current_area);
|
||||
client.updateJudgeLog(area, &client, "WT/CE");
|
||||
}
|
||||
|
||||
bool PacketRT::validatePacket() const
|
||||
{
|
||||
return true;
|
||||
}
|
38
core/src/packet/packet_setcase.cpp
Normal file
38
core/src/packet/packet_setcase.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
#include "include/packet/packet_setcase.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
PacketSetcase::PacketSetcase(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketSetcase::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 7,
|
||||
.header = "Setcase"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketSetcase::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
Q_UNUSED(area)
|
||||
|
||||
QList<bool> l_prefs_list;
|
||||
for (int i = 2; i <= 6; i++) {
|
||||
bool is_int = false;
|
||||
bool pref = m_content[i].toInt(&is_int);
|
||||
if (!is_int)
|
||||
return;
|
||||
l_prefs_list.append(pref);
|
||||
}
|
||||
client.m_casing_preferences = l_prefs_list;
|
||||
}
|
||||
|
||||
bool PacketSetcase::validatePacket() const
|
||||
{
|
||||
return true;
|
||||
}
|
57
core/src/packet/packet_zz.cpp
Normal file
57
core/src/packet/packet_zz.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
#include "include/packet/packet_zz.h"
|
||||
#include "include/config_manager.h"
|
||||
#include "include/packet/packet_factory.h"
|
||||
#include "include/server.h"
|
||||
|
||||
#include <QQueue>
|
||||
|
||||
PacketZZ::PacketZZ(QStringList &contents) :
|
||||
AOPacket(contents)
|
||||
{
|
||||
}
|
||||
|
||||
PacketInfo PacketZZ::getPacketInfo() const
|
||||
{
|
||||
PacketInfo info{
|
||||
.acl_permission = ACLRole::Permission::NONE,
|
||||
.min_args = 0,
|
||||
.header = "ZZ"};
|
||||
return info;
|
||||
}
|
||||
|
||||
void PacketZZ::handlePacket(AreaData *area, AOClient &client) const
|
||||
{
|
||||
QString l_name = client.m_ooc_name;
|
||||
if (client.m_ooc_name.isEmpty())
|
||||
l_name = client.m_current_char;
|
||||
|
||||
QString l_areaName = area->name();
|
||||
|
||||
QString l_modcallNotice = "!!!MODCALL!!!\nArea: " + l_areaName + "\nCaller: " + l_name + "\n";
|
||||
|
||||
if (!m_content[0].isEmpty())
|
||||
l_modcallNotice.append("Reason: " + m_content[0]);
|
||||
else
|
||||
l_modcallNotice.append("No reason given.");
|
||||
|
||||
const QVector<AOClient *> l_clients = client.getServer()->getClients();
|
||||
for (AOClient *l_client : l_clients) {
|
||||
if (l_client->m_authenticated)
|
||||
l_client->sendPacket(PacketFactory::createPacket("ZZ", {l_modcallNotice}));
|
||||
}
|
||||
emit client.logModcall((client.m_current_char + " " + client.m_showname), client.m_ipid, client.m_ooc_name, client.getServer()->getAreaById(client.m_current_area)->name());
|
||||
|
||||
if (ConfigManager::discordModcallWebhookEnabled()) {
|
||||
QString l_name = client.m_ooc_name;
|
||||
if (client.m_ooc_name.isEmpty())
|
||||
l_name = client.m_current_char;
|
||||
|
||||
QString l_areaName = area->name();
|
||||
emit client.getServer()->modcallWebhookRequest(l_name, l_areaName, m_content.value(0), client.getServer()->getAreaBuffer(l_areaName));
|
||||
}
|
||||
}
|
||||
|
||||
bool PacketZZ::validatePacket() const
|
||||
{
|
||||
return true;
|
||||
}
|
@ -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<QTimer *> l_timers = area->timers();
|
||||
for (QTimer *l_timer : l_timers) {
|
||||
int l_timer_id = area->timers().indexOf(l_timer) + 1;
|
||||
if (l_timer->isActive()) {
|
||||
sendPacket("TI", {QString::number(l_timer_id), "2"});
|
||||
sendPacket("TI", {QString::number(l_timer_id), "0", QString::number(QTime(0, 0).msecsTo(QTime(0, 0).addMSecs(l_timer->remainingTime())))});
|
||||
}
|
||||
else {
|
||||
sendPacket("TI", {QString::number(l_timer_id), "3"});
|
||||
}
|
||||
}
|
||||
emit joined();
|
||||
area->clientJoinedArea(-1, m_id);
|
||||
arup(ARUPType::PLAYER_COUNT, true); // Tell everyone there is a new player
|
||||
}
|
||||
|
||||
void AOClient::pktCharPassword(AreaData *area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
Q_UNUSED(area);
|
||||
Q_UNUSED(argc);
|
||||
Q_UNUSED(packet);
|
||||
|
||||
m_password = argv[0];
|
||||
}
|
||||
|
||||
void AOClient::pktSelectChar(AreaData *area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
Q_UNUSED(area);
|
||||
Q_UNUSED(argc);
|
||||
Q_UNUSED(packet);
|
||||
|
||||
bool argument_ok;
|
||||
int l_selected_char_id = argv[1].toInt(&argument_ok);
|
||||
if (!argument_ok) {
|
||||
l_selected_char_id = SPECTATOR_ID;
|
||||
}
|
||||
|
||||
if (l_selected_char_id < -1 || l_selected_char_id > server->getCharacters().size() - 1) {
|
||||
sendPacket(AOPacket("KK", {"A protocol error has been encountered.Packet : CC\nCharacter ID out of range."}));
|
||||
m_socket->close();
|
||||
}
|
||||
|
||||
if (changeCharacter(l_selected_char_id))
|
||||
m_char_id = l_selected_char_id;
|
||||
|
||||
if (m_char_id > SPECTATOR_ID) {
|
||||
setSpectator(false);
|
||||
}
|
||||
}
|
||||
|
||||
void AOClient::pktIcChat(AreaData *area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
Q_UNUSED(argc);
|
||||
Q_UNUSED(argv);
|
||||
|
||||
if (m_is_muted) {
|
||||
sendServerMessage("You cannot speak while muted.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!area->isMessageAllowed() || !server->isMessageAllowed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
AOPacket validated_packet = validateIcPacket(packet);
|
||||
if (validated_packet.getHeader() == "INVALID")
|
||||
return;
|
||||
|
||||
if (m_pos != "")
|
||||
validated_packet.setContentField(5, m_pos);
|
||||
|
||||
server->broadcast(validated_packet, m_current_area);
|
||||
emit logIC((m_current_char + " " + m_showname), m_ooc_name, m_ipid, server->getAreaById(m_current_area)->name(), m_last_message);
|
||||
area->updateLastICMessage(validated_packet.getContent());
|
||||
|
||||
area->startMessageFloodguard(ConfigManager::messageFloodguard());
|
||||
server->startMessageFloodguard(ConfigManager::globalMessageFloodguard());
|
||||
}
|
||||
|
||||
void AOClient::pktOocChat(AreaData *area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
Q_UNUSED(argc);
|
||||
Q_UNUSED(packet);
|
||||
|
||||
if (m_is_ooc_muted) {
|
||||
sendServerMessage("You are OOC muted, and cannot speak.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_ooc_name = dezalgo(argv[0]).replace(QRegExp("\\[|\\]|\\{|\\}|\\#|\\$|\\%|\\&"), ""); // no fucky wucky shit here
|
||||
if (m_ooc_name.isEmpty() || m_ooc_name == ConfigManager::serverName()) // impersonation & empty name protection
|
||||
return;
|
||||
|
||||
if (m_ooc_name.length() > 30) {
|
||||
sendServerMessage("Your name is too long! Please limit it to under 30 characters.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_is_logging_in) {
|
||||
loginAttempt(argv[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
QString l_message = dezalgo(argv[1]);
|
||||
if (l_message.length() == 0 || l_message.length() > ConfigManager::maxCharacters())
|
||||
return;
|
||||
AOPacket final_packet("CT", {m_ooc_name, l_message, "0"});
|
||||
if (l_message.at(0) == '/') {
|
||||
QStringList l_cmd_argv = l_message.split(" ", akashi::SkipEmptyParts);
|
||||
QString l_command = l_cmd_argv[0].trimmed().toLower();
|
||||
l_command = l_command.right(l_command.length() - 1);
|
||||
l_cmd_argv.removeFirst();
|
||||
int l_cmd_argc = l_cmd_argv.length();
|
||||
|
||||
handleCommand(l_command, l_cmd_argc, l_cmd_argv);
|
||||
emit logCMD((m_current_char + " " + m_showname), m_ipid, m_ooc_name, l_command, l_cmd_argv, server->getAreaById(m_current_area)->name());
|
||||
return;
|
||||
}
|
||||
else {
|
||||
server->broadcast(final_packet, m_current_area);
|
||||
}
|
||||
emit logOOC((m_current_char + " " + m_showname), m_ooc_name, m_ipid, area->name(), l_message);
|
||||
}
|
||||
|
||||
void AOClient::pktPing(AreaData *area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
Q_UNUSED(area);
|
||||
Q_UNUSED(argc);
|
||||
Q_UNUSED(argv);
|
||||
Q_UNUSED(packet);
|
||||
|
||||
// Why does this packet exist
|
||||
// At least Crystal made it useful
|
||||
// It is now used for ping measurement
|
||||
sendPacket("CHECK");
|
||||
}
|
||||
|
||||
void AOClient::pktChangeMusic(AreaData *area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
Q_UNUSED(packet);
|
||||
|
||||
// Due to historical reasons, this
|
||||
// packet has two functions:
|
||||
// Change area, and set music.
|
||||
|
||||
// First, we check if the provided
|
||||
// argument is a valid song
|
||||
QString l_argument = argv[0];
|
||||
|
||||
if (server->getMusicList().contains(l_argument) || m_music_manager->isCustom(m_current_area, l_argument) || l_argument == "~stop.mp3") { // ~stop.mp3 is a dummy track used by 2.9+
|
||||
// We have a song here
|
||||
|
||||
if (m_is_spectator) {
|
||||
sendServerMessage("Spectator are blocked from changing the music.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_is_dj_blocked) {
|
||||
sendServerMessage("You are blocked from changing the music.");
|
||||
return;
|
||||
}
|
||||
if (!area->isMusicAllowed() && !checkPermission(ACLRole::CM)) {
|
||||
sendServerMessage("Music is disabled in this area.");
|
||||
return;
|
||||
}
|
||||
QString l_effects;
|
||||
if (argc >= 4)
|
||||
l_effects = argv[3];
|
||||
else
|
||||
l_effects = "0";
|
||||
QString l_final_song;
|
||||
|
||||
// As categories can be used to stop music we need to check if it has a dot for the extension. If not, we assume its a category.
|
||||
if (!l_argument.contains("."))
|
||||
l_final_song = "~stop.mp3";
|
||||
else
|
||||
l_final_song = l_argument;
|
||||
|
||||
// Jukebox intercepts the direct playing of messages.
|
||||
if (area->isjukeboxEnabled()) {
|
||||
QString l_jukebox_reply = area->addJukeboxSong(l_final_song);
|
||||
sendServerMessage(l_jukebox_reply);
|
||||
return;
|
||||
}
|
||||
|
||||
if (l_final_song != "~stop.mp3") {
|
||||
// We might have an aliased song. We check for its real songname and send it to the clients.
|
||||
QPair<QString, float> l_song = m_music_manager->songInformation(l_final_song, m_current_area);
|
||||
l_final_song = l_song.first;
|
||||
}
|
||||
AOPacket l_music_change("MC", {l_final_song, argv[1], m_showname, "1", "0", l_effects});
|
||||
server->broadcast(l_music_change, m_current_area);
|
||||
|
||||
// Since we can't ensure a user has their showname set, we check if its empty to prevent
|
||||
//"played by ." in /currentmusic.
|
||||
if (m_showname.isEmpty()) {
|
||||
area->changeMusic(m_current_char, l_final_song);
|
||||
return;
|
||||
}
|
||||
area->changeMusic(m_showname, l_final_song);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < server->getAreaCount(); i++) {
|
||||
QString l_area = server->getAreaName(i);
|
||||
if (l_area == l_argument) {
|
||||
changeArea(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AOClient::pktWtCe(AreaData *area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
Q_UNUSED(argc);
|
||||
Q_UNUSED(argv);
|
||||
|
||||
if (m_is_wtce_blocked) {
|
||||
sendServerMessage("You are blocked from using the judge controls.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!area->isWtceAllowed()) {
|
||||
sendServerMessage("WTCE animations have been disabled in this area.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (QDateTime::currentDateTime().toSecsSinceEpoch() - m_last_wtce_time <= 5)
|
||||
return;
|
||||
m_last_wtce_time = QDateTime::currentDateTime().toSecsSinceEpoch();
|
||||
server->broadcast(packet, m_current_area);
|
||||
updateJudgeLog(area, this, "WT/CE");
|
||||
}
|
||||
|
||||
void AOClient::pktHpBar(AreaData *area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
Q_UNUSED(argc);
|
||||
Q_UNUSED(packet);
|
||||
|
||||
if (m_is_wtce_blocked) {
|
||||
sendServerMessage("You are blocked from using the judge controls.");
|
||||
return;
|
||||
}
|
||||
int l_newValue = argv.at(1).toInt();
|
||||
|
||||
if (argv[0] == "1") {
|
||||
area->changeHP(AreaData::Side::DEFENCE, l_newValue);
|
||||
}
|
||||
else if (argv[0] == "2") {
|
||||
area->changeHP(AreaData::Side::PROSECUTOR, l_newValue);
|
||||
}
|
||||
|
||||
server->broadcast(AOPacket("HP", {"1", QString::number(area->defHP())}), area->index());
|
||||
server->broadcast(AOPacket("HP", {"2", QString::number(area->proHP())}), area->index());
|
||||
|
||||
updateJudgeLog(area, this, "updated the penalties");
|
||||
}
|
||||
|
||||
void AOClient::pktModCall(AreaData *area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
Q_UNUSED(argc);
|
||||
Q_UNUSED(argv);
|
||||
|
||||
QString l_name = m_ooc_name;
|
||||
if (m_ooc_name.isEmpty())
|
||||
l_name = m_current_char;
|
||||
|
||||
QString l_areaName = area->name();
|
||||
|
||||
QString l_modcallNotice = "!!!MODCALL!!!\nArea: " + l_areaName + "\nCaller: " + l_name + "\n";
|
||||
|
||||
if (!packet.getContent()[0].isEmpty())
|
||||
l_modcallNotice.append("Reason: " + packet.getContent()[0]);
|
||||
else
|
||||
l_modcallNotice.append("No reason given.");
|
||||
|
||||
const QVector<AOClient *> l_clients = server->getClients();
|
||||
for (AOClient *l_client : l_clients) {
|
||||
if (l_client->m_authenticated)
|
||||
l_client->sendPacket(AOPacket("ZZ", {l_modcallNotice}));
|
||||
}
|
||||
emit logModcall((m_current_char + " " + m_showname), m_ipid, m_ooc_name, server->getAreaById(m_current_area)->name());
|
||||
|
||||
if (ConfigManager::discordModcallWebhookEnabled()) {
|
||||
QString l_name = m_ooc_name;
|
||||
if (m_ooc_name.isEmpty())
|
||||
l_name = m_current_char;
|
||||
|
||||
QString l_areaName = area->name();
|
||||
emit server->modcallWebhookRequest(l_name, l_areaName, packet.getContent().value(0), server->getAreaBuffer(l_areaName));
|
||||
}
|
||||
}
|
||||
|
||||
void AOClient::pktAddEvidence(AreaData *area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
Q_UNUSED(argc);
|
||||
Q_UNUSED(packet);
|
||||
|
||||
if (!checkEvidenceAccess(area))
|
||||
return;
|
||||
AreaData::Evidence l_evi = {argv[0], argv[1], argv[2]};
|
||||
area->appendEvidence(l_evi);
|
||||
sendEvidenceList(area);
|
||||
}
|
||||
|
||||
void AOClient::pktRemoveEvidence(AreaData *area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
Q_UNUSED(argc);
|
||||
Q_UNUSED(packet);
|
||||
|
||||
if (!checkEvidenceAccess(area))
|
||||
return;
|
||||
bool is_int = false;
|
||||
int l_idx = argv[0].toInt(&is_int);
|
||||
if (is_int && l_idx < area->evidence().size() && l_idx >= 0) {
|
||||
area->deleteEvidence(l_idx);
|
||||
}
|
||||
sendEvidenceList(area);
|
||||
}
|
||||
|
||||
void AOClient::pktEditEvidence(AreaData *area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
Q_UNUSED(argc);
|
||||
Q_UNUSED(packet);
|
||||
|
||||
if (!checkEvidenceAccess(area))
|
||||
return;
|
||||
bool is_int = false;
|
||||
int l_idx = argv[0].toInt(&is_int);
|
||||
AreaData::Evidence l_evi = {argv[1], argv[2], argv[3]};
|
||||
if (is_int && l_idx < area->evidence().size() && l_idx >= 0) {
|
||||
area->replaceEvidence(l_idx, l_evi);
|
||||
}
|
||||
sendEvidenceList(area);
|
||||
}
|
||||
|
||||
void AOClient::pktSetCase(AreaData *area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
Q_UNUSED(area);
|
||||
Q_UNUSED(argc);
|
||||
Q_UNUSED(packet);
|
||||
|
||||
QList<bool> l_prefs_list;
|
||||
for (int i = 2; i <= 6; i++) {
|
||||
bool is_int = false;
|
||||
bool pref = argv[i].toInt(&is_int);
|
||||
if (!is_int)
|
||||
return;
|
||||
l_prefs_list.append(pref);
|
||||
}
|
||||
m_casing_preferences = l_prefs_list;
|
||||
}
|
||||
|
||||
void AOClient::pktAnnounceCase(AreaData *area, int argc, QStringList argv, AOPacket packet)
|
||||
{
|
||||
Q_UNUSED(area);
|
||||
Q_UNUSED(argc);
|
||||
Q_UNUSED(packet);
|
||||
|
||||
QString l_case_title = argv[0];
|
||||
QStringList l_needed_roles;
|
||||
QList<bool> l_needs_list;
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
bool is_int = false;
|
||||
bool need = argv[i].toInt(&is_int);
|
||||
if (!is_int)
|
||||
return;
|
||||
l_needs_list.append(need);
|
||||
}
|
||||
QStringList l_roles = {"defense attorney", "prosecutor", "judge", "jurors", "stenographer"};
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (l_needs_list[i])
|
||||
l_needed_roles.append(l_roles[i]);
|
||||
}
|
||||
if (l_needed_roles.isEmpty())
|
||||
return;
|
||||
|
||||
QString l_message = "=== Case Announcement ===\r\n" + (m_ooc_name == "" ? m_current_char : m_ooc_name) + " needs " + l_needed_roles.join(", ") + " for " + (l_case_title == "" ? "a case" : l_case_title) + "!";
|
||||
|
||||
QList<AOClient *> l_clients_to_alert;
|
||||
// here lies morton, RIP
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
QSet<bool> l_needs_set(l_needs_list.begin(), l_needs_list.end());
|
||||
#else
|
||||
QSet<bool> l_needs_set = l_needs_list.toSet();
|
||||
#endif
|
||||
const QVector<AOClient *> l_clients = server->getClients();
|
||||
for (AOClient *l_client : l_clients) {
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
QSet<bool> l_matches(l_client->m_casing_preferences.begin(), l_client->m_casing_preferences.end());
|
||||
l_matches.intersect(l_needs_set);
|
||||
#else
|
||||
QSet<bool> l_matches = l_client->m_casing_preferences.toSet().intersect(l_needs_set);
|
||||
#endif
|
||||
if (!l_matches.isEmpty() && !l_clients_to_alert.contains(l_client))
|
||||
l_clients_to_alert.append(l_client);
|
||||
}
|
||||
|
||||
for (AOClient *l_client : l_clients_to_alert) {
|
||||
l_client->sendPacket(AOPacket("CASEA", {l_message, argv[1], argv[2], argv[3], argv[4], argv[5], "1"}));
|
||||
// you may be thinking, "hey wait a minute the network protocol documentation doesn't mention that last argument!"
|
||||
// if you are in fact thinking that, you are correct! it is not in the documentation!
|
||||
// however for some inscrutable reason Attorney Online 2 will outright reject a CASEA packet that does not have
|
||||
// at least 7 arguments despite only using the first 6. Cera, i kneel. you have truly broken me.
|
||||
}
|
||||
}
|
||||
|
||||
void AOClient::sendEvidenceList(AreaData *area)
|
||||
void AOClient::sendEvidenceList(AreaData *area) const
|
||||
{
|
||||
const QVector<AOClient *> 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<QVariant> l_incoming_args;
|
||||
for (const QString &l_arg : packet.getContent()) {
|
||||
l_incoming_args.append(QVariant(l_arg));
|
||||
}
|
||||
|
||||
// desk modifier
|
||||
QStringList allowed_desk_mods;
|
||||
allowed_desk_mods << "chat"
|
||||
<< "0"
|
||||
<< "1"
|
||||
<< "2"
|
||||
<< "3"
|
||||
<< "4"
|
||||
<< "5";
|
||||
QString l_incoming_deskmod = l_incoming_args[0].toString();
|
||||
if (allowed_desk_mods.contains(l_incoming_deskmod)) {
|
||||
// **WARNING : THIS IS A HACK!**
|
||||
// A proper solution would be to deprecate chat as an argument on the clientside
|
||||
// instead of overwriting correct netcode behaviour on the serverside.
|
||||
if (l_incoming_deskmod == "chat") {
|
||||
l_args.append("1");
|
||||
}
|
||||
else {
|
||||
l_args.append(l_incoming_args[0].toString());
|
||||
}
|
||||
}
|
||||
else
|
||||
return l_invalid;
|
||||
|
||||
// preanim
|
||||
l_args.append(l_incoming_args[1].toString());
|
||||
|
||||
// char name
|
||||
if (m_current_char.toLower() != l_incoming_args[2].toString().toLower()) {
|
||||
// Selected char is different from supplied folder name
|
||||
// This means the user is INI-swapped
|
||||
if (!area->iniswapAllowed()) {
|
||||
if (!server->getCharacters().contains(l_incoming_args[2].toString(), Qt::CaseInsensitive))
|
||||
return l_invalid;
|
||||
}
|
||||
qDebug() << "INI swap detected from " << getIpid();
|
||||
}
|
||||
m_current_iniswap = l_incoming_args[2].toString();
|
||||
l_args.append(l_incoming_args[2].toString());
|
||||
|
||||
// emote
|
||||
m_emote = l_incoming_args[3].toString();
|
||||
if (m_first_person)
|
||||
m_emote = "";
|
||||
l_args.append(m_emote);
|
||||
|
||||
// message text
|
||||
if (l_incoming_args[4].toString().size() > ConfigManager::maxCharacters())
|
||||
return l_invalid;
|
||||
|
||||
QString l_incoming_msg = dezalgo(l_incoming_args[4].toString().trimmed());
|
||||
if (!area->lastICMessage().isEmpty() && l_incoming_msg == area->lastICMessage()[4] && l_incoming_msg != "")
|
||||
return l_invalid;
|
||||
|
||||
if (l_incoming_msg == "" && area->blankpostingAllowed() == false) {
|
||||
sendServerMessage("Blankposting has been forbidden in this area.");
|
||||
return l_invalid;
|
||||
}
|
||||
|
||||
if (m_is_gimped) {
|
||||
QString l_gimp_message = ConfigManager::gimpList().at((genRand(1, ConfigManager::gimpList().size() - 1)));
|
||||
l_incoming_msg = l_gimp_message;
|
||||
}
|
||||
|
||||
if (m_is_shaken) {
|
||||
QStringList l_parts = l_incoming_msg.split(" ");
|
||||
std::random_shuffle(l_parts.begin(), l_parts.end());
|
||||
l_incoming_msg = l_parts.join(" ");
|
||||
}
|
||||
|
||||
if (m_is_disemvoweled) {
|
||||
QString l_disemvoweled_message = l_incoming_msg.remove(QRegExp("[AEIOUaeiou]"));
|
||||
l_incoming_msg = l_disemvoweled_message;
|
||||
}
|
||||
|
||||
m_last_message = l_incoming_msg;
|
||||
l_args.append(l_incoming_msg);
|
||||
|
||||
// side
|
||||
// this is validated clientside so w/e
|
||||
l_args.append(l_incoming_args[5].toString());
|
||||
if (m_pos != l_incoming_args[5].toString()) {
|
||||
m_pos = l_incoming_args[5].toString();
|
||||
updateEvidenceList(server->getAreaById(m_current_area));
|
||||
}
|
||||
|
||||
// sfx name
|
||||
l_args.append(l_incoming_args[6].toString());
|
||||
|
||||
// emote modifier
|
||||
// Now, gather round, y'all. Here is a story that is truly a microcosm of the AO dev experience.
|
||||
// If this value is a 4, it will crash the client. Why? Who knows, but it does.
|
||||
// Now here is the kicker: in certain versions, the client would incorrectly send a 4 here
|
||||
// For a long time, by configuring the client to do a zoom with a preanim, it would send 4
|
||||
// This would crash everyone else's client, and the feature had to be disabled
|
||||
// But, for some reason, nobody traced the cause of this issue for many many years.
|
||||
// The serverside fix is needed to ensure invalid values are not sent, because the client sucks
|
||||
int emote_mod = l_incoming_args[7].toInt();
|
||||
|
||||
if (emote_mod == 4)
|
||||
emote_mod = 6;
|
||||
if (emote_mod != 0 && emote_mod != 1 && emote_mod != 2 && emote_mod != 5 && emote_mod != 6)
|
||||
return l_invalid;
|
||||
l_args.append(QString::number(emote_mod));
|
||||
|
||||
// char id
|
||||
if (l_incoming_args[8].toInt() != m_char_id)
|
||||
return l_invalid;
|
||||
l_args.append(l_incoming_args[8].toString());
|
||||
|
||||
// sfx delay
|
||||
l_args.append(l_incoming_args[9].toString());
|
||||
|
||||
// objection modifier
|
||||
if (area->isShoutAllowed()) {
|
||||
if (l_incoming_args[10].toString().contains("4")) {
|
||||
// custom shout includes text metadata
|
||||
l_args.append(l_incoming_args[10].toString());
|
||||
}
|
||||
else {
|
||||
int l_obj_mod = l_incoming_args[10].toInt();
|
||||
if ((l_obj_mod < 0) || (l_obj_mod > 4)) {
|
||||
return l_invalid;
|
||||
}
|
||||
l_args.append(QString::number(l_obj_mod));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (l_incoming_args[10].toString() != "0") {
|
||||
sendServerMessage("Shouts have been disabled in this area.");
|
||||
}
|
||||
l_args.append("0");
|
||||
}
|
||||
|
||||
// evidence
|
||||
int evi_idx = l_incoming_args[11].toInt();
|
||||
if (evi_idx > area->evidence().length())
|
||||
return l_invalid;
|
||||
l_args.append(QString::number(evi_idx));
|
||||
|
||||
// flipping
|
||||
int l_flip = l_incoming_args[12].toInt();
|
||||
if (l_flip != 0 && l_flip != 1)
|
||||
return l_invalid;
|
||||
m_flipping = QString::number(l_flip);
|
||||
l_args.append(m_flipping);
|
||||
|
||||
// realization
|
||||
int realization = l_incoming_args[13].toInt();
|
||||
if (realization != 0 && realization != 1)
|
||||
return l_invalid;
|
||||
l_args.append(QString::number(realization));
|
||||
|
||||
// text color
|
||||
int text_color = l_incoming_args[14].toInt();
|
||||
if (text_color < 0 || text_color > 11)
|
||||
return l_invalid;
|
||||
l_args.append(QString::number(text_color));
|
||||
|
||||
// 2.6 packet extensions
|
||||
if (l_incoming_args.length() >= 19) {
|
||||
// showname
|
||||
QString l_incoming_showname = dezalgo(l_incoming_args[15].toString().trimmed());
|
||||
if (!(l_incoming_showname == m_current_char || l_incoming_showname.isEmpty()) && !area->shownameAllowed()) {
|
||||
sendServerMessage("Shownames are not allowed in this area!");
|
||||
return l_invalid;
|
||||
}
|
||||
if (l_incoming_showname.length() > 30) {
|
||||
sendServerMessage("Your showname is too long! Please limit it to under 30 characters");
|
||||
return l_invalid;
|
||||
}
|
||||
|
||||
// if the raw input is not empty but the trimmed input is, use a single space
|
||||
if (l_incoming_showname.isEmpty() && !l_incoming_args[15].toString().isEmpty())
|
||||
l_incoming_showname = " ";
|
||||
l_args.append(l_incoming_showname);
|
||||
m_showname = l_incoming_showname;
|
||||
|
||||
// other char id
|
||||
// things get a bit hairy here
|
||||
// don't ask me how this works, because i don't know either
|
||||
QStringList l_pair_data = l_incoming_args[16].toString().split("^");
|
||||
m_pairing_with = l_pair_data[0].toInt();
|
||||
QString l_front_back = "";
|
||||
if (l_pair_data.length() > 1)
|
||||
l_front_back = "^" + l_pair_data[1];
|
||||
int l_other_charid = m_pairing_with;
|
||||
bool l_pairing = false;
|
||||
QString l_other_name = "0";
|
||||
QString l_other_emote = "0";
|
||||
QString l_other_offset = "0";
|
||||
QString l_other_flip = "0";
|
||||
for (int l_client_id : area->joinedIDs()) {
|
||||
AOClient *l_client = server->getClientByID(l_client_id);
|
||||
if (l_client->m_pairing_with == m_char_id && l_other_charid != m_char_id && l_client->m_char_id == m_pairing_with && l_client->m_pos == m_pos) {
|
||||
l_other_name = l_client->m_current_iniswap;
|
||||
l_other_emote = l_client->m_emote;
|
||||
l_other_offset = l_client->m_offset;
|
||||
l_other_flip = l_client->m_flipping;
|
||||
l_pairing = true;
|
||||
}
|
||||
}
|
||||
if (!l_pairing) {
|
||||
l_other_charid = -1;
|
||||
l_front_back = "";
|
||||
}
|
||||
l_args.append(QString::number(l_other_charid) + l_front_back);
|
||||
l_args.append(l_other_name);
|
||||
l_args.append(l_other_emote);
|
||||
|
||||
// self offset
|
||||
m_offset = l_incoming_args[17].toString();
|
||||
// versions 2.6-2.8 cannot validate y-offset so we send them just the x-offset
|
||||
if ((m_version.release == 2) && (m_version.major == 6 || m_version.major == 7 || m_version.major == 8)) {
|
||||
QString l_x_offset = m_offset.split("&")[0];
|
||||
l_args.append(l_x_offset);
|
||||
QString l_other_x_offset = l_other_offset.split("&")[0];
|
||||
l_args.append(l_other_x_offset);
|
||||
}
|
||||
else {
|
||||
l_args.append(m_offset);
|
||||
l_args.append(l_other_offset);
|
||||
}
|
||||
l_args.append(l_other_flip);
|
||||
|
||||
// immediate text processing
|
||||
int l_immediate = l_incoming_args[18].toInt();
|
||||
if (area->forceImmediate()) {
|
||||
if (l_args[7] == "1" || l_args[7] == "2") {
|
||||
l_args[7] = "0";
|
||||
l_immediate = 1;
|
||||
}
|
||||
else if (l_args[7] == "6") {
|
||||
l_args[7] = "5";
|
||||
l_immediate = 1;
|
||||
}
|
||||
}
|
||||
if (l_immediate != 1 && l_immediate != 0)
|
||||
return l_invalid;
|
||||
l_args.append(QString::number(l_immediate));
|
||||
}
|
||||
|
||||
// 2.8 packet extensions
|
||||
if (l_incoming_args.length() >= 26) {
|
||||
// sfx looping
|
||||
int l_sfx_loop = l_incoming_args[19].toInt();
|
||||
if (l_sfx_loop != 0 && l_sfx_loop != 1)
|
||||
return l_invalid;
|
||||
l_args.append(QString::number(l_sfx_loop));
|
||||
|
||||
// screenshake
|
||||
int l_screenshake = l_incoming_args[20].toInt();
|
||||
if (l_screenshake != 0 && l_screenshake != 1)
|
||||
return l_invalid;
|
||||
l_args.append(QString::number(l_screenshake));
|
||||
|
||||
// frames shake
|
||||
l_args.append(l_incoming_args[21].toString());
|
||||
|
||||
// frames realization
|
||||
l_args.append(l_incoming_args[22].toString());
|
||||
|
||||
// frames sfx
|
||||
l_args.append(l_incoming_args[23].toString());
|
||||
|
||||
// additive
|
||||
int l_additive = l_incoming_args[24].toInt();
|
||||
if (l_additive != 0 && l_additive != 1)
|
||||
return l_invalid;
|
||||
else if (area->lastICMessage().isEmpty()) {
|
||||
l_additive = 0;
|
||||
}
|
||||
else if (!(m_char_id == area->lastICMessage()[8].toInt())) {
|
||||
l_additive = 0;
|
||||
}
|
||||
else if (l_additive == 1) {
|
||||
l_args[4].insert(0, " ");
|
||||
}
|
||||
l_args.append(QString::number(l_additive));
|
||||
|
||||
// effect
|
||||
l_args.append(l_incoming_args[25].toString());
|
||||
}
|
||||
|
||||
// Testimony playback
|
||||
if (area->testimonyRecording() == AreaData::TestimonyRecording::RECORDING || area->testimonyRecording() == AreaData::TestimonyRecording::ADD) {
|
||||
if (l_args[5] != "wit")
|
||||
return AOPacket("MS", l_args);
|
||||
|
||||
if (area->statement() == -1) {
|
||||
l_args[4] = "~~\\n-- " + l_args[4] + " --";
|
||||
l_args[14] = "3";
|
||||
server->broadcast(AOPacket("RT", {"testimony1"}), m_current_area);
|
||||
}
|
||||
addStatement(l_args);
|
||||
}
|
||||
else if (area->testimonyRecording() == AreaData::TestimonyRecording::UPDATE) {
|
||||
l_args = updateStatement(l_args);
|
||||
}
|
||||
else if (area->testimonyRecording() == AreaData::TestimonyRecording::PLAYBACK) {
|
||||
AreaData::TestimonyProgress l_progress;
|
||||
|
||||
if (l_args[4] == ">") {
|
||||
m_pos = "wit";
|
||||
auto l_statement = area->jumpToStatement(area->statement() + 1);
|
||||
l_args = l_statement.first;
|
||||
l_progress = l_statement.second;
|
||||
|
||||
if (l_progress == AreaData::TestimonyProgress::LOOPED) {
|
||||
sendServerMessageArea("Last statement reached. Looping to first statement.");
|
||||
}
|
||||
}
|
||||
if (l_args[4] == "<") {
|
||||
m_pos = "wit";
|
||||
auto l_statement = area->jumpToStatement(area->statement() - 1);
|
||||
l_args = l_statement.first;
|
||||
l_progress = l_statement.second;
|
||||
|
||||
if (l_progress == AreaData::TestimonyProgress::STAYED_AT_FIRST) {
|
||||
sendServerMessage("First statement reached.");
|
||||
}
|
||||
}
|
||||
|
||||
QString l_decoded_message = decodeMessage(l_args[4]); // Get rid of that pesky encoding first.
|
||||
QRegularExpression jump("(?<arrow>>)(?<int>[0,1,2,3,4,5,6,7,8,9]+)");
|
||||
QRegularExpressionMatch match = jump.match(l_decoded_message);
|
||||
if (match.hasMatch()) {
|
||||
m_pos = "wit";
|
||||
auto l_statement = area->jumpToStatement(match.captured("int").toInt());
|
||||
l_args = l_statement.first;
|
||||
l_progress = l_statement.second;
|
||||
|
||||
switch (l_progress) {
|
||||
case AreaData::TestimonyProgress::LOOPED:
|
||||
{
|
||||
sendServerMessageArea("Last statement reached. Looping to first statement.");
|
||||
break;
|
||||
}
|
||||
case AreaData::TestimonyProgress::STAYED_AT_FIRST:
|
||||
{
|
||||
sendServerMessage("First statement reached.");
|
||||
Q_FALLTHROUGH();
|
||||
}
|
||||
case AreaData::TestimonyProgress::OK:
|
||||
default:
|
||||
// No need to handle.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return AOPacket("MS", l_args);
|
||||
sendPacket(PacketFactory::createPacket("LE", l_evidence_list));
|
||||
}
|
||||
|
||||
QString AOClient::dezalgo(QString p_text)
|
||||
|
@ -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<AOPacket, int>::of(&Server::broadcast));
|
||||
connect(music_manager, &MusicManager::sendAreaFMPacket, this, QOverload<AOPacket *, int>::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<AOPacket, int>::of(&Server::broadcast));
|
||||
connect(l_area, &AreaData::sendAreaPacket, this, QOverload<AOPacket *, int>::of(&Server::broadcast));
|
||||
connect(l_area, &AreaData::sendAreaPacketClient, this, &Server::unicast);
|
||||
connect(l_area, &AreaData::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<int> 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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
111
tests/unittest_akashi_utils/tst_unittest_akashi_utils.cpp
Normal file
111
tests/unittest_akashi_utils/tst_unittest_akashi_utils.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
#include "include/akashiutils.h"
|
||||
#include <QTest>
|
||||
|
||||
namespace tests {
|
||||
namespace unittests {
|
||||
|
||||
class tst_AkashiUtils : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
|
||||
void integer_data();
|
||||
void integer();
|
||||
|
||||
void floating_data();
|
||||
void floating();
|
||||
|
||||
void boolean_data();
|
||||
void boolean();
|
||||
|
||||
void doublep_data();
|
||||
void doublep();
|
||||
};
|
||||
|
||||
void tst_AkashiUtils::integer_data()
|
||||
{
|
||||
QTest::addColumn<QString>("content");
|
||||
QTest::addColumn<bool>("expected_result");
|
||||
|
||||
QTest::addRow("Integer (good)") << "one"
|
||||
<< false;
|
||||
QTest::addRow("Integer (bad)") << "1"
|
||||
<< true;
|
||||
}
|
||||
|
||||
void tst_AkashiUtils::integer()
|
||||
{
|
||||
QFETCH(QString, content);
|
||||
QFETCH(bool, expected_result);
|
||||
|
||||
bool result = AkashiUtils::checkArgType<int>(content);
|
||||
QCOMPARE(result, expected_result);
|
||||
}
|
||||
|
||||
void tst_AkashiUtils::floating_data()
|
||||
{
|
||||
QTest::addColumn<QString>("content");
|
||||
QTest::addColumn<bool>("expected_result");
|
||||
|
||||
QTest::addRow("Float (good)") << "test"
|
||||
<< false;
|
||||
QTest::addRow("Float (bad)") << "3.141"
|
||||
<< true;
|
||||
}
|
||||
|
||||
void tst_AkashiUtils::floating()
|
||||
{
|
||||
QFETCH(QString, content);
|
||||
QFETCH(bool, expected_result);
|
||||
|
||||
bool result = AkashiUtils::checkArgType<float>(content);
|
||||
QCOMPARE(result, expected_result);
|
||||
}
|
||||
|
||||
void tst_AkashiUtils::boolean_data()
|
||||
{
|
||||
QTest::addColumn<QString>("content");
|
||||
QTest::addColumn<bool>("expected_result");
|
||||
|
||||
QTest::addRow("Boolean (random string)") << "test"
|
||||
<< true;
|
||||
QTest::addRow("Boolean (true/false string)") << "true"
|
||||
<< true;
|
||||
}
|
||||
|
||||
void tst_AkashiUtils::boolean()
|
||||
{
|
||||
QFETCH(QString, content);
|
||||
QFETCH(bool, expected_result);
|
||||
|
||||
bool result = AkashiUtils::checkArgType<bool>(content);
|
||||
QCOMPARE(result, expected_result);
|
||||
}
|
||||
|
||||
void tst_AkashiUtils::doublep_data()
|
||||
{
|
||||
QTest::addColumn<QString>("content");
|
||||
QTest::addColumn<bool>("expected_result");
|
||||
|
||||
QTest::addRow("Double (good)") << "test"
|
||||
<< false;
|
||||
QTest::addRow("Double (bad)") << "3.141592653589793"
|
||||
<< true;
|
||||
}
|
||||
|
||||
void tst_AkashiUtils::doublep()
|
||||
{
|
||||
QFETCH(QString, content);
|
||||
QFETCH(bool, expected_result);
|
||||
|
||||
bool result = AkashiUtils::checkArgType<double>(content);
|
||||
QCOMPARE(result, expected_result);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_APPLESS_MAIN(tests::unittests::tst_AkashiUtils)
|
||||
|
||||
#include "tst_unittest_akashi_utils.moc"
|
5
tests/unittest_akashi_utils/unittest_akashi_utils.pro
Normal file
5
tests/unittest_akashi_utils/unittest_akashi_utils.pro
Normal file
@ -0,0 +1,5 @@
|
||||
QT -= gui
|
||||
|
||||
include(../tests_common.pri)
|
||||
|
||||
SOURCES += tst_unittest_akashi_utils.cpp
|
@ -2,6 +2,7 @@
|
||||
#include <QTest>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user