599 lines
23 KiB
C++
599 lines
23 KiB
C++
//////////////////////////////////////////////////////////////////////////////////////
|
|
// 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/>. //
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
#include "aoclient.h"
|
|
|
|
#include "area_data.h"
|
|
#include "command_extension.h"
|
|
#include "config_manager.h"
|
|
#include "packet/packet_factory.h"
|
|
#include "server.h"
|
|
|
|
const QMap<QString, AOClient::CommandInfo> AOClient::COMMANDS{
|
|
{"login", {{ACLRole::NONE}, 0, &AOClient::cmdLogin}},
|
|
{"getarea", {{ACLRole::NONE}, 0, &AOClient::cmdGetArea}},
|
|
{"getareas", {{ACLRole::NONE}, 0, &AOClient::cmdGetAreas}},
|
|
{"ban", {{ACLRole::BAN}, 3, &AOClient::cmdBan}},
|
|
{"kick", {{ACLRole::KICK}, 2, &AOClient::cmdKick}},
|
|
{"shutdown", {{ACLRole::SUPER}, 0, &AOClient::cmdShutdown}},
|
|
{"changeauth", {{ACLRole::SUPER}, 0, &AOClient::cmdChangeAuth}},
|
|
{"rootpass", {{ACLRole::SUPER}, 1, &AOClient::cmdSetRootPass}},
|
|
{"background", {{ACLRole::NONE}, 1, &AOClient::cmdSetBackground}},
|
|
{"side", {{ACLRole::NONE}, 0, &AOClient::cmdSetSide}},
|
|
{"lock_background", {{ACLRole::CM}, 0, &AOClient::cmdBgLock}},
|
|
{"unlock_background", {{ACLRole::CM}, 0, &AOClient::cmdBgUnlock}},
|
|
{"adduser", {{ACLRole::MODIFY_USERS}, 2, &AOClient::cmdAddUser}},
|
|
{"removeuser", {{ACLRole::MODIFY_USERS}, 1, &AOClient::cmdRemoveUser}},
|
|
{"listusers", {{ACLRole::MODIFY_USERS}, 0, &AOClient::cmdListUsers}},
|
|
{"setperms", {{ACLRole::MODIFY_USERS}, 2, &AOClient::cmdSetPerms}},
|
|
{"removeperms", {{ACLRole::MODIFY_USERS}, 1, &AOClient::cmdRemovePerms}},
|
|
{"listperms", {{ACLRole::NONE}, 0, &AOClient::cmdListPerms}},
|
|
{"logout", {{ACLRole::NONE}, 0, &AOClient::cmdLogout}},
|
|
{"pos", {{ACLRole::NONE}, 1, &AOClient::cmdPos}},
|
|
{"g", {{ACLRole::NONE}, 1, &AOClient::cmdG}},
|
|
{"need", {{ACLRole::NONE}, 1, &AOClient::cmdNeed}},
|
|
{"coinflip", {{ACLRole::NONE}, 0, &AOClient::cmdFlip}},
|
|
{"roll", {{ACLRole::NONE}, 0, &AOClient::cmdRoll}},
|
|
{"rollp", {{ACLRole::NONE}, 0, &AOClient::cmdRollP}},
|
|
{"doc", {{ACLRole::NONE}, 0, &AOClient::cmdDoc}},
|
|
{"cleardoc", {{ACLRole::NONE}, 0, &AOClient::cmdClearDoc}},
|
|
{"cm", {{ACLRole::NONE}, 0, &AOClient::cmdCM}},
|
|
{"uncm", {{ACLRole::CM}, 0, &AOClient::cmdUnCM}},
|
|
{"invite", {{ACLRole::CM}, 1, &AOClient::cmdInvite}},
|
|
{"uninvite", {{ACLRole::CM}, 1, &AOClient::cmdUnInvite}},
|
|
{"area_lock", {{ACLRole::CM}, 0, &AOClient::cmdLock}},
|
|
{"area_spectate", {{ACLRole::CM}, 0, &AOClient::cmdSpectatable}},
|
|
{"area_unlock", {{ACLRole::CM}, 0, &AOClient::cmdUnLock}},
|
|
{"timer", {{ACLRole::CM}, 0, &AOClient::cmdTimer}},
|
|
{"area", {{ACLRole::NONE}, 1, &AOClient::cmdArea}},
|
|
{"play", {{ACLRole::NONE}, 1, &AOClient::cmdPlay}},
|
|
{"area_kick", {{ACLRole::CM}, 1, &AOClient::cmdAreaKick}},
|
|
{"randomchar", {{ACLRole::NONE}, 0, &AOClient::cmdRandomChar}},
|
|
{"switch", {{ACLRole::NONE}, 1, &AOClient::cmdSwitch}},
|
|
{"toggleglobal", {{ACLRole::NONE}, 0, &AOClient::cmdToggleGlobal}},
|
|
{"mods", {{ACLRole::NONE}, 0, &AOClient::cmdMods}},
|
|
{"commands", {{ACLRole::NONE}, 0, &AOClient::cmdCommands}},
|
|
{"status", {{ACLRole::NONE}, 1, &AOClient::cmdStatus}},
|
|
{"forcepos", {{ACLRole::CM}, 2, &AOClient::cmdForcePos}},
|
|
{"currentmusic", {{ACLRole::NONE}, 0, &AOClient::cmdCurrentMusic}},
|
|
{"pm", {{ACLRole::NONE}, 2, &AOClient::cmdPM}},
|
|
{"evidence_mod", {{ACLRole::EVI_MOD}, 1, &AOClient::cmdEvidenceMod}},
|
|
{"motd", {{ACLRole::NONE}, 0, &AOClient::cmdMOTD}},
|
|
{"set_motd", {{ACLRole::MOTD}, 1, &AOClient::cmdSetMOTD}},
|
|
{"announce", {{ACLRole::ANNOUNCE}, 1, &AOClient::cmdAnnounce}},
|
|
{"m", {{ACLRole::MODCHAT}, 1, &AOClient::cmdM}},
|
|
{"gm", {{ACLRole::MODCHAT}, 1, &AOClient::cmdGM}},
|
|
{"mute", {{ACLRole::MUTE}, 1, &AOClient::cmdMute}},
|
|
{"unmute", {{ACLRole::MUTE}, 1, &AOClient::cmdUnMute}},
|
|
{"bans", {{ACLRole::BAN}, 0, &AOClient::cmdBans}},
|
|
{"unban", {{ACLRole::BAN}, 1, &AOClient::cmdUnBan}},
|
|
{"subtheme", {{ACLRole::CM}, 1, &AOClient::cmdSubTheme}},
|
|
{"about", {{ACLRole::NONE}, 0, &AOClient::cmdAbout}},
|
|
{"evidence_swap", {{ACLRole::CM}, 2, &AOClient::cmdEvidence_Swap}},
|
|
{"notecard", {{ACLRole::NONE}, 1, &AOClient::cmdNoteCard}},
|
|
{"notecard_reveal", {{ACLRole::CM}, 0, &AOClient::cmdNoteCardReveal}},
|
|
{"notecard_clear", {{ACLRole::NONE}, 0, &AOClient::cmdNoteCardClear}},
|
|
{"8ball", {{ACLRole::NONE}, 1, &AOClient::cmd8Ball}},
|
|
{"lm", {{ACLRole::MODCHAT}, 1, &AOClient::cmdLM}},
|
|
{"judgelog", {{ACLRole::CM}, 0, &AOClient::cmdJudgeLog}},
|
|
{"allow_blankposting", {{ACLRole::MODCHAT}, 0, &AOClient::cmdAllowBlankposting}},
|
|
{"gimp", {{ACLRole::MUTE}, 1, &AOClient::cmdGimp}},
|
|
{"ungimp", {{ACLRole::MUTE}, 1, &AOClient::cmdUnGimp}},
|
|
{"baninfo", {{ACLRole::BAN}, 1, &AOClient::cmdBanInfo}},
|
|
{"testify", {{ACLRole::CM}, 0, &AOClient::cmdTestify}},
|
|
{"testimony", {{ACLRole::NONE}, 0, &AOClient::cmdTestimony}},
|
|
{"examine", {{ACLRole::CM}, 0, &AOClient::cmdExamine}},
|
|
{"pause", {{ACLRole::CM}, 0, &AOClient::cmdPauseTestimony}},
|
|
{"delete", {{ACLRole::CM}, 0, &AOClient::cmdDeleteStatement}},
|
|
{"update", {{ACLRole::CM}, 0, &AOClient::cmdUpdateStatement}},
|
|
{"add", {{ACLRole::CM}, 0, &AOClient::cmdAddStatement}},
|
|
{"reload", {{ACLRole::SUPER}, 0, &AOClient::cmdReload}},
|
|
{"disemvowel", {{ACLRole::MUTE}, 1, &AOClient::cmdDisemvowel}},
|
|
{"undisemvowel", {{ACLRole::MUTE}, 1, &AOClient::cmdUnDisemvowel}},
|
|
{"shake", {{ACLRole::MUTE}, 1, &AOClient::cmdShake}},
|
|
{"unshake", {{ACLRole::MUTE}, 1, &AOClient::cmdUnShake}},
|
|
{"force_noint_pres", {{ACLRole::CM}, 0, &AOClient::cmdForceImmediate}},
|
|
{"allow_iniswap", {{ACLRole::CM}, 0, &AOClient::cmdAllowIniswap}},
|
|
{"afk", {{ACLRole::NONE}, 0, &AOClient::cmdAfk}},
|
|
{"savetestimony", {{ACLRole::NONE}, 1, &AOClient::cmdSaveTestimony}},
|
|
{"loadtestimony", {{ACLRole::CM}, 1, &AOClient::cmdLoadTestimony}},
|
|
{"permitsaving", {{ACLRole::MODCHAT}, 1, &AOClient::cmdPermitSaving}},
|
|
{"saveevidence", {{ACLRole::MODCHAT}, 0, &AOClient::cmdSaveEvidence}},
|
|
{"loadevidence", {{ACLRole::MODCHAT}, 0, &AOClient::cmdLoadEvidence}},
|
|
{"mutepm", {{ACLRole::NONE}, 0, &AOClient::cmdMutePM}},
|
|
{"toggleadverts", {{ACLRole::NONE}, 0, &AOClient::cmdToggleAdverts}},
|
|
{"ooc_mute", {{ACLRole::MUTE}, 1, &AOClient::cmdOocMute}},
|
|
{"ooc_unmute", {{ACLRole::MUTE}, 1, &AOClient::cmdOocUnMute}},
|
|
{"block_wtce", {{ACLRole::MUTE}, 1, &AOClient::cmdBlockWtce}},
|
|
{"unblock_wtce", {{ACLRole::MUTE}, 1, &AOClient::cmdUnBlockWtce}},
|
|
{"block_dj", {{ACLRole::MUTE}, 1, &AOClient::cmdBlockDj}},
|
|
{"unblock_dj", {{ACLRole::MUTE}, 1, &AOClient::cmdUnBlockDj}},
|
|
{"charcurse", {{ACLRole::MUTE}, 1, &AOClient::cmdCharCurse}},
|
|
{"uncharcurse", {{ACLRole::MUTE}, 1, &AOClient::cmdUnCharCurse}},
|
|
{"charselect", {{ACLRole::NONE}, 0, &AOClient::cmdCharSelect}},
|
|
{"force_charselect", {{ACLRole::FORCE_CHARSELECT}, 1, &AOClient::cmdForceCharSelect}},
|
|
{"togglemusic", {{ACLRole::CM}, 0, &AOClient::cmdToggleMusic}},
|
|
{"a", {{ACLRole::NONE}, 2, &AOClient::cmdA}},
|
|
{"s", {{ACLRole::NONE}, 0, &AOClient::cmdS}},
|
|
{"kick_uid", {{ACLRole::KICK}, 2, &AOClient::cmdKickUid}},
|
|
{"firstperson", {{ACLRole::NONE}, 0, &AOClient::cmdFirstPerson}},
|
|
{"update_ban", {{ACLRole::BAN}, 3, &AOClient::cmdUpdateBan}},
|
|
{"changepass", {{ACLRole::NONE}, 1, &AOClient::cmdChangePassword}},
|
|
{"ignore_bglist", {{ACLRole::IGNORE_BGLIST}, 0, &AOClient::cmdIgnoreBgList}},
|
|
{"notice", {{ACLRole::SEND_NOTICE}, 1, &AOClient::cmdNotice}},
|
|
{"noticeg", {{ACLRole::SEND_NOTICE}, 1, &AOClient::cmdNoticeGlobal}},
|
|
{"togglejukebox", {{ACLRole::CM, ACLRole::JUKEBOX}, 0, &AOClient::cmdToggleJukebox}},
|
|
{"help", {{ACLRole::NONE}, 1, &AOClient::cmdHelp}},
|
|
{"clearcm", {{ACLRole::KICK}, 0, &AOClient::cmdClearCM}},
|
|
{"togglemessage", {{ACLRole::CM}, 0, &AOClient::cmdToggleAreaMessageOnJoin}},
|
|
{"clearmessage", {{ACLRole::CM}, 0, &AOClient::cmdClearAreaMessage}},
|
|
{"areamessage", {{ACLRole::CM}, 0, &AOClient::cmdAreaMessage}},
|
|
{"webfiles", {{ACLRole::NONE}, 0, &AOClient::cmdWebfiles}},
|
|
{"addsong", {{ACLRole::CM}, 1, &AOClient::cmdAddSong}},
|
|
{"addcategory", {{ACLRole::CM}, 1, &AOClient::cmdAddCategory}},
|
|
{"removeentry", {{ACLRole::CM}, 1, &AOClient::cmdRemoveCategorySong}},
|
|
{"toggleroot", {{ACLRole::CM}, 0, &AOClient::cmdToggleRootlist}},
|
|
{"clearcustom", {{ACLRole::CM}, 0, &AOClient::cmdClearCustom}},
|
|
{"toggle_wtce", {{ACLRole::CM}, 0, &AOClient::cmdToggleWtce}},
|
|
{"toggle_shouts", {{ACLRole::CM}, 0, &AOClient::cmdToggleShouts}},
|
|
{"kick_other", {{ACLRole::NONE}, 0, &AOClient::cmdKickOther}},
|
|
{"jukebox_skip", {{ACLRole::CM}, 0, &AOClient::cmdJukeboxSkip}},
|
|
{"play_ambience", {{ACLRole::NONE}, 1, &AOClient::cmdPlayAmbience}}};
|
|
|
|
void AOClient::clientDisconnected()
|
|
{
|
|
#ifdef NET_DEBUG
|
|
qDebug() << m_remote_ip.toString() << "disconnected";
|
|
#endif
|
|
if (m_joined) {
|
|
server->getAreaById(areaId())
|
|
->removeClient(server->getCharID(character()), clientId());
|
|
arup(ARUPType::PLAYER_COUNT, true);
|
|
}
|
|
|
|
if (character() != "") {
|
|
server->updateCharsTaken(server->getAreaById(areaId()));
|
|
}
|
|
|
|
bool l_updateLocks = false;
|
|
|
|
const QVector<AreaData *> l_areas = server->getAreas();
|
|
for (AreaData *l_area : l_areas) {
|
|
l_updateLocks = l_updateLocks || l_area->removeOwner(clientId());
|
|
}
|
|
|
|
if (l_updateLocks)
|
|
arup(ARUPType::LOCKED, true);
|
|
arup(ARUPType::CM, true);
|
|
emit clientSuccessfullyDisconnected(clientId());
|
|
}
|
|
|
|
void AOClient::handlePacket(AOPacket *packet)
|
|
{
|
|
#ifdef NET_DEBUG
|
|
qDebug() << "Received packet:" << packet->getPacketInfo().header << ":" << packet->getContent() << "args length:" << packet->getContent().length();
|
|
#endif
|
|
AreaData *l_area = server->getAreaById(areaId());
|
|
|
|
if (packet->getContent().join("").size() > 16384) {
|
|
return;
|
|
}
|
|
|
|
if (!checkPermission(packet->getPacketInfo().acl_permission)) {
|
|
return;
|
|
}
|
|
|
|
if (packet->getPacketInfo().header != "CH" && m_joined) {
|
|
if (m_is_afk)
|
|
sendServerMessage("You are no longer AFK.");
|
|
m_is_afk = false;
|
|
if (characterName().endsWith(" [AFK]")) {
|
|
setCharacterName(characterName().remove(" [AFK]"));
|
|
}
|
|
m_afk_timer->start(ConfigManager::afkTimeout() * 1000);
|
|
}
|
|
|
|
if (packet->getContent().length() < packet->getPacketInfo().min_args) {
|
|
#ifdef NET_DEBUG
|
|
qDebug() << "Invalid packet args length. Minimum is" << packet->getPacketInfo().min_args << "but only" << packet->getContent().length() << "were given.";
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
packet->handlePacket(l_area, *this);
|
|
}
|
|
|
|
void AOClient::changeArea(int new_area)
|
|
{
|
|
if (areaId() == new_area) {
|
|
sendServerMessage("You are already in area " + server->getAreaName(areaId()));
|
|
return;
|
|
}
|
|
if (server->getAreaById(new_area)->lockStatus() == AreaData::LockStatus::LOCKED && !server->getAreaById(new_area)->invited().contains(clientId()) && !checkPermission(ACLRole::BYPASS_LOCKS)) {
|
|
sendServerMessage("Area " + server->getAreaName(new_area) + " is locked.");
|
|
return;
|
|
}
|
|
|
|
if (character() != "") {
|
|
server->getAreaById(areaId())
|
|
->changeCharacter(server->getCharID(character()), -1);
|
|
}
|
|
server->getAreaById(areaId())->removeClient(m_char_id, clientId());
|
|
bool l_character_taken = false;
|
|
if (server->getAreaById(new_area)->charactersTaken().contains(
|
|
server->getCharID(character()))) {
|
|
setCharacter("");
|
|
m_char_id = -1;
|
|
l_character_taken = true;
|
|
}
|
|
int old_area_id = areaId();
|
|
server->getAreaById(new_area)->addClient(m_char_id, clientId());
|
|
setAreaId(new_area);
|
|
server->updateCharsTaken(server->getAreaById(old_area_id));
|
|
server->updateCharsTaken(server->getAreaById(new_area));
|
|
arup(ARUPType::PLAYER_COUNT, true);
|
|
sendEvidenceList(server->getAreaById(new_area));
|
|
sendPacket("HP", {"1", QString::number(server->getAreaById(new_area)->defHP())});
|
|
sendPacket("HP", {"2", QString::number(server->getAreaById(new_area)->proHP())});
|
|
sendPacket("BN", {server->getAreaById(new_area)->background(), server->getAreaById(new_area)->side()});
|
|
if (l_character_taken) {
|
|
sendPacket("DONE");
|
|
}
|
|
const QList<QTimer *> l_timers = server->getAreaById(areaId())->timers();
|
|
for (QTimer *l_timer : l_timers) {
|
|
int l_timer_id = server->getAreaById(areaId())->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"});
|
|
}
|
|
}
|
|
sendServerMessage("You moved to area " + server->getAreaName(areaId()));
|
|
if (server->getAreaById(areaId())->sendAreaMessageOnJoin())
|
|
sendServerMessage(server->getAreaById(areaId())->areaMessage());
|
|
|
|
if (server->getAreaById(areaId())->lockStatus() == AreaData::LockStatus::SPECTATABLE)
|
|
sendServerMessage("Area " + server->getAreaName(areaId()) + " is spectate-only; to chat IC you will need to be invited by the CM.");
|
|
}
|
|
|
|
bool AOClient::changeCharacter(int char_id)
|
|
{
|
|
AreaData *l_area = server->getAreaById(areaId());
|
|
|
|
if (char_id >= server->getCharacterCount())
|
|
return false;
|
|
|
|
if (m_is_charcursed && !m_charcurse_list.contains(char_id)) {
|
|
return false;
|
|
}
|
|
|
|
bool l_successfulChange = l_area->changeCharacter(server->getCharID(character()),
|
|
char_id);
|
|
|
|
if (char_id < 0) {
|
|
setCharacter("");
|
|
m_char_id = char_id;
|
|
setSpectator(true);
|
|
}
|
|
|
|
if (l_successfulChange == true) {
|
|
QString l_char_selected = server->getCharacterById(char_id);
|
|
setCharacter(l_char_selected);
|
|
m_pos = "";
|
|
server->updateCharsTaken(l_area);
|
|
sendPacket("PV", {QString::number(clientId()), "CID", QString::number(char_id)});
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void AOClient::changePosition(QString new_pos)
|
|
{
|
|
m_pos = new_pos;
|
|
sendServerMessage("Position changed to " + m_pos + ".");
|
|
sendPacket("SP", {m_pos});
|
|
}
|
|
|
|
void AOClient::handleCommand(QString command, int argc, QStringList argv)
|
|
{
|
|
command = command.toLower();
|
|
QString l_target_command = command;
|
|
QVector<ACLRole::Permission> l_permissions;
|
|
|
|
// check for aliases
|
|
const QList<CommandExtension> l_extensions = server->getCommandExtensionCollection()->getExtensions();
|
|
for (const CommandExtension &i_extension : l_extensions) {
|
|
if (i_extension.checkCommandNameAndAlias(command)) {
|
|
l_target_command = i_extension.getCommandName();
|
|
l_permissions = i_extension.getPermissions();
|
|
break;
|
|
}
|
|
}
|
|
|
|
CommandInfo l_command = COMMANDS.value(l_target_command, {{ACLRole::NONE}, -1, &AOClient::cmdDefault});
|
|
if (l_permissions.isEmpty()) {
|
|
l_permissions.append(l_command.acl_permissions);
|
|
}
|
|
|
|
bool l_has_permissions = false;
|
|
for (const ACLRole::Permission i_permission : qAsConst(l_permissions)) {
|
|
if (checkPermission(i_permission)) {
|
|
l_has_permissions = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!l_has_permissions) {
|
|
sendServerMessage("You do not have permission to use that command.");
|
|
return;
|
|
}
|
|
|
|
if (argc < l_command.minArgs) {
|
|
sendServerMessage("Invalid command syntax.");
|
|
sendServerMessage("The expected syntax for this command is: \n" + ConfigManager::commandHelp(command).usage);
|
|
return;
|
|
}
|
|
|
|
(this->*(l_command.action))(argc, argv);
|
|
}
|
|
|
|
void AOClient::arup(ARUPType type, bool broadcast)
|
|
{
|
|
QStringList l_arup_data;
|
|
l_arup_data.append(QString::number(type));
|
|
const QVector<AreaData *> l_areas = server->getAreas();
|
|
for (AreaData *l_area : l_areas) {
|
|
switch (type) {
|
|
case ARUPType::PLAYER_COUNT:
|
|
{
|
|
l_arup_data.append(QString::number(l_area->playerCount()));
|
|
break;
|
|
}
|
|
case ARUPType::STATUS:
|
|
{
|
|
QString l_area_status = QVariant::fromValue(l_area->status()).toString().replace("_", "-"); // LOOKING_FOR_PLAYERS to LOOKING-FOR-PLAYERS
|
|
l_arup_data.append(l_area_status);
|
|
break;
|
|
}
|
|
case ARUPType::CM:
|
|
{
|
|
if (l_area->owners().isEmpty())
|
|
l_arup_data.append("FREE");
|
|
else {
|
|
QStringList l_area_owners;
|
|
const QList<int> l_owner_ids = l_area->owners();
|
|
for (int l_owner_id : l_owner_ids) {
|
|
AOClient *l_owner = server->getClientByID(l_owner_id);
|
|
l_area_owners.append("[" + QString::number(l_owner->clientId()) + "] " + l_owner->character());
|
|
}
|
|
l_arup_data.append(l_area_owners.join(", "));
|
|
}
|
|
break;
|
|
}
|
|
case ARUPType::LOCKED:
|
|
{
|
|
QString l_lock_status = QVariant::fromValue(l_area->lockStatus()).toString();
|
|
l_arup_data.append(l_lock_status);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (broadcast)
|
|
server->broadcast(PacketFactory::createPacket("ARUP", l_arup_data));
|
|
else
|
|
sendPacket("ARUP", l_arup_data);
|
|
}
|
|
|
|
void AOClient::fullArup()
|
|
{
|
|
arup(ARUPType::PLAYER_COUNT, false);
|
|
arup(ARUPType::STATUS, false);
|
|
arup(ARUPType::CM, false);
|
|
arup(ARUPType::LOCKED, false);
|
|
}
|
|
|
|
void AOClient::sendPacket(AOPacket *packet)
|
|
{
|
|
m_socket->write(packet);
|
|
}
|
|
|
|
void AOClient::sendPacket(QString header, QStringList contents)
|
|
{
|
|
sendPacket(PacketFactory::createPacket(header, contents));
|
|
}
|
|
|
|
void AOClient::sendPacket(QString header)
|
|
{
|
|
sendPacket(PacketFactory::createPacket(header, {}));
|
|
}
|
|
|
|
void AOClient::calculateIpid()
|
|
{
|
|
// TODO: add support for longer ipids?
|
|
// This reduces the (fairly high) chance of
|
|
// birthday paradox issues arising. However,
|
|
// typing more than 8 characters might be a
|
|
// bit cumbersome.
|
|
|
|
QCryptographicHash hash(QCryptographicHash::Md5); // Don't need security, just hashing for uniqueness
|
|
|
|
hash.addData(m_remote_ip.toString().toUtf8());
|
|
|
|
m_ipid = hash.result().toHex().right(8); // Use the last 8 characters (4 bytes)
|
|
}
|
|
|
|
void AOClient::sendServerMessage(QString message)
|
|
{
|
|
sendPacket("CT", {ConfigManager::serverTag(), message, "1"});
|
|
}
|
|
|
|
void AOClient::sendServerMessageArea(QString message)
|
|
{
|
|
server->broadcast(PacketFactory::createPacket("CT", {ConfigManager::serverTag(), message, "1"}),
|
|
areaId());
|
|
}
|
|
|
|
void AOClient::sendServerBroadcast(QString message)
|
|
{
|
|
server->broadcast(PacketFactory::createPacket("CT", {ConfigManager::serverTag(), message, "1"}));
|
|
}
|
|
|
|
bool AOClient::checkPermission(ACLRole::Permission f_permission) const
|
|
{
|
|
if (f_permission == ACLRole::NONE) {
|
|
return true;
|
|
}
|
|
|
|
if ((f_permission == ACLRole::CM) && server->getAreaById(areaId())->owners().contains(clientId())) {
|
|
return true; // I'm sorry for this hack.
|
|
}
|
|
|
|
if (!isAuthenticated()) {
|
|
return false;
|
|
}
|
|
|
|
if (ConfigManager::authType() == DataTypes::AuthType::SIMPLE) {
|
|
return true;
|
|
}
|
|
|
|
const ACLRole l_role = server->getACLRolesHandler()->getRoleById(m_acl_role_id);
|
|
return l_role.checkPermission(f_permission);
|
|
}
|
|
|
|
QString AOClient::getIpid() const
|
|
{
|
|
return m_ipid;
|
|
}
|
|
|
|
QString AOClient::getHwid() const
|
|
{
|
|
return m_hwid;
|
|
}
|
|
|
|
bool AOClient::hasJoined() const
|
|
{
|
|
return m_joined;
|
|
}
|
|
|
|
bool AOClient::isAuthenticated() const
|
|
{
|
|
return m_authenticated;
|
|
}
|
|
|
|
Server *AOClient::getServer() { return server; }
|
|
|
|
int AOClient::clientId() const
|
|
{
|
|
return m_id;
|
|
}
|
|
|
|
QString AOClient::name() const { return m_ooc_name; }
|
|
|
|
void AOClient::setName(const QString &f_name)
|
|
{
|
|
if (f_name != m_ooc_name) {
|
|
m_ooc_name = f_name;
|
|
Q_EMIT nameChanged(m_ooc_name);
|
|
}
|
|
}
|
|
|
|
int AOClient::areaId() const
|
|
{
|
|
return m_current_area;
|
|
}
|
|
|
|
void AOClient::setAreaId(const int f_area_id)
|
|
{
|
|
if (f_area_id != m_current_area) {
|
|
m_current_area = f_area_id;
|
|
Q_EMIT areaIdChanged(m_current_area);
|
|
}
|
|
}
|
|
|
|
QString AOClient::character() const
|
|
{
|
|
return m_current_char;
|
|
}
|
|
|
|
void AOClient::setCharacter(const QString &f_character)
|
|
{
|
|
if (f_character != m_current_char) {
|
|
m_current_char = f_character;
|
|
Q_EMIT characterChanged(m_current_char);
|
|
}
|
|
}
|
|
|
|
QString AOClient::characterName() const { return m_showname; }
|
|
|
|
void AOClient::setCharacterName(const QString &f_showname)
|
|
{
|
|
if (f_showname != m_showname) {
|
|
m_showname = f_showname;
|
|
Q_EMIT characterNameChanged(m_showname);
|
|
}
|
|
}
|
|
|
|
void AOClient::setSpectator(bool f_spectator)
|
|
{
|
|
m_is_spectator = f_spectator;
|
|
}
|
|
|
|
bool AOClient::isSpectator() const
|
|
{
|
|
return m_is_spectator;
|
|
}
|
|
|
|
void AOClient::onAfkTimeout()
|
|
{
|
|
if (!m_is_afk) {
|
|
sendServerMessage("You are now AFK.");
|
|
setCharacterName(characterName() + " [AFK]");
|
|
}
|
|
m_is_afk = true;
|
|
}
|
|
|
|
AOClient::AOClient(
|
|
Server *p_server, NetworkSocket *socket, QObject *parent, int user_id, MusicManager *p_manager) :
|
|
QObject(parent),
|
|
m_remote_ip(socket->peerAddress()),
|
|
m_password(""),
|
|
m_joined(false),
|
|
m_socket(socket),
|
|
m_music_manager(p_manager),
|
|
m_last_wtce_time(0),
|
|
m_id(user_id),
|
|
m_current_area(0),
|
|
m_current_char(""),
|
|
server(p_server),
|
|
is_partial(false)
|
|
{
|
|
m_afk_timer = new QTimer;
|
|
m_afk_timer->setSingleShot(true);
|
|
connect(m_afk_timer, &QTimer::timeout, this, &AOClient::onAfkTimeout);
|
|
}
|
|
|
|
AOClient::~AOClient()
|
|
{
|
|
clientDisconnected();
|
|
m_socket->deleteLater();
|
|
}
|