mirror of
https://github.com/EQEmu/Server.git
synced 2026-04-04 06:02:26 +00:00
- License was intended to be GPLv3 per earlier commit of GPLv3 LICENSE FILE - This is confirmed by the inclusion of libraries that are incompatible with GPLv2 - This is also confirmed by KLS and the agreement of KLS's predecessors - Added GPLv3 license headers to the compilable source files - Removed Folly licensing in strings.h since the string functions do not match the Folly functions and are standard functions - this must have been left over from previous implementations - Removed individual contributor license headers since the project has been under the "developer" mantle for many years - Removed comments on files that were previously automatically generated since they've been manually modified multiple times and there are no automatic scripts referencing them (removed in 2023)
965 lines
25 KiB
C++
965 lines
25 KiB
C++
/* EQEmu: EQEmulator
|
|
|
|
Copyright (C) 2001-2026 EQEmu Development Team
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "chatchannel.h"
|
|
|
|
#include "common/eqemu_logsys.h"
|
|
#include "common/strings.h"
|
|
#include "ucs/clientlist.h"
|
|
#include "ucs/database.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
|
|
extern UCSDatabase database;
|
|
extern uint32 ChatMessagesSent;
|
|
|
|
void ServerToClient45SayLink(std::string& clientSayLink, const std::string& serverSayLink);
|
|
void ServerToClient50SayLink(std::string& clientSayLink, const std::string& serverSayLink);
|
|
void ServerToClient55SayLink(std::string& clientSayLink, const std::string& serverSayLink);
|
|
|
|
ChatChannel::ChatChannel(const std::string& inName, const std::string& inOwner, const std::string& inPassword, bool inPermanent, int inMinimumStatus) :
|
|
m_delete_timer(0) {
|
|
|
|
m_name = inName;
|
|
|
|
m_owner = inOwner;
|
|
|
|
m_password = inPassword;
|
|
|
|
m_permanent = inPermanent;
|
|
|
|
m_minimum_status = inMinimumStatus;
|
|
|
|
m_moderated = false;
|
|
|
|
LogDebug(
|
|
"New ChatChannel created: Name: [{}] Owner: [{}] Password: [{}] MinStatus: [{}]",
|
|
m_name,
|
|
m_owner,
|
|
m_password,
|
|
m_minimum_status
|
|
);
|
|
|
|
}
|
|
|
|
ChatChannel::~ChatChannel() {
|
|
|
|
LinkedListIterator<Client*> iterator(m_clients_in_channel);
|
|
|
|
iterator.Reset();
|
|
|
|
while(iterator.MoreElements())
|
|
iterator.RemoveCurrent(false);
|
|
}
|
|
|
|
ChatChannel *ChatChannelList::CreateChannel(
|
|
const std::string& name,
|
|
const std::string& owner,
|
|
const std::string& password,
|
|
bool permanent,
|
|
int minimum_status,
|
|
bool save_to_db
|
|
)
|
|
{
|
|
uint8 max_perm_player_channels = RuleI(Chat, MaxPermanentPlayerChannels);
|
|
|
|
if (!RuleB(Chat, ChannelsIgnoreNameFilter) && !database.CheckChannelNameFilter(name)) {
|
|
if (!(owner == SYSTEM_OWNER)) {
|
|
return nullptr;
|
|
}
|
|
else {
|
|
LogDebug("Ignoring Name Filter as channel is owned by System...");
|
|
}
|
|
}
|
|
|
|
if (IsOnChannelBlockList(name)) {
|
|
if (!(owner == SYSTEM_OWNER)) {
|
|
LogInfo("Channel name [{}] is a reserved/blocked channel name. Channel creation canceled.", name);
|
|
return nullptr;
|
|
}
|
|
else {
|
|
LogInfo("Ignoring reserved/blocked channel name [{}] as channel is owned by System...", name);
|
|
}
|
|
}
|
|
else {
|
|
LogDebug("Channel name [{}] passed the reserved/blocked channel name check...", name);
|
|
}
|
|
|
|
|
|
auto *new_channel = new ChatChannel(CapitaliseName(name), owner, password, permanent, minimum_status);
|
|
|
|
ChatChannels.Insert(new_channel);
|
|
|
|
if (owner == SYSTEM_OWNER) {
|
|
save_to_db = false;
|
|
}
|
|
|
|
// If permanent player channels are enabled (and not a system channel)
|
|
// save channel to database if not exceeding limit.
|
|
bool can_save_channel = (max_perm_player_channels > 0) && !(owner == SYSTEM_OWNER) && save_to_db;
|
|
if (can_save_channel) {
|
|
|
|
// Ensure there is room to save another chat channel to the database.
|
|
bool player_under_channel_limit = database.CurrentPlayerChannelCount(owner) + 1 <= max_perm_player_channels;
|
|
if (player_under_channel_limit) {
|
|
database.SaveChatChannel(
|
|
CapitaliseName(name),
|
|
owner,
|
|
password,
|
|
minimum_status
|
|
);
|
|
}
|
|
else {
|
|
LogDebug(
|
|
"Maximum number of channels [{}] reached for player [{}], channel [{}] save to database aborted.",
|
|
max_perm_player_channels,
|
|
owner,
|
|
CapitaliseName(name)
|
|
);
|
|
}
|
|
}
|
|
|
|
return new_channel;
|
|
}
|
|
|
|
ChatChannel* ChatChannelList::FindChannel(const std::string& Name) {
|
|
|
|
std::string normalized_name = CapitaliseName(Name);
|
|
|
|
LinkedListIterator<ChatChannel*> iterator(ChatChannels);
|
|
|
|
iterator.Reset();
|
|
|
|
while(iterator.MoreElements()) {
|
|
|
|
auto *current_channel = iterator.GetData();
|
|
|
|
if(current_channel && (current_channel->m_name == normalized_name))
|
|
return iterator.GetData();
|
|
|
|
iterator.Advance();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void ChatChannelList::SendAllChannels(Client *c) {
|
|
|
|
if(!c) return;
|
|
|
|
if(!c->CanListAllChannels()) {
|
|
c->GeneralChannelMessage("You do not have permission to list all the channels.");
|
|
return;
|
|
}
|
|
|
|
c->GeneralChannelMessage("All current channels:");
|
|
|
|
int ChannelsInLine = 0;
|
|
|
|
LinkedListIterator<ChatChannel*> iterator(ChatChannels);
|
|
|
|
iterator.Reset();
|
|
|
|
std::string Message;
|
|
|
|
char CountString[13];
|
|
|
|
while(iterator.MoreElements()) {
|
|
|
|
ChatChannel *CurrentChannel = iterator.GetData();
|
|
|
|
if(!CurrentChannel || (CurrentChannel->GetMinStatus() > c->GetAccountStatus())) {
|
|
|
|
iterator.Advance();
|
|
|
|
continue;
|
|
}
|
|
|
|
if(ChannelsInLine > 0)
|
|
Message += ", ";
|
|
|
|
sprintf(CountString, "(%i)", CurrentChannel->MemberCount(c->GetAccountStatus()));
|
|
|
|
Message += CurrentChannel->GetName();
|
|
|
|
Message += CountString;
|
|
|
|
ChannelsInLine++;
|
|
|
|
if(ChannelsInLine == 6) {
|
|
|
|
c->GeneralChannelMessage(Message);
|
|
|
|
ChannelsInLine = 0;
|
|
|
|
Message.clear();
|
|
}
|
|
|
|
iterator.Advance();
|
|
}
|
|
|
|
if(ChannelsInLine > 0)
|
|
c->GeneralChannelMessage(Message);
|
|
|
|
}
|
|
|
|
void ChatChannelList::RemoveChannel(ChatChannel *Channel) {
|
|
|
|
LogDebug("Remove channel [{}]", Channel->GetName().c_str());
|
|
|
|
LinkedListIterator<ChatChannel*> iterator(ChatChannels);
|
|
|
|
iterator.Reset();
|
|
|
|
while(iterator.MoreElements()) {
|
|
|
|
if(iterator.GetData() == Channel) {
|
|
|
|
iterator.RemoveCurrent();
|
|
|
|
return;
|
|
}
|
|
|
|
iterator.Advance();
|
|
}
|
|
}
|
|
|
|
void ChatChannelList::RemoveAllChannels() {
|
|
|
|
LogDebug("RemoveAllChannels");
|
|
|
|
LinkedListIterator<ChatChannel*> iterator(ChatChannels);
|
|
|
|
iterator.Reset();
|
|
|
|
while(iterator.MoreElements())
|
|
iterator.RemoveCurrent();
|
|
}
|
|
|
|
int ChatChannel::MemberCount(int Status) {
|
|
|
|
int Count = 0;
|
|
|
|
LinkedListIterator<Client*> iterator(m_clients_in_channel);
|
|
|
|
iterator.Reset();
|
|
|
|
while(iterator.MoreElements()) {
|
|
|
|
Client *ChannelClient = iterator.GetData();
|
|
|
|
if(ChannelClient && (!ChannelClient->GetHideMe() || (ChannelClient->GetAccountStatus() < Status)))
|
|
Count++;
|
|
|
|
iterator.Advance();
|
|
}
|
|
|
|
return Count;
|
|
}
|
|
|
|
void ChatChannel::SetPassword(const std::string& in_password) {
|
|
|
|
m_password = in_password;
|
|
|
|
if(m_permanent)
|
|
{
|
|
RemoveApostrophes(m_password);
|
|
database.SetChannelPassword(m_name, m_password);
|
|
}
|
|
}
|
|
|
|
void ChatChannel::SetOwner(const std::string& in_owner) {
|
|
|
|
m_owner = in_owner;
|
|
|
|
if(m_permanent)
|
|
database.SetChannelOwner(m_name, m_owner);
|
|
}
|
|
|
|
// Returns the owner's name in type std::string()
|
|
std::string& ChatChannel::GetOwnerName() {
|
|
return m_owner;
|
|
}
|
|
|
|
void ChatChannel::SetTemporary() {
|
|
m_permanent = false;
|
|
}
|
|
|
|
void ChatChannel::SetPermanent() {
|
|
m_permanent = true;
|
|
}
|
|
|
|
void ChatChannel::AddClient(Client *c) {
|
|
|
|
if(!c) return;
|
|
|
|
m_delete_timer.Disable();
|
|
|
|
if(IsClientInChannel(c)) {
|
|
|
|
LogInfo("Client [{}] already in channel [{}]", c->GetName().c_str(), GetName().c_str());
|
|
|
|
return;
|
|
}
|
|
|
|
bool HideMe = c->GetHideMe();
|
|
|
|
int AccountStatus = c->GetAccountStatus();
|
|
|
|
LogDebug("Adding [{}] to channel [{}]", c->GetName().c_str(), m_name.c_str());
|
|
|
|
LinkedListIterator<Client*> iterator(m_clients_in_channel);
|
|
|
|
iterator.Reset();
|
|
|
|
while(iterator.MoreElements()) {
|
|
|
|
Client *CurrentClient = iterator.GetData();
|
|
|
|
if(CurrentClient && CurrentClient->IsAnnounceOn())
|
|
if(!HideMe || (CurrentClient->GetAccountStatus() > AccountStatus))
|
|
CurrentClient->AnnounceJoin(this, c);
|
|
|
|
iterator.Advance();
|
|
}
|
|
|
|
m_clients_in_channel.Insert(c);
|
|
|
|
}
|
|
|
|
bool ChatChannel::RemoveClient(Client *c) {
|
|
|
|
if(!c) return false;
|
|
|
|
LogDebug("Remove client [{}] from channel [{}]", c->GetName().c_str(), GetName().c_str());
|
|
|
|
bool hide_me = c->GetHideMe();
|
|
|
|
int account_status = c->GetAccountStatus();
|
|
|
|
int players_in_channel = 0;
|
|
|
|
LinkedListIterator<Client*> iterator(m_clients_in_channel);
|
|
|
|
iterator.Reset();
|
|
|
|
while(iterator.MoreElements()) {
|
|
|
|
auto *current_client = iterator.GetData();
|
|
|
|
if(current_client == c) {
|
|
iterator.RemoveCurrent(false);
|
|
}
|
|
else if(current_client) {
|
|
|
|
players_in_channel++;
|
|
|
|
if(current_client->IsAnnounceOn())
|
|
if(!hide_me || (current_client->GetAccountStatus() > account_status))
|
|
current_client->AnnounceLeave(this, c);
|
|
|
|
iterator.Advance();
|
|
}
|
|
|
|
}
|
|
|
|
if((players_in_channel == 0) && !m_permanent) {
|
|
|
|
if((m_password.length() == 0) || (RuleI(Channels, DeleteTimer) == 0))
|
|
return false;
|
|
|
|
LogDebug("Starting delete timer for empty password protected channel [{}]", m_name.c_str());
|
|
|
|
m_delete_timer.Start(RuleI(Channels, DeleteTimer) * 60000);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ChatChannel::SendOPList(Client *c)
|
|
{
|
|
if (!c) {
|
|
return;
|
|
}
|
|
|
|
c->GeneralChannelMessage("Channel " + m_name + " op-list: (Owner=" + m_owner + ")");
|
|
|
|
for (auto &&m : m_moderators) {
|
|
c->GeneralChannelMessage(m);
|
|
}
|
|
}
|
|
|
|
void ChatChannel::SendChannelMembers(Client *c) {
|
|
|
|
if(!c) return;
|
|
|
|
char CountString[13];
|
|
|
|
sprintf(CountString, "(%i)", MemberCount(c->GetAccountStatus()));
|
|
|
|
std::string Message = "Channel " + GetName();
|
|
|
|
Message += CountString;
|
|
|
|
Message += " members:";
|
|
|
|
c->GeneralChannelMessage(Message);
|
|
|
|
int AccountStatus = c->GetAccountStatus();
|
|
|
|
Message.clear();
|
|
|
|
int MembersInLine = 0;
|
|
|
|
LinkedListIterator<Client*> iterator(m_clients_in_channel);
|
|
|
|
iterator.Reset();
|
|
|
|
while(iterator.MoreElements()) {
|
|
|
|
Client *ChannelClient = iterator.GetData();
|
|
|
|
// Don't list hidden characters with status higher or equal than the character requesting the list.
|
|
//
|
|
if(!ChannelClient || (ChannelClient->GetHideMe() && (ChannelClient->GetAccountStatus() >= AccountStatus))) {
|
|
iterator.Advance();
|
|
continue;
|
|
}
|
|
|
|
if(MembersInLine > 0)
|
|
Message += ", ";
|
|
|
|
Message += ChannelClient->GetName();
|
|
|
|
MembersInLine++;
|
|
|
|
if(MembersInLine == 6) {
|
|
|
|
c->GeneralChannelMessage(Message);
|
|
|
|
MembersInLine = 0;
|
|
|
|
Message.clear();
|
|
}
|
|
|
|
iterator.Advance();
|
|
}
|
|
|
|
if(MembersInLine > 0)
|
|
c->GeneralChannelMessage(Message);
|
|
|
|
}
|
|
|
|
void ChatChannel::SendMessageToChannel(const std::string& Message, Client* Sender) {
|
|
|
|
if(!Sender) return;
|
|
|
|
std::string cv_messages[EQ::versions::ClientVersionCount];
|
|
|
|
ChatMessagesSent++;
|
|
|
|
LinkedListIterator<Client*> iterator(m_clients_in_channel);
|
|
|
|
iterator.Reset();
|
|
|
|
while(iterator.MoreElements()) {
|
|
|
|
auto *channel_client = iterator.GetData();
|
|
|
|
if(channel_client)
|
|
{
|
|
LogDebug("Sending message to [{}] from [{}]",
|
|
channel_client->GetName().c_str(), Sender->GetName().c_str());
|
|
|
|
if (cv_messages[static_cast<uint32>(channel_client->GetClientVersion())].length() == 0) {
|
|
switch (channel_client->GetClientVersion()) {
|
|
case EQ::versions::ClientVersion::Titanium:
|
|
ServerToClient45SayLink(cv_messages[static_cast<uint32>(channel_client->GetClientVersion())], Message);
|
|
break;
|
|
case EQ::versions::ClientVersion::SoF:
|
|
case EQ::versions::ClientVersion::SoD:
|
|
case EQ::versions::ClientVersion::UF:
|
|
ServerToClient50SayLink(cv_messages[static_cast<uint32>(channel_client->GetClientVersion())], Message);
|
|
break;
|
|
case EQ::versions::ClientVersion::RoF:
|
|
ServerToClient55SayLink(cv_messages[static_cast<uint32>(channel_client->GetClientVersion())], Message);
|
|
break;
|
|
case EQ::versions::ClientVersion::RoF2:
|
|
default:
|
|
cv_messages[static_cast<uint32>(channel_client->GetClientVersion())] = Message;
|
|
break;
|
|
}
|
|
}
|
|
|
|
channel_client->SendChannelMessage(m_name, cv_messages[static_cast<uint32>(channel_client->GetClientVersion())], Sender);
|
|
}
|
|
|
|
iterator.Advance();
|
|
}
|
|
}
|
|
|
|
void ChatChannel::SetModerated(bool inModerated) {
|
|
|
|
m_moderated = inModerated;
|
|
|
|
LinkedListIterator<Client*> iterator(m_clients_in_channel);
|
|
|
|
iterator.Reset();
|
|
|
|
while(iterator.MoreElements()) {
|
|
|
|
Client *ChannelClient = iterator.GetData();
|
|
|
|
if(ChannelClient) {
|
|
|
|
if(m_moderated)
|
|
ChannelClient->GeneralChannelMessage("Channel " + m_name + " is now moderated.");
|
|
else
|
|
ChannelClient->GeneralChannelMessage("Channel " + m_name + " is no longer moderated.");
|
|
}
|
|
|
|
iterator.Advance();
|
|
}
|
|
|
|
}
|
|
bool ChatChannel::IsClientInChannel(Client *c) {
|
|
|
|
if(!c) return false;
|
|
|
|
LinkedListIterator<Client*> iterator(m_clients_in_channel);
|
|
|
|
iterator.Reset();
|
|
|
|
while(iterator.MoreElements()) {
|
|
|
|
if(iterator.GetData() == c)
|
|
return true;
|
|
|
|
iterator.Advance();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
ChatChannel *ChatChannelList::AddClientToChannel(std::string channel_name, Client *c, bool command_directed) {
|
|
|
|
if(!c) return nullptr;
|
|
|
|
if ((channel_name.length() > 0) && (isdigit(channel_name[0]))) { // Ensure channel name does not start with a number
|
|
c->GeneralChannelMessage("The channel name can not begin with a number.");
|
|
return nullptr;
|
|
}
|
|
else if (channel_name.empty()) { // Ensure channel name is not empty
|
|
return nullptr;
|
|
}
|
|
|
|
std::string normalized_name, password;
|
|
|
|
std::string::size_type Colon = channel_name.find_first_of(":");
|
|
|
|
if(Colon == std::string::npos)
|
|
normalized_name = CapitaliseName(channel_name);
|
|
else {
|
|
normalized_name = CapitaliseName(channel_name.substr(0, Colon));
|
|
|
|
password = channel_name.substr(Colon + 1);
|
|
}
|
|
|
|
if((normalized_name.length() > 64) || (password.length() > 64)) {
|
|
|
|
c->GeneralChannelMessage("The channel name or password cannot exceed 64 characters.");
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
ChatChannel *RequiredChannel = FindChannel(normalized_name);
|
|
|
|
if (RequiredChannel) {
|
|
if (IsOnChannelBlockList(channel_name)) { // Ensure channel name is not blocked
|
|
if (!(RequiredChannel->GetOwnerName() == SYSTEM_OWNER)) {
|
|
c->GeneralChannelMessage("That channel name is blocked by the server operator.");
|
|
return nullptr;
|
|
}
|
|
else {
|
|
LogDebug("Reserved/blocked channel name check for [{}] ignored due to channel being owned by System...", normalized_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
const std::string& channel_owner = c->GetName();
|
|
|
|
bool permanent = false;
|
|
if (command_directed && RuleI(Chat, MaxPermanentPlayerChannels) > 0) {
|
|
permanent = true;
|
|
}
|
|
|
|
if (!RequiredChannel) {
|
|
RequiredChannel = CreateChannel(normalized_name, channel_owner, password, permanent, 0, command_directed);
|
|
if (RequiredChannel == nullptr) {
|
|
LogDebug("Failed to create new channel with name: {}. Possible blocked or reserved channel name.", normalized_name);
|
|
c->GeneralChannelMessage("Failed to create new channel with provided name. Possible blocked or reserved channel name.");
|
|
return nullptr;
|
|
}
|
|
LogDebug("Created and added Client to channel [{}] with password [{}]. Owner: {}. Command Directed: {}", normalized_name.c_str(), password.c_str(), channel_owner, command_directed);
|
|
}
|
|
|
|
LogDebug("Checking status requirement of channel: {}. Channel status required: {}, player status: {}.", normalized_name, std::to_string(RequiredChannel->GetMinStatus()), std::to_string(c->GetAccountStatus()));
|
|
if (RequiredChannel->GetMinStatus() > c->GetAccountStatus()) {
|
|
std::string Message = "You do not have the required account status to join channel " + normalized_name;
|
|
|
|
c->GeneralChannelMessage(Message);
|
|
LogInfo("Client [{}] connection to channel [{}] refused due to insufficient status.", c->GetName(), normalized_name);
|
|
return nullptr;
|
|
}
|
|
|
|
if (RequiredChannel->IsClientInChannel(c)) {
|
|
return nullptr;
|
|
}
|
|
|
|
if(RequiredChannel->IsInvitee(c->GetName())) {
|
|
|
|
RequiredChannel->AddClient(c);
|
|
|
|
RequiredChannel->RemoveInvitee(c->GetName());
|
|
|
|
return RequiredChannel;
|
|
}
|
|
|
|
if(RequiredChannel->CheckPassword(password) || RequiredChannel->IsOwner(c->GetName()) || RequiredChannel->IsModerator(c->GetName()) ||
|
|
c->IsChannelAdmin()) {
|
|
|
|
RequiredChannel->AddClient(c);
|
|
|
|
return RequiredChannel;
|
|
}
|
|
|
|
c->GeneralChannelMessage("Incorrect password for channel " + (normalized_name));
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
ChatChannel *ChatChannelList::RemoveClientFromChannel(const std::string& in_channel_name, Client *c, bool command_directed) {
|
|
if(!c) return nullptr;
|
|
|
|
std::string channel_name = in_channel_name;
|
|
|
|
if (in_channel_name.length() > 0 && isdigit(channel_name[0])) {
|
|
channel_name = c->ChannelSlotName(Strings::ToInt(in_channel_name));
|
|
}
|
|
|
|
auto *required_channel = FindChannel(channel_name);
|
|
|
|
if (!required_channel) {
|
|
return nullptr;
|
|
}
|
|
|
|
LogDebug("Client [{}] removed from channel [{}]. Channel is owned by {}. Command directed: {}", c->GetName(), channel_name, required_channel->GetOwnerName(), command_directed);
|
|
if (c->GetName() == required_channel->GetOwnerName() && command_directed) { // Check if the client that is leaving is the channel owner
|
|
LogDebug("Owner left the channel [{}], removing channel from database...", channel_name);
|
|
database.DeleteChatChannel(channel_name); // Remove the channel from the database.
|
|
LogDebug("Flagging [{}] channel as temporary...", channel_name);
|
|
required_channel->SetTemporary();
|
|
}
|
|
|
|
// RemoveClient will return false if there is no-one left in the channel, and the channel is not permanent and has
|
|
// no password.
|
|
if (!required_channel->RemoveClient(c)) {
|
|
LogDebug("Noone left in the temporary channel [{}] and no password is set; removing temporary channel.", channel_name);
|
|
RemoveChannel(required_channel);
|
|
}
|
|
|
|
return required_channel;
|
|
}
|
|
|
|
void ChatChannelList::Process() {
|
|
|
|
LinkedListIterator<ChatChannel*> iterator(ChatChannels);
|
|
|
|
iterator.Reset();
|
|
|
|
while(iterator.MoreElements()) {
|
|
|
|
ChatChannel *CurrentChannel = iterator.GetData();
|
|
|
|
if(CurrentChannel && CurrentChannel->ReadyToDelete()) {
|
|
|
|
LogDebug("Empty temporary password protected channel [{}] being destroyed",
|
|
CurrentChannel->GetName().c_str());
|
|
|
|
iterator.RemoveCurrent();
|
|
}
|
|
else {
|
|
iterator.Advance();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ChatChannel::AddInvitee(const std::string &Invitee)
|
|
{
|
|
if (!IsInvitee(Invitee)) {
|
|
m_invitees.push_back(Invitee);
|
|
|
|
LogDebug("Added [{}] as invitee to channel [{}]", Invitee.c_str(), m_name.c_str());
|
|
}
|
|
|
|
}
|
|
|
|
void ChatChannel::RemoveInvitee(std::string Invitee)
|
|
{
|
|
auto it = std::find(std::begin(m_invitees), std::end(m_invitees), Invitee);
|
|
|
|
if(it != std::end(m_invitees)) {
|
|
m_invitees.erase(it);
|
|
LogDebug("Removed [{}] as invitee to channel [{}]", Invitee.c_str(), m_name.c_str());
|
|
}
|
|
}
|
|
|
|
bool ChatChannel::IsInvitee(std::string Invitee)
|
|
{
|
|
return std::find(std::begin(m_invitees), std::end(m_invitees), Invitee) != std::end(m_invitees);
|
|
}
|
|
|
|
void ChatChannel::AddModerator(const std::string &Moderator)
|
|
{
|
|
if (!IsModerator(Moderator)) {
|
|
m_moderators.push_back(Moderator);
|
|
|
|
LogInfo("Added [{}] as moderator to channel [{}]", Moderator.c_str(), m_name.c_str());
|
|
}
|
|
|
|
}
|
|
|
|
void ChatChannel::RemoveModerator(const std::string &Moderator)
|
|
{
|
|
auto it = std::find(std::begin(m_moderators), std::end(m_moderators), Moderator);
|
|
|
|
if (it != std::end(m_moderators)) {
|
|
m_moderators.erase(it);
|
|
LogInfo("Removed [{}] as moderator to channel [{}]", Moderator.c_str(), m_name.c_str());
|
|
}
|
|
}
|
|
|
|
bool ChatChannel::IsModerator(std::string Moderator)
|
|
{
|
|
return std::find(std::begin(m_moderators), std::end(m_moderators), Moderator) != std::end(m_moderators);
|
|
}
|
|
|
|
void ChatChannel::AddVoice(const std::string &inVoiced)
|
|
{
|
|
if (!HasVoice(inVoiced)) {
|
|
m_voiced.push_back(inVoiced);
|
|
|
|
LogInfo("Added [{}] as voiced to channel [{}]", inVoiced.c_str(), m_name.c_str());
|
|
}
|
|
}
|
|
|
|
void ChatChannel::RemoveVoice(const std::string &inVoiced)
|
|
{
|
|
auto it = std::find(std::begin(m_voiced), std::end(m_voiced), inVoiced);
|
|
|
|
if (it != std::end(m_voiced)) {
|
|
m_voiced.erase(it);
|
|
|
|
LogInfo("Removed [{}] as voiced to channel [{}]", inVoiced.c_str(), m_name.c_str());
|
|
}
|
|
}
|
|
|
|
bool ChatChannel::HasVoice(std::string inVoiced)
|
|
{
|
|
return std::find(std::begin(m_voiced), std::end(m_voiced), inVoiced) != std::end(m_voiced);
|
|
}
|
|
|
|
std::string CapitaliseName(const std::string& inString) {
|
|
|
|
std::string NormalisedName = inString;
|
|
|
|
for(unsigned int i = 0; i < NormalisedName.length(); i++) {
|
|
|
|
if(i == 0)
|
|
NormalisedName[i] = toupper(NormalisedName[i]);
|
|
else
|
|
NormalisedName[i] = tolower(NormalisedName[i]);
|
|
}
|
|
|
|
return NormalisedName;
|
|
}
|
|
|
|
bool ChatChannelList::IsOnChannelBlockList(const std::string& channel_name) {
|
|
if (channel_name.empty()) {
|
|
return false;
|
|
}
|
|
|
|
// Check if channel_name is already in the BlockedChannelNames vector
|
|
return Strings::Contains(GetBlockedChannelNames(), channel_name);
|
|
}
|
|
|
|
bool ChatChannelList::IsOnFilteredNameList(const std::string& name) {
|
|
if (name.empty()) {
|
|
return false;
|
|
}
|
|
|
|
// Check if name is already in the filtered name vector
|
|
return Strings::Contains(GetFilteredNames(), name);
|
|
}
|
|
|
|
|
|
void ChatChannelList::AddToChannelBlockList(const std::string& channel_name) {
|
|
if (channel_name.empty()) {
|
|
return;
|
|
}
|
|
|
|
// Check if channelName is already in the BlockedChannelNames vector
|
|
bool is_found = Strings::Contains(ChatChannelList::GetBlockedChannelNames(), channel_name);
|
|
|
|
// Add channelName to the BlockedChannelNames vector if it is not already present
|
|
if (!is_found) {
|
|
auto blocked_channel_names = GetBlockedChannelNames(); // Get current blocked list
|
|
blocked_channel_names.push_back(channel_name); // Add new name to local blocked list
|
|
SetChannelBlockList(blocked_channel_names); // Set blocked list to match local blocked list
|
|
}
|
|
}
|
|
|
|
void ChatChannelList::AddToFilteredNames(const std::string& name) {
|
|
if (name.empty()) {
|
|
return;
|
|
}
|
|
|
|
// Add name to the filtered names vector if it is not already present
|
|
if (!Strings::Contains(ChatChannelList::GetFilteredNames(), name)) {
|
|
auto filtered_names = GetFilteredNames(); // Get current filter name list
|
|
filtered_names.push_back(name); // Add new name to local filtered names list
|
|
SetFilteredNameList(filtered_names); // Set filtered names list to match local filtered names list
|
|
}
|
|
}
|
|
|
|
void ServerToClient45SayLink(std::string& clientSayLink, const std::string& serverSayLink) {
|
|
if (serverSayLink.find('\x12') == std::string::npos) {
|
|
clientSayLink = serverSayLink;
|
|
return;
|
|
}
|
|
|
|
auto segments = Strings::Split(serverSayLink, '\x12');
|
|
|
|
for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) {
|
|
if (segment_iter & 1) {
|
|
if (segments[segment_iter].length() <= 56) {
|
|
clientSayLink.append(segments[segment_iter]);
|
|
// TODO: log size mismatch error
|
|
continue;
|
|
}
|
|
|
|
// Idx: 0 1 6 11 16 21 26 31 36 37 41 43 48 (Source)
|
|
// RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56)
|
|
// 6.2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXXXXX (45)
|
|
// Diff: ^^^^^ ^ ^^^^^
|
|
|
|
clientSayLink.push_back('\x12');
|
|
clientSayLink.append(segments[segment_iter].substr(0, 31));
|
|
clientSayLink.append(segments[segment_iter].substr(36, 5));
|
|
|
|
if (segments[segment_iter][41] == '0')
|
|
clientSayLink.push_back(segments[segment_iter][42]);
|
|
else
|
|
clientSayLink.push_back('F');
|
|
|
|
clientSayLink.append(segments[segment_iter].substr(48));
|
|
clientSayLink.push_back('\x12');
|
|
}
|
|
else {
|
|
clientSayLink.append(segments[segment_iter]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ServerToClient50SayLink(std::string& clientSayLink, const std::string& serverSayLink) {
|
|
if (serverSayLink.find('\x12') == std::string::npos) {
|
|
clientSayLink = serverSayLink;
|
|
return;
|
|
}
|
|
|
|
auto segments = Strings::Split(serverSayLink, '\x12');
|
|
|
|
for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) {
|
|
if (segment_iter & 1) {
|
|
if (segments[segment_iter].length() <= 56) {
|
|
clientSayLink.append(segments[segment_iter]);
|
|
// TODO: log size mismatch error
|
|
continue;
|
|
}
|
|
|
|
// Idx: 0 1 6 11 16 21 26 31 36 37 41 43 48 (Source)
|
|
// RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56)
|
|
// SoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (50)
|
|
// Diff: ^^^^^ ^
|
|
|
|
clientSayLink.push_back('\x12');
|
|
clientSayLink.append(segments[segment_iter].substr(0, 31));
|
|
clientSayLink.append(segments[segment_iter].substr(36, 5));
|
|
|
|
if (segments[segment_iter][41] == '0')
|
|
clientSayLink.push_back(segments[segment_iter][42]);
|
|
else
|
|
clientSayLink.push_back('F');
|
|
|
|
clientSayLink.append(segments[segment_iter].substr(43));
|
|
clientSayLink.push_back('\x12');
|
|
}
|
|
else {
|
|
clientSayLink.append(segments[segment_iter]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ServerToClient55SayLink(std::string& clientSayLink, const std::string& serverSayLink) {
|
|
if (serverSayLink.find('\x12') == std::string::npos) {
|
|
clientSayLink = serverSayLink;
|
|
return;
|
|
}
|
|
|
|
auto segments = Strings::Split(serverSayLink, '\x12');
|
|
|
|
for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) {
|
|
if (segment_iter & 1) {
|
|
if (segments[segment_iter].length() <= 56) {
|
|
clientSayLink.append(segments[segment_iter]);
|
|
// TODO: log size mismatch error
|
|
continue;
|
|
}
|
|
|
|
// Idx: 0 1 6 11 16 21 26 31 36 37 41 43 48 (Source)
|
|
// RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56)
|
|
// RoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (55)
|
|
// Diff: ^
|
|
|
|
clientSayLink.push_back('\x12');
|
|
clientSayLink.append(segments[segment_iter].substr(0, 41));
|
|
|
|
if (segments[segment_iter][41] == '0')
|
|
clientSayLink.push_back(segments[segment_iter][42]);
|
|
else
|
|
clientSayLink.push_back('F');
|
|
|
|
clientSayLink.append(segments[segment_iter].substr(43));
|
|
clientSayLink.push_back('\x12');
|
|
}
|
|
else {
|
|
clientSayLink.append(segments[segment_iter]);
|
|
}
|
|
}
|
|
}
|