Merge branch 'master' into feature/net-auth

This commit is contained in:
in1tiate 2021-03-15 18:17:12 -05:00
commit 64d745dc0f
17 changed files with 1028 additions and 206 deletions

View File

@ -10,4 +10,8 @@ Requires Qt >= 5.10, and Qt websockets
make make
``` ```
Copyright © scatterflower 2021 Copyright © scatterflower 2020-2021
Copyright © Salanto 2021
Copyright © in1tiate 2021

View File

@ -1,5 +1,11 @@
[Basement] [0:Basement]
background=gs4 background=gs4
protected_area=true
iniswap_allowed=false
evidence_mod=cm
[Courtroom 1] [1:Courtroom 1]
background=gs4 background=gs4
protected_area=false
iniswap_allowed=true
evidence_mod=ffa

View File

@ -9,6 +9,7 @@ ms_port=27016
port=27016 port=27016
server_description=This is a placeholder server description. Tell the world of AO who you are here! server_description=This is a placeholder server description. Tell the world of AO who you are here!
server_name=An Unnamed Server server_name=An Unnamed Server
motd=MOTD is not set.
webao_enable=true webao_enable=true
webao_port=27017 webao_port=27017
auth=simple auth=simple

View File

@ -28,7 +28,7 @@
#include <QHostAddress> #include <QHostAddress>
#include <QTcpSocket> #include <QTcpSocket>
#include <QDateTime> #include <QDateTime>
#include <QRegExp> #include <QRegularExpression>
#include <QtGlobal> #include <QtGlobal>
#if QT_VERSION > QT_VERSION_CHECK(5, 10, 0) #if QT_VERSION > QT_VERSION_CHECK(5, 10, 0)
#include <QRandomGenerator> #include <QRandomGenerator>
@ -42,10 +42,9 @@ class AOClient : public QObject {
AOClient(Server* p_server, QTcpSocket* p_socket, QObject* parent = nullptr, int user_id = 0); AOClient(Server* p_server, QTcpSocket* p_socket, QObject* parent = nullptr, int user_id = 0);
~AOClient(); ~AOClient();
QString getHwid();
QString getIpid();
Server* getServer(); Server* getServer();
void setHwid(QString p_hwid); QString getIpid();
void calculateIpid();
int id; int id;
@ -57,6 +56,16 @@ class AOClient : public QObject {
bool authenticated = false; bool authenticated = false;
QString moderator_name = ""; QString moderator_name = "";
QString ooc_name = ""; QString ooc_name = "";
QString showname = "";
bool global_enabled = true;
bool is_muted = false;
struct ClientVersion {
QString string;
int release = -1;
int major = -1;
int minor = -1;
};
ClientVersion version;
QMap<QString, unsigned long long> ACLFlags { QMap<QString, unsigned long long> ACLFlags {
{"NONE", 0ULL}, {"NONE", 0ULL},
@ -66,6 +75,11 @@ class AOClient : public QObject {
{"MODIFY_USERS", 1ULL << 3}, {"MODIFY_USERS", 1ULL << 3},
{"CM", 1ULL << 4}, {"CM", 1ULL << 4},
{"GLOBAL_TIMER", 1ULL << 5}, {"GLOBAL_TIMER", 1ULL << 5},
{"EVI_MOD", 1ULL << 6},
{"MOTD", 1ULL << 7},
{"ANNOUNCE", 1ULL << 8},
{"MODCHAT", 1ULL << 9},
{"MUTE", 1ULL << 10},
{"SUPER", ~0ULL} {"SUPER", ~0ULL}
}; };
@ -92,14 +106,16 @@ class AOClient : public QObject {
}; };
enum RollType { enum RollType {
ROLL, ROLL,
ROLLP, ROLLP,
ROLLA ROLLA
}; };
void handlePacket(AOPacket packet); void handlePacket(AOPacket packet);
void handleCommand(QString command, int argc, QStringList argv); void handleCommand(QString command, int argc, QStringList argv);
void changeArea(int new_area); void changeArea(int new_area);
void changeCharacter(int char_id);
void changePosition(QString new_pos);
void arup(ARUPType type, bool broadcast); void arup(ARUPType type, bool broadcast);
void fullArup(); void fullArup();
void sendServerMessage(QString message); void sendServerMessage(QString message);
@ -131,8 +147,10 @@ class AOClient : public QObject {
// Packet helper functions // Packet helper functions
void sendEvidenceList(AreaData* area); void sendEvidenceList(AreaData* area);
void updateEvidenceList(AreaData* area);
AOPacket validateIcPacket(AOPacket packet); AOPacket validateIcPacket(AOPacket packet);
QString dezalgo(QString p_text); QString dezalgo(QString p_text);
bool checkEvidenceAccess(AreaData* area);
// Packet helper global variables // Packet helper global variables
int char_id = -1; int char_id = -1;
@ -172,11 +190,15 @@ class AOClient : public QObject {
//// Commands //// Commands
void cmdDefault(int argc, QStringList argv); void cmdDefault(int argc, QStringList argv);
void cmdHelp(int argc, QStringList argv);
void cmdMOTD(int argc, QStringList argv);
void cmdAbout(int argc, QStringList argv);
// Authentication // Authentication
void cmdLogin(int argc, QStringList argv); void cmdLogin(int argc, QStringList argv);
void cmdChangeAuth(int argc, QStringList argv); void cmdChangeAuth(int argc, QStringList argv);
void cmdSetRootPass(int argc, QStringList argv); void cmdSetRootPass(int argc, QStringList argv);
void cmdAddUser(int argc, QStringList argv); void cmdAddUser(int argc, QStringList argv);
void cmdRemoveUser(int argc, QStringList argv);
void cmdListPerms(int argc, QStringList argv); void cmdListPerms(int argc, QStringList argv);
void cmdAddPerms(int argc, QStringList argv); void cmdAddPerms(int argc, QStringList argv);
void cmdRemovePerms(int argc, QStringList argv); void cmdRemovePerms(int argc, QStringList argv);
@ -192,13 +214,26 @@ class AOClient : public QObject {
void cmdUnLock(int argc, QStringList argv); void cmdUnLock(int argc, QStringList argv);
void cmdGetAreas(int argc, QStringList argv); void cmdGetAreas(int argc, QStringList argv);
void cmdGetArea(int argc, QStringList argv); void cmdGetArea(int argc, QStringList argv);
void cmdArea(int argc, QStringList argv);
void cmdAreaKick(int argc, QStringList argv);
void cmdSetBackground(int argc, QStringList argv); void cmdSetBackground(int argc, QStringList argv);
void cmdBgLock(int argc, QStringList argv); void cmdBgLock(int argc, QStringList argv);
void cmdBgUnlock(int argc, QStringList argv); void cmdBgUnlock(int argc, QStringList argv);
void cmdStatus(int argc, QStringList argv);
void cmdCurrentMusic(int argc, QStringList argv);
// Moderation // Moderation
void cmdMods(int argc, QStringList argv);
void cmdBan(int argc, QStringList argv); void cmdBan(int argc, QStringList argv);
void cmdUnBan(int argc, QStringList argv);
void cmdKick(int argc, QStringList argv); void cmdKick(int argc, QStringList argv);
void cmdAnnounce(int argc, QStringList argv);
void cmdM(int argc, QStringList argv);
void cmdGM(int argc, QStringList argv);
void cmdMute(int argc, QStringList argv);
void cmdUnmute(int argc, QStringList argv);
void cmdBans(int argc, QStringList argv);
// Casing/RP // Casing/RP
void cmdPlay(int argc, QStringList argv);
void cmdNeed(int argc, QStringList argv); void cmdNeed(int argc, QStringList argv);
void cmdFlip(int argc, QStringList argv); void cmdFlip(int argc, QStringList argv);
void cmdRoll(int argc, QStringList argv); void cmdRoll(int argc, QStringList argv);
@ -206,15 +241,23 @@ class AOClient : public QObject {
void cmdDoc(int argc, QStringList argv); void cmdDoc(int argc, QStringList argv);
void cmdClearDoc(int argc, QStringList argv); void cmdClearDoc(int argc, QStringList argv);
void cmdTimer(int argc, QStringList argv); void cmdTimer(int argc, QStringList argv);
void cmdEvidenceMod(int argc, QStringList argv);
void cmdSubTheme(int argc, QStringList argv);
// Messaging/Client // Messaging/Client
void cmdPos(int argc, QStringList argv); void cmdPos(int argc, QStringList argv);
void cmdForcePos(int argc, QStringList argv);
void cmdSwitch(int argc, QStringList argv);
void cmdRandomChar(int argc, QStringList argv);
void cmdG(int argc, QStringList argv); void cmdG(int argc, QStringList argv);
void cmdToggleGlobal(int argc, QStringList argv);
void cmdPM(int argc, QStringList argv);
// Command helper functions // Command helper functions
QString getAreaTimer(int area_idx, QTimer* timer); QString getAreaTimer(int area_idx, QTimer* timer);
QStringList buildAreaList(int area_idx); QStringList buildAreaList(int area_idx);
int genRand(int min, int max); int genRand(int min, int max);
void diceThrower(int argc, QStringList argv, RollType Type); void diceThrower(int argc, QStringList argv, RollType Type);
long long parseTime(QString input);
// Command function global variables // Command function global variables
bool change_auth_started = false; bool change_auth_started = false;
@ -246,7 +289,7 @@ class AOClient : public QObject {
{"pos", {ACLFlags.value("NONE"), 1, &AOClient::cmdPos}}, {"pos", {ACLFlags.value("NONE"), 1, &AOClient::cmdPos}},
{"g", {ACLFlags.value("NONE"), 1, &AOClient::cmdG}}, {"g", {ACLFlags.value("NONE"), 1, &AOClient::cmdG}},
{"need", {ACLFlags.value("NONE"), 1, &AOClient::cmdNeed}}, {"need", {ACLFlags.value("NONE"), 1, &AOClient::cmdNeed}},
{"flip", {ACLFlags.value("NONE"), 0, &AOClient::cmdFlip}}, {"coinflip", {ACLFlags.value("NONE"), 0, &AOClient::cmdFlip}},
{"roll", {ACLFlags.value("NONE"), 0, &AOClient::cmdRoll}}, {"roll", {ACLFlags.value("NONE"), 0, &AOClient::cmdRoll}},
{"rollp", {ACLFlags.value("NONE"), 0, &AOClient::cmdRollP}}, {"rollp", {ACLFlags.value("NONE"), 0, &AOClient::cmdRollP}},
{"doc", {ACLFlags.value("NONE"), 0, &AOClient::cmdDoc}}, {"doc", {ACLFlags.value("NONE"), 0, &AOClient::cmdDoc}},
@ -256,9 +299,37 @@ class AOClient : public QObject {
{"invite", {ACLFlags.value("CM"), 1, &AOClient::cmdInvite}}, {"invite", {ACLFlags.value("CM"), 1, &AOClient::cmdInvite}},
{"uninvite", {ACLFlags.value("CM"), 1, &AOClient::cmdUnInvite}}, {"uninvite", {ACLFlags.value("CM"), 1, &AOClient::cmdUnInvite}},
{"lock", {ACLFlags.value("CM"), 0, &AOClient::cmdLock}}, {"lock", {ACLFlags.value("CM"), 0, &AOClient::cmdLock}},
{"area_lock", {ACLFlags.value("CM"), 0, &AOClient::cmdLock}},
{"spectatable", {ACLFlags.value("CM"), 0, &AOClient::cmdSpectatable}}, {"spectatable", {ACLFlags.value("CM"), 0, &AOClient::cmdSpectatable}},
{"area_spectate", {ACLFlags.value("CM"), 0, &AOClient::cmdSpectatable}},
{"unlock", {ACLFlags.value("CM"), 0, &AOClient::cmdUnLock}}, {"unlock", {ACLFlags.value("CM"), 0, &AOClient::cmdUnLock}},
{"area_unlock", {ACLFlags.value("CM"), 0, &AOClient::cmdUnLock}},
{"timer", {ACLFlags.value("CM"), 0, &AOClient::cmdTimer}}, {"timer", {ACLFlags.value("CM"), 0, &AOClient::cmdTimer}},
{"area", {ACLFlags.value("NONE"), 1, &AOClient::cmdArea}},
{"play", {ACLFlags.value("CM"), 1, &AOClient::cmdPlay}},
{"areakick", {ACLFlags.value("CM"), 1, &AOClient::cmdAreaKick}},
{"area_kick", {ACLFlags.value("CM"), 1, &AOClient::cmdAreaKick}},
{"randomchar", {ACLFlags.value("NONE"), 0, &AOClient::cmdRandomChar}},
{"switch", {ACLFlags.value("NONE"), 1, &AOClient::cmdSwitch}},
{"toggleglobal", {ACLFlags.value("NONE"), 0, &AOClient::cmdToggleGlobal}},
{"mods", {ACLFlags.value("NONE"), 0, &AOClient::cmdMods}},
{"help", {ACLFlags.value("NONE"), 0, &AOClient::cmdHelp}},
{"status", {ACLFlags.value("NONE"), 1, &AOClient::cmdStatus}},
{"forcepos", {ACLFlags.value("CM"), 2, &AOClient::cmdForcePos}},
{"currentmusic", {ACLFlags.value("NONE"), 0, &AOClient::cmdCurrentMusic}},
{"pm", {ACLFlags.value("NONE"), 2, &AOClient::cmdPM}},
{"evidence_mod", {ACLFlags.value("EVI_MOD"), 1, &AOClient::cmdEvidenceMod}},
{"motd", {ACLFlags.value("NONE"), 0, &AOClient::cmdMOTD}},
{"announce", {ACLFlags.value("ANNOUNCE"), 1, &AOClient::cmdAnnounce}},
{"m", {ACLFlags.value("MODCHAT"), 1, &AOClient::cmdM}},
{"gm", {ACLFlags.value("MODCHAT"), 1, &AOClient::cmdGM}},
{"mute", {ACLFlags.value("MUTE"), 1, &AOClient::cmdMute}},
{"unmute", {ACLFlags.value("MUTE"), 1, &AOClient::cmdUnmute}},
{"bans", {ACLFlags.value("BAN"), 0, &AOClient::cmdBans}},
{"unban", {ACLFlags.value("BAN"), 1, &AOClient::cmdUnBan}},
{"removeuser", {ACLFlags.value("MODIFY_USERS"), 1, &AOClient::cmdRemoveUser}},
{"subtheme", {ACLFlags.value("CM"), 1, &AOClient::cmdSubTheme}},
{"about", {ACLFlags.value("NONE"), 0, &AOClient::cmdAbout}}
}; };
QString partial_packet; QString partial_packet;

View File

@ -28,9 +28,10 @@
#include <QElapsedTimer> #include <QElapsedTimer>
class Logger; class Logger;
class AreaData { class AreaData : public QObject {
Q_OBJECT
public: public:
AreaData(QStringList p_characters, QString p_name, int p_index); AreaData(QString p_name, int p_index);
struct Evidence { struct Evidence {
QString name; QString name;
@ -40,10 +41,19 @@ class AreaData {
QList<QTimer*> timers; QList<QTimer*> timers;
QString name; QString name;
int index; int index;
QMap<QString, bool> characters_taken; QList<int> characters_taken;
QList<Evidence> evidence; QList<Evidence> evidence;
int player_count; int player_count;
QString status; enum Status {
IDLE,
RP,
CASING,
LOOKING_FOR_PLAYERS,
RECESS,
GAMING
};
Q_ENUM(Status);
Status status;
QList<int> owners; QList<int> owners;
QList<int> invited; QList<int> invited;
enum LockStatus { enum LockStatus {
@ -51,6 +61,7 @@ class AreaData {
LOCKED, LOCKED,
SPECTATABLE SPECTATABLE
}; };
Q_ENUM(LockStatus);
LockStatus locked; LockStatus locked;
QString background; QString background;
bool is_protected; bool is_protected;
@ -60,7 +71,16 @@ class AreaData {
QString document; QString document;
int def_hp; int def_hp;
int pro_hp; int pro_hp;
QString current_music;
QString music_played_by;
Logger* logger; Logger* logger;
enum EvidenceMod{
FFA,
MOD,
CM,
HIDDEN_CM
};
EvidenceMod evi_mod;
}; };
#endif // AREA_DATA_H #endif // AREA_DATA_H

View File

@ -36,7 +36,7 @@ class ConfigManager {
QString ms_ip; QString ms_ip;
int port; int port;
int ws_port; int ws_port;
int local_port; int ms_port;
QString name; QString name;
QString description; QString description;
bool advertise_server; bool advertise_server;

View File

@ -19,6 +19,7 @@
#define BAN_MANAGER_H #define BAN_MANAGER_H
#include <QDebug> #include <QDebug>
#include <QDateTime>
#include <QHostAddress> #include <QHostAddress>
#include <QMessageAuthenticationCode> #include <QMessageAuthenticationCode>
#include <QString> #include <QString>
@ -37,10 +38,26 @@ public:
QString getBanReason(QHostAddress ip); QString getBanReason(QHostAddress ip);
QString getBanReason(QString hdid); QString getBanReason(QString hdid);
long long getBanDuration(QString hdid);
long long getBanDuration(QHostAddress ip);
int getBanID(QString hdid);
int getBanID(QHostAddress ip);
void addBan(QString ipid, QHostAddress ip, QString hdid, unsigned long time, QString reason); struct BanInfo {
QString ipid;
QHostAddress ip;
QString hdid;
unsigned long time;
QString reason;
long long duration;
};
QList<BanInfo> getRecentBans();
void addBan(BanInfo ban);
bool invalidateBan(int id);
bool createUser(QString username, QString salt, QString password, unsigned long long acl); bool createUser(QString username, QString salt, QString password, unsigned long long acl);
bool deleteUser(QString username);
unsigned long long getACL(QString moderator_name); unsigned long long getACL(QString moderator_name);
bool authenticate(QString username, QString password); bool authenticate(QString username, QString password);
bool updateACL(QString username, unsigned long long acl, bool mode); bool updateACL(QString username, unsigned long long acl, bool mode);

View File

@ -46,13 +46,14 @@ class Server : public QObject {
~Server(); ~Server();
void start(); void start();
AOClient* getClient(QString ipid); QList<AOClient*> getClientsByIpid(QString ipid);
AOClient* getClientByID(int id); AOClient* getClientByID(int id);
void updateCharsTaken(AreaData* area); void updateCharsTaken(AreaData* area);
void broadcast(AOPacket packet, int area_index); void broadcast(AOPacket packet, int area_index);
void broadcast(AOPacket packet); void broadcast(AOPacket packet);
QString getServerName(); QString getServerName();
int getDiceValue(QString value_type); int getDiceValue(QString value_type);
int getCharID(QString char_name);
QVector<AOClient*> clients; QVector<AOClient*> clients;
@ -64,6 +65,7 @@ class Server : public QObject {
QStringList backgrounds; QStringList backgrounds;
DBManager* db_manager; DBManager* db_manager;
QString server_name; QString server_name;
QString MOTD;
QTimer* timer; QTimer* timer;

View File

@ -62,7 +62,7 @@ void Advertiser::socketConnected()
AOPacket ao_packet("SCC", AOPacket ao_packet("SCC",
{concat_ports, name, description, {concat_ports, name, description,
"akashi v" + QCoreApplication::applicationVersion()}); "akashi " + QCoreApplication::applicationVersion()});
QByteArray data = ao_packet.toUtf8(); QByteArray data = ao_packet.toUtf8();
socket->write(data); socket->write(data);

View File

@ -28,6 +28,7 @@ AOClient::AOClient(Server* p_server, QTcpSocket* p_socket, QObject* parent, int
current_area = 0; current_area = 0;
current_char = ""; current_char = "";
remote_ip = p_socket->peerAddress(); remote_ip = p_socket->peerAddress();
calculateIpid();
is_partial = false; is_partial = false;
last_wtce_time = 0; last_wtce_time = 0;
last_message = ""; last_message = "";
@ -64,14 +65,20 @@ void AOClient::clientDisconnected()
arup(ARUPType::PLAYER_COUNT, true); arup(ARUPType::PLAYER_COUNT, true);
} }
if (current_char != "") { if (current_char != "") {
server->areas[current_area]->characters_taken[current_char] = server->areas[current_area]->characters_taken.removeAll(server->getCharID(current_char));
false;
server->updateCharsTaken(server->areas[current_area]); server->updateCharsTaken(server->areas[current_area]);
} }
bool update_locks;
for (AreaData* area : server->areas) { for (AreaData* area : server->areas) {
area->owners.removeAll(id); area->owners.removeAll(id);
area->invited.removeAll(id); area->invited.removeAll(id);
if (area->owners.isEmpty() && area->locked != AreaData::FREE) {
area->locked = AreaData::FREE;
update_locks = true;
}
} }
if (update_locks)
arup(ARUPType::LOCKED, true);
arup(ARUPType::CM, true); arup(ARUPType::CM, true);
} }
@ -109,8 +116,7 @@ void AOClient::changeArea(int new_area)
} }
if (current_char != "") { if (current_char != "") {
server->areas[current_area]->characters_taken[current_char] = server->areas[current_area]->characters_taken.removeAll(server->getCharID(current_char));
false;
server->updateCharsTaken(server->areas[current_area]); server->updateCharsTaken(server->areas[current_area]);
} }
server->areas[new_area]->player_count++; server->areas[new_area]->player_count++;
@ -121,23 +127,23 @@ void AOClient::changeArea(int new_area)
sendPacket("HP", {"1", QString::number(server->areas[new_area]->def_hp)}); sendPacket("HP", {"1", QString::number(server->areas[new_area]->def_hp)});
sendPacket("HP", {"2", QString::number(server->areas[new_area]->pro_hp)}); sendPacket("HP", {"2", QString::number(server->areas[new_area]->pro_hp)});
sendPacket("BN", {server->areas[new_area]->background}); sendPacket("BN", {server->areas[new_area]->background});
if (server->areas[current_area]->characters_taken[current_char]) { if (server->areas[current_area]->characters_taken.contains(server->getCharID(current_char))) {
server->updateCharsTaken(server->areas[current_area]); server->updateCharsTaken(server->areas[current_area]);
current_char = ""; current_char = "";
sendPacket("DONE"); sendPacket("DONE");
} }
else { else {
server->areas[current_area]->characters_taken[current_char] = true; server->areas[current_area]->characters_taken.append(server->getCharID(current_char));
server->updateCharsTaken(server->areas[current_area]); server->updateCharsTaken(server->areas[current_area]);
} }
for (QTimer* timer : server->areas[current_area]->timers) { for (QTimer* timer : server->areas[current_area]->timers) {
int timer_id = server->areas[current_area]->timers.indexOf(timer) + 1; int timer_id = server->areas[current_area]->timers.indexOf(timer) + 1;
if (timer->isActive()) { if (timer->isActive()) {
sendPacket("TI", {QString::number(timer_id), QString::number(2)}); sendPacket("TI", {QString::number(timer_id), "2"});
sendPacket("TI", {QString::number(timer_id), QString::number(0), QString::number(QTime(0,0).msecsTo(QTime(0,0).addMSecs(timer->remainingTime())))}); sendPacket("TI", {QString::number(timer_id), "0", QString::number(QTime(0,0).msecsTo(QTime(0,0).addMSecs(timer->remainingTime())))});
} }
else { else {
sendPacket("TI", {QString::number(timer_id), QString::number(3)}); sendPacket("TI", {QString::number(timer_id), "3"});
} }
} }
sendServerMessage("You moved to area " + server->area_names[current_area]); sendServerMessage("You moved to area " + server->area_names[current_area]);
@ -145,6 +151,43 @@ void AOClient::changeArea(int new_area)
sendServerMessage("Area " + server->area_names[current_area] + " is spectate-only; to chat IC you will need to be invited by the CM."); sendServerMessage("Area " + server->area_names[current_area] + " is spectate-only; to chat IC you will need to be invited by the CM.");
} }
void AOClient::changeCharacter(int char_id)
{
AreaData* area = server->areas[current_area];
if (current_char != "") {
area->characters_taken.removeAll(server->getCharID(current_char));
}
if(char_id > server->characters.length())
return;
if (char_id >= 0) {
QString char_selected = server->characters[char_id];
bool taken = area->characters_taken.contains(char_id);
if (taken || char_selected == "")
return;
area->characters_taken.append(char_id);
current_char = char_selected;
}
else {
current_char = "";
}
pos = "";
server->updateCharsTaken(area);
sendPacket("PV", {QString::number(id), "CID", QString::number(char_id)});
}
void AOClient::changePosition(QString new_pos)
{
pos = new_pos;
sendServerMessage("Position changed to " + pos + ".");
sendPacket("SP", {pos});
}
void AOClient::handleCommand(QString command, int argc, QStringList argv) void AOClient::handleCommand(QString command, int argc, QStringList argv)
{ {
CommandInfo info = commands.value(command, {false, -1, &AOClient::cmdDefault}); CommandInfo info = commands.value(command, {false, -1, &AOClient::cmdDefault});
@ -167,42 +210,38 @@ void AOClient::arup(ARUPType type, bool broadcast)
QStringList arup_data; QStringList arup_data;
arup_data.append(QString::number(type)); arup_data.append(QString::number(type));
for (AreaData* area : server->areas) { for (AreaData* area : server->areas) {
if (type == ARUPType::PLAYER_COUNT) { switch(type) {
arup_data.append(QString::number(area->player_count)); case ARUPType::PLAYER_COUNT: {
} arup_data.append(QString::number(area->player_count));
else if (type == ARUPType::STATUS) { break;
arup_data.append(area->status); }
} case ARUPType::STATUS: {
else if (type == ARUPType::CM) { QString area_status = QVariant::fromValue(area->status).toString().replace("_", "-"); // LOOKING_FOR_PLAYERS to LOOKING-FOR-PLAYERS
if (area->owners.isEmpty()) arup_data.append(area_status);
arup_data.append("FREE"); break;
else { }
QStringList area_owners; case ARUPType::CM: {
for (int owner_id : area->owners) { if (area->owners.isEmpty())
AOClient* owner = server->getClientByID(owner_id); arup_data.append("FREE");
area_owners.append("[" + QString::number(owner->id) + "] " + owner->current_char); else {
QStringList area_owners;
for (int owner_id : area->owners) {
AOClient* owner = server->getClientByID(owner_id);
area_owners.append("[" + QString::number(owner->id) + "] " + owner->current_char);
}
arup_data.append(area_owners.join(", "));
} }
arup_data.append(area_owners.join(", ")); break;
}
case ARUPType::LOCKED: {
QString lock_status = QVariant::fromValue(area->locked).toString();
arup_data.append(lock_status);
break;
}
default: {
return;
} }
} }
else if (type == ARUPType::LOCKED) {
QString lock_status;
switch (area->locked) {
case AreaData::LockStatus::FREE:
lock_status = "FREE";
break;
case AreaData::LockStatus::LOCKED:
lock_status = "LOCKED";
break;
case AreaData::LockStatus::SPECTATABLE:
lock_status = "SPECTATABLE";
break;
default:
break;
}
arup_data.append(lock_status);
}
else return;
} }
if (broadcast) if (broadcast)
server->broadcast(AOPacket("ARUP", arup_data)); server->broadcast(AOPacket("ARUP", arup_data));
@ -236,22 +275,17 @@ void AOClient::sendPacket(QString header)
sendPacket(AOPacket(header, {})); sendPacket(AOPacket(header, {}));
} }
QString AOClient::getHwid() { return hwid; } void AOClient::calculateIpid()
void AOClient::setHwid(QString p_hwid)
{ {
// TODO: add support for longer hwids? // TODO: add support for longer ipids?
// This reduces the (fairly high) chance of // This reduces the (fairly high) chance of
// birthday paradox issues arising. However, // birthday paradox issues arising. However,
// typing more than 8 characters might be a // typing more than 8 characters might be a
// bit cumbersome. // bit cumbersome.
hwid = p_hwid;
QCryptographicHash hash( QCryptographicHash hash(QCryptographicHash::Md5); // Don't need security, just hashing for uniqueness
QCryptographicHash::Md5); // Don't need security, just
// hashing for uniqueness hash.addData(remote_ip.toString().toUtf8());
QString concat_ip_id = remote_ip.toString() + p_hwid;
hash.addData(concat_ip_id.toUtf8());
ipid = hash.result().toHex().right(8); // Use the last 8 characters (4 bytes) ipid = hash.result().toHex().right(8); // Use the last 8 characters (4 bytes)
} }

View File

@ -17,22 +17,23 @@
////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////
#include "include/area_data.h" #include "include/area_data.h"
AreaData::AreaData(QStringList characters, QString p_name, int p_index) AreaData::AreaData(QString p_name, int p_index)
{ {
name = p_name; QStringList name_split = p_name.split(":");
name_split.removeFirst();
name = name_split.join(":");
index = p_index; index = p_index;
for (QString cur_char : characters) {
characters_taken.insert(cur_char, false);
}
QSettings areas_ini("config/areas.ini", QSettings::IniFormat); QSettings areas_ini("config/areas.ini", QSettings::IniFormat);
areas_ini.beginGroup(p_name); areas_ini.beginGroup(p_name);
background = areas_ini.value("background", "gs4").toString(); background = areas_ini.value("background", "gs4").toString();
is_protected = areas_ini.value("protected_area").toBool(); is_protected = areas_ini.value("protected_area", "false").toBool();
iniswap_allowed = areas_ini.value("iniswap_allowed", "true").toBool();
bg_locked = areas_ini.value("bg_locked", "false").toBool(); bg_locked = areas_ini.value("bg_locked", "false").toBool();
QString configured_evi_mod = areas_ini.value("evidence_mod", "FFA").toString().toLower();
areas_ini.endGroup(); areas_ini.endGroup();
player_count = 0; player_count = 0;
locked = FREE; locked = FREE;
status = "FREE"; status = IDLE;
def_hp = 10; def_hp = 10;
pro_hp = 10; pro_hp = 10;
document = "No document."; document = "No document.";
@ -51,4 +52,13 @@ AreaData::AreaData(QStringList characters, QString p_name, int p_index)
timers.append(timer3); timers.append(timer3);
QTimer* timer4 = new QTimer(); QTimer* timer4 = new QTimer();
timers.append(timer4); timers.append(timer4);
if (configured_evi_mod == "cm")
evi_mod = EvidenceMod::CM;
else if (configured_evi_mod == "mod")
evi_mod = EvidenceMod::MOD;
else if (configured_evi_mod == "hiddencm")
evi_mod = EvidenceMod::HIDDEN_CM;
else
evi_mod = EvidenceMod::FFA;
} }

View File

@ -93,33 +93,72 @@ void AOClient::cmdGetArea(int argc, QStringList argv)
void AOClient::cmdBan(int argc, QStringList argv) void AOClient::cmdBan(int argc, QStringList argv)
{ {
QString target_ipid = argv[0]; QString args_str = argv[1];
QHostAddress ip; if (argc > 2) {
QString hdid; for (int i = 2; i < argc; i++)
unsigned long time = QDateTime::currentDateTime().toTime_t(); args_str += " " + argv[i];
QString reason = argv[1]; }
DBManager::BanInfo ban;
QRegularExpression quoteMatcher("['\"](.+?)[\"']");
QRegularExpressionMatchIterator matches = quoteMatcher.globalMatch(args_str);
QList<QString> unquoted_args;
while (matches.hasNext()) {
QRegularExpressionMatch match = matches.next();
unquoted_args.append(match.captured(1));
}
QString duration = "perma";
if (unquoted_args.length() < 1) {
sendServerMessage("Invalid syntax. Usage:\n/ban <ipid> \"<reason>\" \"<duration>\"");
return;
}
ban.reason = unquoted_args.at(0);
if (unquoted_args.length() > 1)
duration = unquoted_args.at(1);
long long duration_seconds = 0;
if (duration == "perma")
duration_seconds = -2;
else
duration_seconds = parseTime(duration);
if (duration_seconds == -1) {
sendServerMessage("Invalid time format. Format example: 1h30m");
return;
}
ban.duration = duration_seconds;
ban.ipid = argv[0];
ban.time = QDateTime::currentDateTime().toSecsSinceEpoch();
bool ban_logged = false; bool ban_logged = false;
int kick_counter = 0;
if (argc > 2) { if (argc > 2) {
for (int i = 2; i < argv.length(); i++) { for (int i = 2; i < argv.length(); i++) {
reason += " " + argv[i]; ban.reason += " " + argv[i];
} }
} }
for (AOClient* client : server->clients) { for (AOClient* client : server->getClientsByIpid(ban.ipid)) {
if (client->getIpid() == target_ipid) { if (!ban_logged) {
if (!ban_logged) { ban.ip = client->remote_ip;
ip = client->remote_ip; ban.hdid = client->hwid;
hdid = client->hwid; server->db_manager->addBan(ban);
server->db_manager->addBan(target_ipid, ip, hdid, time, reason); sendServerMessage("Banned user with ipid " + ban.ipid + " for reason: " + ban.reason);
sendServerMessage("Banned user with ipid " + target_ipid + " for reason: " + reason); ban_logged = true;
ban_logged = true;
}
client->sendPacket("KB", {reason});
client->socket->close();
} }
client->sendPacket("KB", {ban.reason + "\nID: " + QString::number(server->db_manager->getBanID(ban.ip)) + "\nUntil: " + QDateTime::fromSecsSinceEpoch(ban.time).addSecs(ban.duration).toString("dd.MM.yyyy, hh:mm")});
client->socket->close();
kick_counter++;
} }
if (kick_counter > 1)
sendServerMessage("Kicked " + QString::number(kick_counter) + " clients with matching ipids.");
if (!ban_logged) if (!ban_logged)
sendServerMessage("User with ipid not found!"); sendServerMessage("User with ipid not found!");
} }
@ -128,7 +167,7 @@ void AOClient::cmdKick(int argc, QStringList argv)
{ {
QString target_ipid = argv[0]; QString target_ipid = argv[0];
QString reason = argv[1]; QString reason = argv[1];
bool did_kick = false; int kick_counter = 0;
if (argc > 2) { if (argc > 2) {
for (int i = 2; i < argv.length(); i++) { for (int i = 2; i < argv.length(); i++) {
@ -136,16 +175,14 @@ void AOClient::cmdKick(int argc, QStringList argv)
} }
} }
for (AOClient* client : server->clients) { for (AOClient* client : server->getClientsByIpid(target_ipid)) {
if (client->getIpid() == target_ipid) { client->sendPacket("KK", {reason});
client->sendPacket("KK", {reason}); client->socket->close();
client->socket->close(); kick_counter++;
did_kick = true;
}
} }
if (did_kick) if (kick_counter > 0)
sendServerMessage("Kicked user with ipid " + target_ipid + " for reason: " + reason); sendServerMessage("Kicked " + QString::number(kick_counter) + " client(s) with ipid " + target_ipid + " for reason: " + reason);
else else
sendServerMessage("User with ipid not found!"); sendServerMessage("User with ipid not found!");
} }
@ -236,6 +273,14 @@ void AOClient::cmdAddUser(int argc, QStringList argv)
sendServerMessage("Unable to create user " + argv[0] + ".\nDoes a user with that name already exist?"); sendServerMessage("Unable to create user " + argv[0] + ".\nDoes a user with that name already exist?");
} }
void AOClient::cmdRemoveUser(int argc, QStringList argv)
{
if (server->db_manager->deleteUser(argv[0]))
sendServerMessage("Successfully removed user " + argv[0] + ".");
else
sendServerMessage("Unable to remove user " + argv[0] + ".\nDoes it exist?");
}
void AOClient::cmdListPerms(int argc, QStringList argv) void AOClient::cmdListPerms(int argc, QStringList argv)
{ {
unsigned long long user_acl = server->db_manager->getACL(moderator_name); unsigned long long user_acl = server->db_manager->getACL(moderator_name);
@ -368,8 +413,43 @@ void AOClient::cmdLogout(int argc, QStringList argv)
void AOClient::cmdPos(int argc, QStringList argv) void AOClient::cmdPos(int argc, QStringList argv)
{ {
pos = argv[0]; changePosition(argv[0]);
sendServerMessage("Position changed to " + pos + "."); updateEvidenceList(server->areas[current_area]);
}
void AOClient::cmdForcePos(int argc, QStringList argv)
{
bool ok;
QList<AOClient*> targets;
AreaData* area = server->areas[current_area];
int target_id = argv[1].toInt(&ok);
int forced_clients = 0;
if (!ok && argv[1] != "*") {
sendServerMessage("That does not look like a valid ID.");
return;
}
else if (ok) {
AOClient* target_client = server->getClientByID(target_id);
if (target_client != nullptr)
targets.append(target_client);
else {
sendServerMessage("Target ID not found!");
return;
}
}
else if (argv[1] == "*") { // force all clients in the area
for (AOClient* client : server->clients) {
if (client->current_area == current_area)
targets.append(client);
}
}
for (AOClient* target : targets) {
target->sendServerMessage("Position forcibly changed by CM.");
target->changePosition(argv[0]);
forced_clients++;
}
sendServerMessage("Forced " + QString::number(forced_clients) + " into pos " + argv[0] + ".");
} }
void AOClient::cmdG(int argc, QStringList argv) void AOClient::cmdG(int argc, QStringList argv)
@ -377,7 +457,10 @@ void AOClient::cmdG(int argc, QStringList argv)
QString sender_name = ooc_name; QString sender_name = ooc_name;
QString sender_area = server->area_names.value(current_area); QString sender_area = server->area_names.value(current_area);
QString sender_message = argv.join(" "); QString sender_message = argv.join(" ");
server->broadcast(AOPacket("CT", {"[" + sender_area + "]" + sender_name, sender_message})); for (AOClient* client : server->clients) {
if (client->global_enabled)
client->sendPacket("CT", {"[G][" + sender_area + "]" + sender_name, sender_message});
}
return; return;
} }
@ -471,8 +554,13 @@ void AOClient::cmdUnCM(int argc, QStringList argv)
area->invited.removeAll(id); area->invited.removeAll(id);
sendServerMessage("You are no longer CM in this area."); sendServerMessage("You are no longer CM in this area.");
arup(ARUPType::CM, true); arup(ARUPType::CM, true);
if (area->owners.isEmpty()) if (area->owners.isEmpty()) {
area->invited.clear(); area->invited.clear();
if (area->locked != AreaData::FREE) {
area->locked = AreaData::FREE;
arup(ARUPType::LOCKED, true);
}
}
} }
void AOClient::cmdInvite(int argc, QStringList argv) void AOClient::cmdInvite(int argc, QStringList argv)
@ -484,6 +572,10 @@ void AOClient::cmdInvite(int argc, QStringList argv)
sendServerMessage("That does not look like a valid ID."); sendServerMessage("That does not look like a valid ID.");
return; return;
} }
else if (server->getClientByID(invited_id) == nullptr) {
sendServerMessage("No client with that ID found.");
return;
}
else if (area->invited.contains(invited_id)) { else if (area->invited.contains(invited_id)) {
sendServerMessage("That ID is already on the invite list."); sendServerMessage("That ID is already on the invite list.");
return; return;
@ -501,6 +593,10 @@ void AOClient::cmdUnInvite(int argc, QStringList argv)
sendServerMessage("That does not look like a valid ID."); sendServerMessage("That does not look like a valid ID.");
return; return;
} }
else if (server->getClientByID(uninvited_id) == nullptr) {
sendServerMessage("No client with that ID found.");
return;
}
else if (area->owners.contains(uninvited_id)) { else if (area->owners.contains(uninvited_id)) {
sendServerMessage("You cannot uninvite a CM!"); sendServerMessage("You cannot uninvite a CM!");
return; return;
@ -615,32 +711,329 @@ void AOClient::cmdTimer(int argc, QStringList argv)
requested_timer->setInterval(QTime(0,0).msecsTo(requested_time)); requested_timer->setInterval(QTime(0,0).msecsTo(requested_time));
requested_timer->start(); requested_timer->start();
sendServerMessage("Set timer " + QString::number(timer_id) + " to " + argv[1] + "."); sendServerMessage("Set timer " + QString::number(timer_id) + " to " + argv[1] + ".");
sendPacket("TI", {QString::number(timer_id), QString::number(2)}); sendPacket("TI", {QString::number(timer_id), "2"}); // Show the timer
sendPacket("TI", {QString::number(timer_id), QString::number(0), QString::number(QTime(0,0).msecsTo(requested_time))}); sendPacket("TI", {QString::number(timer_id), "0", QString::number(QTime(0,0).msecsTo(requested_time))});
return; return;
} }
else { else {
if (argv[1] == "start") { if (argv[1] == "start") {
requested_timer->start(); requested_timer->start();
sendServerMessage("Started timer " + QString::number(timer_id) + "."); sendServerMessage("Started timer " + QString::number(timer_id) + ".");
sendPacket("TI", {QString::number(timer_id), QString::number(2)}); sendPacket("TI", {QString::number(timer_id), "2"}); // Show the timer
sendPacket("TI", {QString::number(timer_id), QString::number(0), QString::number(QTime(0,0).msecsTo(QTime(0,0).addMSecs(requested_timer->remainingTime())))}); sendPacket("TI", {QString::number(timer_id), "0", QString::number(QTime(0,0).msecsTo(QTime(0,0).addMSecs(requested_timer->remainingTime())))});
} }
else if (argv[1] == "pause" || argv[1] == "stop") { else if (argv[1] == "pause" || argv[1] == "stop") {
requested_timer->setInterval(requested_timer->remainingTime()); requested_timer->setInterval(requested_timer->remainingTime());
requested_timer->stop(); requested_timer->stop();
sendServerMessage("Stopped timer " + QString::number(timer_id) + "."); sendServerMessage("Stopped timer " + QString::number(timer_id) + ".");
sendPacket("TI", {QString::number(timer_id), QString::number(1), QString::number(QTime(0,0).msecsTo(QTime(0,0).addMSecs(requested_timer->interval())))}); sendPacket("TI", {QString::number(timer_id), "1", QString::number(QTime(0,0).msecsTo(QTime(0,0).addMSecs(requested_timer->interval())))});
} }
else if (argv[1] == "hide" || argv[1] == "unset") { else if (argv[1] == "hide" || argv[1] == "unset") {
requested_timer->setInterval(0); requested_timer->setInterval(0);
requested_timer->stop(); requested_timer->stop();
sendServerMessage("Hid timer " + QString::number(timer_id) + "."); sendServerMessage("Hid timer " + QString::number(timer_id) + ".");
sendPacket("TI", {QString::number(timer_id), QString::number(3)}); sendPacket("TI", {QString::number(timer_id), "3"}); // Hide the timer
} }
} }
} }
void AOClient::cmdEvidenceMod(int argc, QStringList argv)
{
AreaData* area = server->areas[current_area];
argv[0] = argv[0].toLower();
if (argv[0] == "cm")
area->evi_mod = AreaData::EvidenceMod::CM;
else if (argv[0] == "mod")
area->evi_mod = AreaData::EvidenceMod::MOD;
else if (argv[0] == "hiddencm")
area->evi_mod = AreaData::EvidenceMod::HIDDEN_CM;
else if (argv[0] == "ffa")
area->evi_mod = AreaData::EvidenceMod::FFA;
else {
sendServerMessage("Invalid evidence mod.");
return;
}
sendServerMessage("Changed evidence mod.");
// Resend evidence lists to everyone in the area
sendEvidenceList(area);
}
void AOClient::cmdArea(int argc, QStringList argv)
{
bool ok;
int new_area = argv[0].toInt(&ok);
if (!ok || new_area >= server->areas.size() || new_area < 0) {
sendServerMessage("That does not look like a valid area ID.");
return;
}
changeArea(new_area);
}
void AOClient::cmdPlay(int argc, QStringList argv)
{
AreaData* area = server->areas[current_area];
QString song = argv.join(" ");
area->current_music = song;
area->music_played_by = showname;
AOPacket music_change("MC", {song, QString::number(server->getCharID(current_char)), showname, "1", "0"});
server->broadcast(music_change, current_area);
}
void AOClient::cmdAreaKick(int argc, QStringList argv)
{
bool ok;
int idx = argv[0].toInt(&ok);
if (!ok) {
sendServerMessage("That does not look like a valid ID.");
return;
}
AOClient* client_to_kick = server->getClientByID(idx);
if (client_to_kick == nullptr) {
sendServerMessage("No client with that ID found.");
return;
}
client_to_kick->changeArea(0);
sendServerMessage("Client " + argv[0] + " kicked back to area 0.");
}
void AOClient::cmdSwitch(int argc, QStringList argv)
{
int char_id = server->getCharID(argv.join(" "));
if (char_id == -1) {
sendServerMessage("That does not look like a valid character.");
return;
}
changeCharacter(char_id);
}
void AOClient::cmdRandomChar(int argc, QStringList argv)
{
int char_id = genRand(0, server->characters.size() - 1);
changeCharacter(char_id);
}
void AOClient::cmdToggleGlobal(int argc, QStringList argv)
{
global_enabled = !global_enabled;
QString str_en = global_enabled ? "shown" : "hidden";
sendServerMessage("Global chat set to " + str_en);
}
void AOClient::cmdMods(int argc, QStringList argv)
{
QStringList entries;
QSettings config("config/config.ini", QSettings::IniFormat);
config.beginGroup("Options");
QString auth_type = config.value("auth", "simple").toString();
int online_count = 0;
for (AOClient* client : server->clients) {
if (client->authenticated) {
entries << "---";
if (auth_type != "simple")
entries << "Moderator: " + client->moderator_name;
entries << "OOC name: " + client->ooc_name;
entries << "ID: " + QString::number(client->id);
entries << "Area: " + QString::number(client->current_area);
entries << "Character: " + client->current_char;
online_count++;
}
}
entries << "---";
entries << "Total online: " << QString::number(online_count);
sendServerMessage(entries.join("\n"));
}
void AOClient::cmdHelp(int argc, QStringList argv)
{
QStringList entries;
entries << "Allowed commands:";
QMap<QString, CommandInfo>::const_iterator i;
for (i = commands.constBegin(); i!= commands.constEnd(); ++i) {
CommandInfo info = i.value();
if (checkAuth(info.acl_mask)) { // if we are allowed to use this command
entries << "/" + i.key();
}
}
sendServerMessage(entries.join("\n"));
}
void AOClient::cmdStatus(int argc, QStringList argv)
{
AreaData* area = server->areas[current_area];
QString arg = argv[0].toLower();
if (arg == "idle")
area->status = AreaData::IDLE;
else if (arg == "rp")
area->status = AreaData::RP;
else if (arg == "casing")
area->status = AreaData::CASING;
else if (arg == "looking-for-players" || arg == "lfp")
area->status = AreaData::LOOKING_FOR_PLAYERS;
else if (arg == "recess")
area->status = AreaData::RECESS;
else if (arg == "gaming")
area->status = AreaData::GAMING;
else {
sendServerMessage("That does not look like a valid status. Valid statuses are idle, rp, casing, lfp, recess, gaming");
return;
}
arup(ARUPType::STATUS, true);
}
void AOClient::cmdCurrentMusic(int argc, QStringList argv)
{
AreaData* area = server->areas[current_area];
if (area->current_music != "" && area->current_music != "~stop.mp3") // dummy track for stopping music
sendServerMessage("The current song is " + area->current_music + " played by " + area->music_played_by);
else
sendServerMessage("There is no music playing.");
}
void AOClient::cmdPM(int arc, QStringList argv)
{
bool ok;
int target_id = argv.takeFirst().toInt(&ok); // using takeFirst removes the ID from our list of arguments...
if (!ok) {
sendServerMessage("That does not look like a valid ID.");
return;
}
AOClient* target_client = server->getClientByID(target_id);
if (target_client == nullptr) {
sendServerMessage("No client with that ID found.");
return;
}
QString message = argv.join(" "); //...which means it will not end up as part of the message
target_client->sendServerMessage("Message from " + ooc_name + " (" + QString::number(id) + "): " + message);
}
void AOClient::cmdMOTD(int argc, QStringList argv)
{
if (argc == 0) {
sendServerMessage("=== MOTD ===\r\n" + server->MOTD + "\r\n=============");
}
else if (argc > 0) {
if (checkAuth(ACLFlags.value("MOTD"))) {
QString MOTD = argv.join(" ");
server->MOTD = MOTD;
sendServerMessage("MOTD has been changed.");
}
else {
sendServerMessage("You do not have permission to change the MOTD");
}
}
}
void AOClient::cmdAnnounce(int argc, QStringList argv)
{
sendServerBroadcast("=== Announcement ===\r\n" + argv.join(" ") + "\r\n=============");
}
void AOClient::cmdM(int argc, QStringList argv)
{
QString sender_name = ooc_name;
QString sender_message = argv.join(" ");
for (AOClient* client : server->clients) {
if (client->checkAuth(ACLFlags.value("MODCHAT")))
client->sendPacket("CT", {"[M]" + sender_name, sender_message});
}
return;
}
void AOClient::cmdGM(int argc, QStringList argv)
{
QString sender_name = ooc_name;
QString sender_area = server->area_names.value(current_area);
QString sender_message = argv.join(" ");
for (AOClient* client : server->clients) {
if (client->global_enabled) {
client->sendPacket("CT", {"[G][" + sender_area + "]" + "["+sender_name+"][M]", sender_message});
}
}
}
void AOClient::cmdMute(int argc, QStringList argv)
{
bool conv_ok = false;
int uid = argv[0].toInt(&conv_ok);
if (!conv_ok) {
sendServerMessage("Invalid user ID.");
return;
}
if (server->getClientByID(uid)->is_muted)
sendServerMessage("That player is already muted!");
else
sendServerMessage("Muted player.");
server->getClientByID(uid)->is_muted = true;
}
void AOClient::cmdUnmute(int argc, QStringList argv)
{
bool conv_ok = false;
int uid = argv[0].toInt(&conv_ok);
if (!conv_ok) {
sendServerMessage("Invalid user ID.");
return;
}
if (!server->getClientByID(uid)->is_muted)
sendServerMessage("That player is already unmuted!");
else
sendServerMessage("Unmuted player.");
server->getClientByID(uid)->is_muted = false;
}
void AOClient::cmdBans(int argc, QStringList argv)
{
QStringList recent_bans;
recent_bans << "Last 5 bans:";
recent_bans << "-----";
for (DBManager::BanInfo ban : server->db_manager->getRecentBans()) {
QString banned_until;
if (ban.duration == -2)
banned_until = "The heat death of the universe";
else
banned_until = QDateTime::fromSecsSinceEpoch(ban.time).addSecs(ban.duration).toString("dd.MM.yyyy, hh:mm");
recent_bans << "Affected IPID: " + ban.ipid;
recent_bans << "Affected HDID: " + ban.hdid;
recent_bans << "Reason for ban: " + ban.reason;
recent_bans << "Date of ban: " + QDateTime::fromSecsSinceEpoch(ban.time).toString("dd.MM.yyyy, hh:mm");
recent_bans << "Ban lasts until: " + banned_until;
recent_bans << "-----";
}
sendServerMessage(recent_bans.join("\n"));
}
void AOClient::cmdUnBan(int argc, QStringList argv)
{
bool ok;
int target_ban = argv[0].toInt(&ok);
if (!ok) {
sendServerMessage("Invalid ban ID.");
return;
}
else if (server->db_manager->invalidateBan(target_ban))
sendServerMessage("Successfully invalidated ban " + argv[0] + ".");
else
sendServerMessage("Couldn't invalidate ban " + argv[0] + ", are you sure it exists?");
}
void AOClient::cmdSubTheme(int argc, QStringList argv)
{
QString subtheme = argv.join(" ");
for (AOClient* client : server->clients) {
if (client->current_area == current_area)
client->sendPacket("ST", {subtheme, "1"});
}
sendServerMessageArea("Subtheme was set to " + subtheme);
}
void AOClient::cmdAbout(int argc, QStringList argv)
{
sendPacket("CT", {"The akashi dev team", "Thank you for using akashi! Made with love by scatterflower, with help from in1tiate and Salanto. akashi " + QCoreApplication::applicationVersion()});
}
QStringList AOClient::buildAreaList(int area_idx) QStringList AOClient::buildAreaList(int area_idx)
{ {
QStringList entries; QStringList entries;
@ -658,7 +1051,7 @@ QStringList AOClient::buildAreaList(int area_idx)
default: default:
break; break;
} }
entries.append("[" + QString::number(area->player_count) + " users][" + area->status + "]"); entries.append("[" + QString::number(area->player_count) + " users][" + QVariant::fromValue(area->status).toString().replace("_", "-") + "]");
for (AOClient* client : server->clients) { for (AOClient* client : server->clients) {
if (client->current_area == area_idx && client->joined) { if (client->current_area == area_idx && client->joined) {
QString char_entry = "[" + QString::number(client->id) + "] " + client->current_char; QString char_entry = "[" + QString::number(client->id) + "] " + client->current_char;
@ -757,3 +1150,45 @@ QString AOClient::getAreaTimer(int area_idx, QTimer* timer)
} }
} }
long long AOClient::parseTime(QString input)
{
QRegularExpression regex("(?:(?:(?<year>.*?)y)*(?:(?<week>.*?)w)*(?:(?<day>.*?)d)*(?:(?<hr>.*?)h)*(?:(?<min>.*?)m)*(?:(?<sec>.*?)s)*)");
QRegularExpressionMatch match = regex.match(input);
QString str_year, str_week, str_hour, str_day, str_minute, str_second;
int year, week, day, hour, minute, second;
str_year = match.captured("year");
str_week = match.captured("week");
str_day = match.captured("day");
str_hour = match.captured("hr");
str_minute = match.captured("min");
str_second = match.captured("sec");
bool is_well_formed = false;
QString concat_str(str_year + str_week + str_day + str_hour + str_minute + str_second);
concat_str.toInt(&is_well_formed);
if (!is_well_formed) {
return -1;
}
year = str_year.toInt();
week = str_week.toInt();
day = str_day.toInt();
hour = str_hour.toInt();
minute = str_minute.toInt();
second = str_second.toInt();
long long total = 0;
total += 31622400 * year;
total += 604800 * week;
total += 86400 * day;
total += 3600 * hour;
total += 60 * minute;
total += second;
if (total < 0)
return -1;
return total;
}

View File

@ -139,8 +139,8 @@ bool ConfigManager::loadServerSettings(server_settings* settings)
config.value("port", "27016").toInt(&port_conversion_success); config.value("port", "27016").toInt(&port_conversion_success);
settings->ws_port = settings->ws_port =
config.value("webao_port", "27017").toInt(&ws_port_conversion_success); config.value("webao_port", "27017").toInt(&ws_port_conversion_success);
settings->local_port = settings->ms_port =
config.value("port", "27016").toInt(&local_port_conversion_success); config.value("ms_port", "27016").toInt(&local_port_conversion_success);
settings->name = config.value("server_name", "My First Server").toString(); settings->name = config.value("server_name", "My First Server").toString();
settings->description = settings->description =
config.value("server_description", "This is my flashy new server") config.value("server_description", "This is my flashy new server")

View File

@ -24,32 +24,52 @@ DBManager::DBManager() :
db.setDatabaseName("config/akashi.db"); db.setDatabaseName("config/akashi.db");
if (!db.open()) if (!db.open())
qCritical() << "Database Error:" << db.lastError(); qCritical() << "Database Error:" << db.lastError();
QSqlQuery create_ban_table("CREATE TABLE IF NOT EXISTS bans ('ID' INTEGER, 'IPID' TEXT, 'HDID' TEXT, 'IP' TEXT, 'TIME' INTEGER, 'REASON' TEXT, PRIMARY KEY('ID' AUTOINCREMENT))"); QSqlQuery create_ban_table("CREATE TABLE IF NOT EXISTS bans ('ID' INTEGER, 'IPID' TEXT, 'HDID' TEXT, 'IP' TEXT, 'TIME' INTEGER, 'REASON' TEXT, 'DURATION' INTEGER, PRIMARY KEY('ID' AUTOINCREMENT))");
QSqlQuery create_user_table("CREATE TABLE IF NOT EXISTS users ('ID' INTEGER, 'USERNAME' TEXT, 'SALT' TEXT, 'PASSWORD' TEXT, 'ACL' TEXT, PRIMARY KEY('ID' AUTOINCREMENT))"); QSqlQuery create_user_table("CREATE TABLE IF NOT EXISTS users ('ID' INTEGER, 'USERNAME' TEXT, 'SALT' TEXT, 'PASSWORD' TEXT, 'ACL' TEXT, PRIMARY KEY('ID' AUTOINCREMENT))");
} }
bool DBManager::isIPBanned(QHostAddress ip) bool DBManager::isIPBanned(QHostAddress ip)
{ {
QSqlQuery query; QSqlQuery query;
query.prepare("SELECT ID FROM BANS WHERE IP = ?"); query.prepare("SELECT TIME FROM BANS WHERE IP = ? ORDER BY TIME DESC");
query.addBindValue(ip.toString()); query.addBindValue(ip.toString());
query.exec(); query.exec();
return query.first(); if (query.first()) {
long long duration = getBanDuration(ip);
long long ban_time = query.value(0).toLongLong();
if (duration == -2)
return true;
long long current_time = QDateTime::currentDateTime().toSecsSinceEpoch();
if (ban_time + duration > current_time)
return true;
else return false;
}
else return false;
} }
bool DBManager::isHDIDBanned(QString hdid) bool DBManager::isHDIDBanned(QString hdid)
{ {
QSqlQuery query; QSqlQuery query;
query.prepare("SELECT ID FROM BANS WHERE HDID = ?"); query.prepare("SELECT TIME FROM BANS WHERE HDID = ? ORDER BY TIME DESC");
query.addBindValue(hdid); query.addBindValue(hdid);
query.exec(); query.exec();
return query.first(); if (query.first()) {
long long duration = getBanDuration(hdid);
long long ban_time = query.value(0).toLongLong();
if (duration == -2)
return true;
long long current_time = QDateTime::currentDateTime().toSecsSinceEpoch();
if (ban_time + duration > current_time)
return true;
else return false;
}
else return false;
} }
QString DBManager::getBanReason(QHostAddress ip) QString DBManager::getBanReason(QHostAddress ip)
{ {
QSqlQuery query; QSqlQuery query;
query.prepare("SELECT REASON FROM BANS WHERE IP = ?"); query.prepare("SELECT REASON FROM BANS WHERE IP = ? ORDER BY TIME DESC");
query.addBindValue(ip.toString()); query.addBindValue(ip.toString());
query.exec(); query.exec();
if (query.first()) { if (query.first()) {
@ -63,7 +83,7 @@ QString DBManager::getBanReason(QHostAddress ip)
QString DBManager::getBanReason(QString hdid) QString DBManager::getBanReason(QString hdid)
{ {
QSqlQuery query; QSqlQuery query;
query.prepare("SELECT REASON FROM BANS WHERE HDID = ?"); query.prepare("SELECT REASON FROM BANS WHERE HDID = ? ORDER BY TIME DESC");
query.addBindValue(hdid); query.addBindValue(hdid);
query.exec(); query.exec();
if (query.first()) { if (query.first()) {
@ -74,19 +94,116 @@ QString DBManager::getBanReason(QString hdid)
} }
} }
void DBManager::addBan(QString ipid, QHostAddress ip, QString hdid, unsigned long time, QString reason) long long DBManager::getBanDuration(QString hdid)
{ {
QSqlQuery query; QSqlQuery query;
query.prepare("INSERT INTO BANS(IPID, HDID, IP, TIME, REASON) VALUES(?, ?, ?, ?, ?)"); query.prepare("SELECT DURATION FROM BANS WHERE HDID = ? ORDER BY TIME DESC");
query.addBindValue(ipid);
query.addBindValue(hdid); query.addBindValue(hdid);
query.exec();
if (query.first()) {
return query.value(0).toLongLong();
}
else {
return -1;
}
}
long long DBManager::getBanDuration(QHostAddress ip)
{
QSqlQuery query;
query.prepare("SELECT DURATION FROM BANS WHERE IP = ? ORDER BY TIME DESC");
query.addBindValue(ip.toString()); query.addBindValue(ip.toString());
query.addBindValue(QString::number(time)); query.exec();
query.addBindValue(reason); if (query.first()) {
return query.value(0).toLongLong();
}
else {
return -1;
}
}
int DBManager::getBanID(QString hdid)
{
QSqlQuery query;
query.prepare("SELECT ID FROM BANS WHERE HDID = ?");
query.addBindValue(hdid);
query.exec();
if (query.first()) {
return query.value(0).toInt();
}
else {
return -1;
}
}
int DBManager::getBanID(QHostAddress ip)
{
QSqlQuery query;
query.prepare("SELECT ID FROM BANS WHERE IP = ?");
query.addBindValue(ip.toString());
query.exec();
if (query.first()) {
return query.value(0).toInt();
}
else {
return -1;
}
}
QList<DBManager::BanInfo> DBManager::getRecentBans()
{
QList<BanInfo> return_list;
QSqlQuery query;
query.prepare("SELECT TOP(5) * FROM BANS ORDER BY TIME DESC");
query.setForwardOnly(true);
query.exec();
while (query.next()) {
BanInfo ban;
ban.ipid = query.value(0).toString();
ban.hdid = query.value(1).toString();
ban.ip = QHostAddress(query.value(2).toString());
ban.time = static_cast<unsigned long>(query.value(3).toULongLong());
ban.reason = query.value(4).toString();
ban.duration = query.value(5).toLongLong();
return_list.append(ban);
}
std::reverse(return_list.begin(), return_list.end());
return return_list;
}
void DBManager::addBan(BanInfo ban)
{
QSqlQuery query;
query.prepare("INSERT INTO BANS(IPID, HDID, IP, TIME, REASON, DURATION) VALUES(?, ?, ?, ?, ?, ?)");
query.addBindValue(ban.ipid);
query.addBindValue(ban.hdid);
query.addBindValue(ban.ip.toString());
query.addBindValue(QString::number(ban.time));
query.addBindValue(ban.reason);
query.addBindValue(ban.duration);
if (!query.exec()) if (!query.exec())
qDebug() << "SQL Error:" << query.lastError().text(); qDebug() << "SQL Error:" << query.lastError().text();
} }
bool DBManager::invalidateBan(int id)
{
QSqlQuery ban_exists;
ban_exists.prepare("SELECT DURATION FROM bans WHERE ID = ?");
ban_exists.addBindValue(id);
ban_exists.exec();
if (ban_exists.first())
return false;
QSqlQuery query;
query.prepare("UPDATE bans SET DURATION = 0 WHERE ID = ?");
query.addBindValue(id);
query.exec();
return true;
}
bool DBManager::createUser(QString username, QString salt, QString password, unsigned long long acl) bool DBManager::createUser(QString username, QString salt, QString password, unsigned long long acl)
{ {
QSqlQuery username_exists; QSqlQuery username_exists;
@ -115,6 +232,23 @@ bool DBManager::createUser(QString username, QString salt, QString password, uns
return true; return true;
} }
bool DBManager::deleteUser(QString username)
{
QSqlQuery username_exists;
username_exists.prepare("SELECT ACL FROM users WHERE USERNAME = ?");
username_exists.addBindValue(username);
username_exists.exec();
if (username_exists.first())
return false;
QSqlQuery query;
query.prepare("DELETE FROM users WHERE USERNAME = ?");
username_exists.addBindValue(username);
username_exists.exec();
return true;
}
unsigned long long DBManager::getACL(QString moderator_name) unsigned long long DBManager::getACL(QString moderator_name)
{ {
if (moderator_name == "") if (moderator_name == "")

View File

@ -36,7 +36,7 @@ int main(int argc, char* argv[])
{ {
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
QCoreApplication::setApplicationName("akashi"); QCoreApplication::setApplicationName("akashi");
QCoreApplication::setApplicationVersion("0.0.1"); QCoreApplication::setApplicationVersion("apricot r2 prerelease");
std::atexit(cleanup); std::atexit(cleanup);
ConfigManager config_manager; ConfigManager config_manager;
@ -55,8 +55,8 @@ int main(int argc, char* argv[])
else { else {
if (settings.advertise_server) { if (settings.advertise_server) {
advertiser = advertiser =
new Advertiser(settings.ms_ip, settings.port, new Advertiser(settings.ms_ip, settings.ms_port,
settings.ws_port, settings.local_port, settings.ws_port, settings.port,
settings.name, settings.description); settings.name, settings.description);
advertiser->contactMasterServer(); advertiser->contactMasterServer();
} }

View File

@ -26,9 +26,9 @@ void AOClient::pktDefault(AreaData* area, int argc, QStringList argv, AOPacket p
void AOClient::pktHardwareId(AreaData* area, int argc, QStringList argv, AOPacket packet) void AOClient::pktHardwareId(AreaData* area, int argc, QStringList argv, AOPacket packet)
{ {
setHwid(argv[0]); hwid = argv[0];
if(server->db_manager->isHDIDBanned(getHwid())) { if(server->db_manager->isHDIDBanned(hwid)) {
sendPacket("BD", {server->db_manager->getBanReason(getHwid())}); sendPacket("BD", {server->db_manager->getBanReason(hwid)});
socket->close(); socket->close();
return; return;
} }
@ -54,6 +54,16 @@ void AOClient::pktSoftwareId(AreaData* area, int argc, QStringList argv, AOPacke
"y_offset", "expanded_desk_mods", "auth_packet" "y_offset", "expanded_desk_mods", "auth_packet"
}; };
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 match = rx.match(version.string);
if (match.hasMatch()) {
version.release = match.captured(1).toInt();
version.major = match.captured(2).toInt();
version.minor = match.captured(3).toInt();
}
sendPacket("PN", {QString::number(server->player_count), max_players}); sendPacket("PN", {QString::number(server->player_count), max_players});
sendPacket("FL", feature_list); sendPacket("FL", feature_list);
} }
@ -78,7 +88,7 @@ void AOClient::pktRequestMusic(AreaData* area, int argc, QStringList argv, AOPac
void AOClient::pktLoadingDone(AreaData* area, int argc, QStringList argv, AOPacket packet) void AOClient::pktLoadingDone(AreaData* area, int argc, QStringList argv, AOPacket packet)
{ {
if (getHwid() == "") { if (hwid == "") {
// No early connecting! // No early connecting!
socket->close(); socket->close();
return; return;
@ -92,16 +102,37 @@ void AOClient::pktLoadingDone(AreaData* area, int argc, QStringList argv, AOPack
area->player_count++; area->player_count++;
joined = true; joined = true;
server->updateCharsTaken(area); server->updateCharsTaken(area);
fullArup(); // Give client all the area data
arup(ARUPType::PLAYER_COUNT, true); // Tell everyone there is a new player arup(ARUPType::PLAYER_COUNT, true); // Tell everyone there is a new player
sendEvidenceList(area); sendEvidenceList(area);
sendPacket("HP", {"1", QString::number(area->def_hp)}); sendPacket("HP", {"1", QString::number(area->def_hp)});
sendPacket("HP", {"2", QString::number(area->pro_hp)}); sendPacket("HP", {"2", QString::number(area->pro_hp)});
sendPacket("FA", server->area_names); sendPacket("FA", server->area_names);
sendPacket("BN", {area->background});
sendPacket("OPPASS", {"DEADBEEF"}); sendPacket("OPPASS", {"DEADBEEF"});
sendPacket("DONE"); sendPacket("DONE");
sendPacket("BN", {area->background});
sendServerMessage("=== MOTD ===\r\n" + server->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"});
}
for (QTimer* timer : area->timers) {
int timer_id = area->timers.indexOf(timer) + 1;
if (timer->isActive()) {
sendPacket("TI", {QString::number(timer_id), "2"});
sendPacket("TI", {QString::number(timer_id), "0", QString::number(QTime(0,0).msecsTo(QTime(0,0).addMSecs(timer->remainingTime())))});
}
else {
sendPacket("TI", {QString::number(timer_id), "3"});
}
}
} }
void AOClient::pktCharPassword(AreaData* area, int argc, QStringList argv, AOPacket packet) void AOClient::pktCharPassword(AreaData* area, int argc, QStringList argv, AOPacket packet)
@ -118,52 +149,16 @@ void AOClient::pktSelectChar(AreaData* area, int argc, QStringList argv, AOPacke
return; return;
} }
if (current_char != "") { changeCharacter(char_id);
area->characters_taken[current_char] = false;
}
if(char_id > server->characters.length())
return;
if (char_id >= 0) {
QString char_selected = server->characters[char_id];
bool taken = area->characters_taken.value(char_selected);
if (taken || char_selected == "")
return;
area->characters_taken[char_selected] = true;
current_char = char_selected;
}
else {
current_char = "";
}
pos = "";
server->updateCharsTaken(area);
sendPacket("PV", {QString::number(id), "CID", argv[1]});
fullArup();
if (server->timer->isActive()) {
sendPacket("TI", {QString::number(0), QString::number(2)});
sendPacket("TI", {QString::number(0), QString::number(0), QString::number(QTime(0,0).msecsTo(QTime(0,0).addMSecs(server->timer->remainingTime())))});
}
else {
sendPacket("TI", {QString::number(0), QString::number(3)});
}
for (QTimer* timer : area->timers) {
int timer_id = area->timers.indexOf(timer) + 1;
if (timer->isActive()) {
sendPacket("TI", {QString::number(timer_id), QString::number(2)});
sendPacket("TI", {QString::number(timer_id), QString::number(0), QString::number(QTime(0,0).msecsTo(QTime(0,0).addMSecs(timer->remainingTime())))});
}
else {
sendPacket("TI", {QString::number(timer_id), QString::number(3)});
}
}
} }
void AOClient::pktIcChat(AreaData* area, int argc, QStringList argv, AOPacket packet) void AOClient::pktIcChat(AreaData* area, int argc, QStringList argv, AOPacket packet)
{ {
if (is_muted) {
sendServerMessage("You cannot speak while muted.");
return;
}
AOPacket validated_packet = validateIcPacket(packet); AOPacket validated_packet = validateIcPacket(packet);
if (validated_packet.header == "INVALID") if (validated_packet.header == "INVALID")
return; return;
@ -177,7 +172,10 @@ void AOClient::pktIcChat(AreaData* area, int argc, QStringList argv, AOPacket pa
void AOClient::pktOocChat(AreaData* area, int argc, QStringList argv, AOPacket packet) void AOClient::pktOocChat(AreaData* area, int argc, QStringList argv, AOPacket packet)
{ {
ooc_name = dezalgo(argv[0]); ooc_name = dezalgo(argv[0]).replace(QRegExp("\\[|\\]|\\{|\\}|\\#|\\$|\\%|\\&"), ""); // no fucky wucky shit here
if (ooc_name.isEmpty() || ooc_name == server->getServerName()) // impersonation & empty name protection
return;
QString message = dezalgo(argv[1]); QString message = dezalgo(argv[1]);
AOPacket final_packet("CT", {ooc_name, message, "0"}); AOPacket final_packet("CT", {ooc_name, message, "0"});
if(message.at(0) == '/') { if(message.at(0) == '/') {
@ -214,9 +212,21 @@ void AOClient::pktChangeMusic(AreaData* area, int argc, QStringList argv, AOPack
QString argument = argv[0]; QString argument = argv[0];
for (QString song : server->music_list) { for (QString song : server->music_list) {
if (song == argument) { if (song == argument || song == "~stop.mp3") { // ~stop.mp3 is a dummy track used by 2.9+
// We have a song here // We have a song here
AOPacket music_change("MC", {song, argv[1], argv[2], "1", "0", argv[3]}); QString effects;
if (argc >= 4)
effects = argv[3];
else
effects = "0";
QString final_song;
if (!argument.contains("."))
final_song = "~stop.mp3";
else
final_song = argument;
AOPacket music_change("MC", {final_song, argv[1], showname, "1", "0", effects});
area->current_music = final_song;
area->music_played_by = showname;
server->broadcast(music_change, current_area); server->broadcast(music_change, current_area);
return; return;
} }
@ -265,6 +275,7 @@ void AOClient::pktWebSocketIp(AreaData* area, int argc, QStringList argv, AOPack
qDebug() << "ws ip set to" << argv[0]; qDebug() << "ws ip set to" << argv[0];
#endif #endif
remote_ip = QHostAddress(argv[0]); remote_ip = QHostAddress(argv[0]);
calculateIpid();
} }
} }
@ -280,6 +291,8 @@ void AOClient::pktModCall(AreaData* area, int argc, QStringList argv, AOPacket p
void AOClient::pktAddEvidence(AreaData* area, int argc, QStringList argv, AOPacket packet) void AOClient::pktAddEvidence(AreaData* area, int argc, QStringList argv, AOPacket packet)
{ {
if (!checkEvidenceAccess(area))
return;
AreaData::Evidence evi = {argv[0], argv[1], argv[2]}; AreaData::Evidence evi = {argv[0], argv[1], argv[2]};
area->evidence.append(evi); area->evidence.append(evi);
sendEvidenceList(area); sendEvidenceList(area);
@ -287,9 +300,11 @@ void AOClient::pktAddEvidence(AreaData* area, int argc, QStringList argv, AOPack
void AOClient::pktRemoveEvidence(AreaData* area, int argc, QStringList argv, AOPacket packet) void AOClient::pktRemoveEvidence(AreaData* area, int argc, QStringList argv, AOPacket packet)
{ {
if (!checkEvidenceAccess(area))
return;
bool is_int = false; bool is_int = false;
int idx = argv[0].toInt(&is_int); int idx = argv[0].toInt(&is_int);
if (is_int) { if (is_int && idx <= area->evidence.size() && idx >= 0) {
area->evidence.removeAt(idx); area->evidence.removeAt(idx);
} }
sendEvidenceList(area); sendEvidenceList(area);
@ -297,28 +312,49 @@ void AOClient::pktRemoveEvidence(AreaData* area, int argc, QStringList argv, AOP
void AOClient::pktEditEvidence(AreaData* area, int argc, QStringList argv, AOPacket packet) void AOClient::pktEditEvidence(AreaData* area, int argc, QStringList argv, AOPacket packet)
{ {
if (!checkEvidenceAccess(area))
return;
bool is_int = false; bool is_int = false;
int idx = argv[0].toInt(&is_int); int idx = argv[0].toInt(&is_int);
AreaData::Evidence evi = {argv[1], argv[2], argv[3]}; AreaData::Evidence evi = {argv[1], argv[2], argv[3]};
if (is_int) { if (is_int && idx <= area->evidence.size() && idx >= 0) {
area->evidence.replace(idx, evi); area->evidence.replace(idx, evi);
} }
sendEvidenceList(area); sendEvidenceList(area);
} }
void AOClient::sendEvidenceList(AreaData* area) void AOClient::sendEvidenceList(AreaData* area)
{
for (AOClient* client : server->clients) {
if (client->current_area == current_area)
client->updateEvidenceList(area);
}
}
void AOClient::updateEvidenceList(AreaData* area)
{ {
QStringList evidence_list; QStringList evidence_list;
QString evidence_format("%1&%2&%3"); QString evidence_format("%1&%2&%3");
for (AreaData::Evidence evidence : area->evidence) { for (AreaData::Evidence evidence : area->evidence) {
if (!checkAuth(ACLFlags.value("CM")) && area->evi_mod == AreaData::EvidenceMod::HIDDEN_CM) {
QRegularExpression regex("<owner=(.*?)>");
QRegularExpressionMatch match = regex.match(evidence.description);
if (match.hasMatch()) {
QStringList owners = match.captured(1).split(",");
if (!owners.contains("all", Qt::CaseSensitivity::CaseInsensitive) && !owners.contains(pos, Qt::CaseSensitivity::CaseInsensitive)) {
continue;
}
}
// no match = show it to all
}
evidence_list.append(evidence_format evidence_list.append(evidence_format
.arg(evidence.name) .arg(evidence.name)
.arg(evidence.description) .arg(evidence.description)
.arg(evidence.image)); .arg(evidence.image));
} }
server->broadcast(AOPacket("LE", evidence_list), current_area); sendPacket(AOPacket("LE", evidence_list));
} }
AOPacket AOClient::validateIcPacket(AOPacket packet) AOPacket AOClient::validateIcPacket(AOPacket packet)
@ -362,10 +398,10 @@ AOPacket AOClient::validateIcPacket(AOPacket packet)
if (current_char != incoming_args[2].toString()) { if (current_char != incoming_args[2].toString()) {
// Selected char is different from supplied folder name // Selected char is different from supplied folder name
// This means the user is INI-swapped // This means the user is INI-swapped
// TODO: ini swap locking if (!area->iniswap_allowed) {
// if no iniswap allowed then if (!server->characters.contains(incoming_args[2].toString()))
// if (!server->characters.contains(incoming_args[2].toString())) return invalid;
// return invalid; }
qDebug() << "INI swap detected from " << getIpid(); qDebug() << "INI swap detected from " << getIpid();
} }
args.append(incoming_args[2].toString()); args.append(incoming_args[2].toString());
@ -385,6 +421,10 @@ AOPacket AOClient::validateIcPacket(AOPacket packet)
// side // side
// this is validated clientside so w/e // this is validated clientside so w/e
args.append(incoming_args[5].toString()); args.append(incoming_args[5].toString());
if (pos != incoming_args[5].toString()) {
pos = incoming_args[5].toString();
updateEvidenceList(server->areas[current_area]);
}
// sfx name // sfx name
args.append(incoming_args[6].toString()); args.append(incoming_args[6].toString());
@ -453,8 +493,9 @@ AOPacket AOClient::validateIcPacket(AOPacket packet)
// 2.6 packet extensions // 2.6 packet extensions
if (incoming_args.length() > 15) { if (incoming_args.length() > 15) {
// showname // showname
QString showname = dezalgo(incoming_args[15].toString().trimmed()); QString incoming_showname = dezalgo(incoming_args[15].toString().trimmed());
args.append(showname); args.append(incoming_showname);
showname = incoming_showname;
// other char id // other char id
// things get a bit hairy here // things get a bit hairy here
@ -489,8 +530,17 @@ AOPacket AOClient::validateIcPacket(AOPacket packet)
// self offset // self offset
offset = incoming_args[17].toString(); offset = incoming_args[17].toString();
args.append(offset); // versions 2.6-2.8 cannot validate y-offset so we send them just the x-offset
args.append(other_offset); if ((version.release == 2) && (version.major == 6 || version.major == 7 || version.major == 8)) {
QString x_offset = offset.split("&")[0];
args.append(x_offset);
QString other_x_offset = other_offset.split("&")[0];
args.append(other_x_offset);
}
else {
args.append(offset);
args.append(other_offset);
}
args.append(other_flip); args.append(other_flip);
// noninterrupting preanim // noninterrupting preanim
@ -549,3 +599,18 @@ QString AOClient::dezalgo(QString p_text)
QString filtered = p_text.replace(rxp, ""); QString filtered = p_text.replace(rxp, "");
return filtered; return filtered;
} }
bool AOClient::checkEvidenceAccess(AreaData *area)
{
switch(area->evi_mod) {
case AreaData::EvidenceMod::FFA:
return true;
case AreaData::EvidenceMod::CM:
case AreaData::EvidenceMod::HIDDEN_CM:
return checkAuth(ACLFlags.value("CM"));
case AreaData::EvidenceMod::MOD:
return authenticated;
default:
return false;
}
}

View File

@ -51,6 +51,8 @@ void Server::start()
qDebug() << "Server listening on" << port; qDebug() << "Server listening on" << port;
} }
MOTD = config.value("motd","MOTD is not set.").toString();
proxy = new WSProxy(port, ws_port, this); proxy = new WSProxy(port, ws_port, this);
if(ws_port != -1) if(ws_port != -1)
proxy->start(); proxy->start();
@ -79,10 +81,20 @@ void Server::start()
bg_file.close(); bg_file.close();
QSettings areas_ini("config/areas.ini", QSettings::IniFormat); QSettings areas_ini("config/areas.ini", QSettings::IniFormat);
area_names = areas_ini.childGroups(); area_names = areas_ini.childGroups(); // invisibly does a lexicographical sort, because Qt is great like that
for (int i = 0; i < area_names.length(); i++) { std::sort(area_names.begin(), area_names.end(), [] (const QString &a, const QString &b) {return a.split(":")[0].toInt() < b.split(":")[0].toInt();});
QString area_name = area_names[i]; QStringList sanitized_area_names;
areas.insert(i, new AreaData(characters, area_name, i)); QStringList raw_area_names = area_names;
for (QString area_name : area_names) {
QStringList name_split = area_name.split(":");
name_split.removeFirst();
QString area_name_sanitized = name_split.join(":");
sanitized_area_names.append(area_name_sanitized);
}
area_names = sanitized_area_names;
for (int i = 0; i < raw_area_names.length(); i++) {
QString area_name = raw_area_names[i];
areas.insert(i, new AreaData(area_name, i));
} }
} }
@ -130,8 +142,8 @@ void Server::clientConnected()
void Server::updateCharsTaken(AreaData* area) void Server::updateCharsTaken(AreaData* area)
{ {
QStringList chars_taken; QStringList chars_taken;
for (QString cur_char : area->characters_taken.keys()) { for (QString cur_char : characters) {
chars_taken.append(area->characters_taken.value(cur_char) chars_taken.append(area->characters_taken.contains(getCharID(cur_char))
? QStringLiteral("-1") ? QStringLiteral("-1")
: QStringLiteral("0")); : QStringLiteral("0"));
} }
@ -173,13 +185,14 @@ int Server::getDiceValue(QString value_type)
return value; return value;
} }
AOClient* Server::getClient(QString ipid) QList<AOClient*> Server::getClientsByIpid(QString ipid)
{ {
QList<AOClient*> return_clients;
for (AOClient* client : clients) { for (AOClient* client : clients) {
if (client->getIpid() == ipid) if (client->getIpid() == ipid)
return client; return_clients.append(client);
} }
return nullptr; return return_clients;
} }
AOClient* Server::getClientByID(int id) AOClient* Server::getClientByID(int id)
@ -191,6 +204,16 @@ AOClient* Server::getClientByID(int id)
return nullptr; return nullptr;
} }
int Server::getCharID(QString char_name)
{
for (QString character : characters) {
if (character.toLower() == char_name.toLower()) {
return characters.indexOf(QRegExp(character, Qt::CaseInsensitive));
}
}
return -1; // character does not exist
}
Server::~Server() Server::~Server()
{ {
for (AOClient* client : clients) { for (AOClient* client : clients) {