Merge pull request #20 from AttorneyOnline/in1tiate/commands-uwu

Add /help, /status, /forcepos, /currentmusic, /pm; 2.9 compatibility fixes
This commit is contained in:
scatterflower 2021-03-11 00:32:41 -06:00 committed by GitHub
commit 5b328859cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 224 additions and 42 deletions

View File

@ -59,6 +59,13 @@ class AOClient : public QObject {
QString ooc_name = "";
QString showname = "";
bool global_enabled = true;
struct ClientVersion {
QString string;
int release = -1;
int major = -1;
int minor = -1;
};
ClientVersion version;
QMap<QString, unsigned long long> ACLFlags {
{"NONE", 0ULL},
@ -99,6 +106,7 @@ class AOClient : public QObject {
void handleCommand(QString command, int argc, QStringList argv);
void changeArea(int new_area);
void changeCharacter(int char_id);
void changePosition(QString new_pos);
void arup(ARUPType type, bool broadcast);
void fullArup();
void sendServerMessage(QString message);
@ -171,6 +179,7 @@ class AOClient : public QObject {
//// Commands
void cmdDefault(int argc, QStringList argv);
void cmdHelp(int argc, QStringList argv);
// Authentication
void cmdLogin(int argc, QStringList argv);
void cmdChangeAuth(int argc, QStringList argv);
@ -196,6 +205,8 @@ class AOClient : public QObject {
void cmdSetBackground(int argc, QStringList argv);
void cmdBgLock(int argc, QStringList argv);
void cmdBgUnlock(int argc, QStringList argv);
void cmdStatus(int argc, QStringList argv);
void cmdCurrentMusic(int argc, QStringList argv);
// Moderation
void cmdMods(int argc, QStringList argv);
void cmdBan(int argc, QStringList argv);
@ -211,10 +222,12 @@ class AOClient : public QObject {
void cmdTimer(int argc, QStringList argv);
// Messaging/Client
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 cmdToggleGlobal(int argc, QStringList argv);
void cmdPM(int argc, QStringList argv);
// Command helper functions
QString getAreaTimer(int area_idx, QTimer* timer);
@ -276,6 +289,11 @@ class AOClient : public QObject {
{"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}},
};
QString partial_packet;

View File

@ -28,7 +28,8 @@
#include <QElapsedTimer>
class Logger;
class AreaData {
class AreaData : public QObject {
Q_OBJECT
public:
AreaData(QStringList p_characters, QString p_name, int p_index);
@ -43,7 +44,16 @@ class AreaData {
QMap<QString, bool> characters_taken;
QList<Evidence> evidence;
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> invited;
enum LockStatus {
@ -51,6 +61,7 @@ class AreaData {
LOCKED,
SPECTATABLE
};
Q_ENUM(LockStatus);
LockStatus locked;
QString background;
bool is_protected;
@ -60,6 +71,8 @@ class AreaData {
QString document;
int def_hp;
int pro_hp;
QString current_music;
QString music_played_by;
Logger* logger;
};

View File

@ -193,6 +193,13 @@ void AOClient::changeCharacter(int 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)
{
CommandInfo info = commands.value(command, {false, -1, &AOClient::cmdDefault});
@ -215,42 +222,38 @@ void AOClient::arup(ARUPType type, bool broadcast)
QStringList arup_data;
arup_data.append(QString::number(type));
for (AreaData* area : server->areas) {
if (type == ARUPType::PLAYER_COUNT) {
arup_data.append(QString::number(area->player_count));
}
else if (type == ARUPType::STATUS) {
arup_data.append(area->status);
}
else if (type == ARUPType::CM) {
if (area->owners.isEmpty())
arup_data.append("FREE");
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);
switch(type) {
case ARUPType::PLAYER_COUNT: {
arup_data.append(QString::number(area->player_count));
break;
}
case ARUPType::STATUS: {
QString area_status = QVariant::fromValue(area->status).toString().replace("_", "-"); // LOOKING_FOR_PLAYERS to LOOKING-FOR-PLAYERS
arup_data.append(area_status);
break;
}
case ARUPType::CM: {
if (area->owners.isEmpty())
arup_data.append("FREE");
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)
server->broadcast(AOPacket("ARUP", arup_data));

View File

@ -33,7 +33,7 @@ AreaData::AreaData(QStringList characters, QString p_name, int p_index)
areas_ini.endGroup();
player_count = 0;
locked = FREE;
status = "FREE";
status = IDLE;
def_hp = 10;
pro_hp = 10;
document = "No document.";

View File

@ -364,8 +364,42 @@ void AOClient::cmdLogout(int argc, QStringList argv)
void AOClient::cmdPos(int argc, QStringList argv)
{
pos = argv[0];
sendServerMessage("Position changed to " + pos + ".");
changePosition(argv[0]);
}
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)
@ -483,6 +517,10 @@ void AOClient::cmdInvite(int argc, QStringList argv)
sendServerMessage("That does not look like a valid ID.");
return;
}
else if (server->getClientByID(invited_id) == nullptr) {
sendServerMessage("No client with that ID found.");
return;
}
else if (area->invited.contains(invited_id)) {
sendServerMessage("That ID is already on the invite list.");
return;
@ -500,6 +538,10 @@ void AOClient::cmdUnInvite(int argc, QStringList argv)
sendServerMessage("That does not look like a valid ID.");
return;
}
else if (server->getClientByID(uninvited_id) == nullptr) {
sendServerMessage("No client with that ID found.");
return;
}
else if (area->owners.contains(uninvited_id)) {
sendServerMessage("You cannot uninvite a CM!");
return;
@ -653,7 +695,11 @@ void AOClient::cmdArea(int argc, QStringList argv)
void AOClient::cmdPlay(int argc, QStringList argv)
{
sendPacket("MC", {argv.join(" "), QString::number(server->getCharID(current_char)), showname, "1", "0"});
AreaData* area = server->areas[current_area];
QString song = argv.join(" ");
area->current_music = song;
area->music_played_by = showname;
sendPacket("MC", {song, QString::number(server->getCharID(current_char)), showname, "1", "0"});
}
void AOClient::cmdAreaKick(int argc, QStringList argv)
@ -665,6 +711,10 @@ void AOClient::cmdAreaKick(int argc, QStringList argv)
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.");
}
@ -716,6 +766,69 @@ void AOClient::cmdMods(int argc, QStringList argv)
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);
}
QStringList AOClient::buildAreaList(int area_idx)
{
QStringList entries;

View File

@ -54,6 +54,30 @@ void AOClient::pktSoftwareId(AreaData* area, int argc, QStringList argv, AOPacke
"y_offset"
};
// Extremely cursed client version string validation
// Ideally version strings should be X.X.X but it can be literally anything
// so we have to be super careful
version.string = argv[1];
QStringList version_raw = version.string.split(".");
bool ok;
int release_version = version_raw[0].toInt(&ok);
if (ok && version_raw.size() >= 1)
version.release = release_version;
if (ok && version_raw.size() >= 2) {
int major_version = version_raw[1].toInt(&ok);
if (ok)
version.major = major_version;
}
if (ok && version_raw.size() >= 3) {
int minor_version = version_raw[2].toInt(&ok);
if (ok)
version.minor = minor_version;
}
sendPacket("PN", {QString::number(server->player_count), max_players});
sendPacket("FL", feature_list);
}
@ -177,9 +201,11 @@ void AOClient::pktChangeMusic(AreaData* area, int argc, QStringList argv, AOPack
QString argument = argv[0];
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
AOPacket music_change("MC", {song, argv[1], argv[2], "1", "0", argv[3]});
area->current_music = song;
area->music_played_by = argv[2];
server->broadcast(music_change, current_area);
return;
}
@ -454,8 +480,17 @@ AOPacket AOClient::validateIcPacket(AOPacket packet)
// self offset
offset = incoming_args[17].toString();
args.append(offset);
args.append(other_offset);
// versions 2.6-2.8 cannot validate y-offset so we send them just the x-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);
// noninterrupting preanim