eqemu-server/ucs/chatchannel.cpp
Chris Miles dfd8f84cac
[Strings] Refactor Strings Usage (#2305)
* Initial commit checkpoint

* More functions converted

* Commify

* More functions

* Fin

* Sort declarations

* Split functions between files

* Bots

* Update strings.h

* Split

* Revert find replaces

* Repository template

* Money

* Misc function

* Update CMakeLists.txt

* Saylink

* Update strings.cpp

* Swap Strings::Saylink for Saylink::Create since saylink is coupled to zone database

* API casings
2022-07-14 02:10:52 -05:00

805 lines
19 KiB
C++

/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2008 EQEMu Development Team (http://eqemulator.net)
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; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "../common/eqemu_logsys.h"
#include "../common/strings.h"
#include "chatchannel.h"
#include "clientlist.h"
#include "database.h"
#include <cstdlib>
#include <algorithm>
extern Database 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(std::string inName, std::string inOwner, std::string inPassword, bool inPermanent, int inMinimumStatus) :
DeleteTimer(0) {
Name = inName;
Owner = inOwner;
Password = inPassword;
Permanent = inPermanent;
MinimumStatus = inMinimumStatus;
Moderated = false;
LogDebug(
"New ChatChannel created: Name: [[{}]], Owner: [[{}]], Password: [[{}]], MinStatus: [{}]",
Name.c_str(),
Owner.c_str(),
Password.c_str(),
MinimumStatus
);
}
ChatChannel::~ChatChannel() {
LinkedListIterator<Client*> iterator(ClientsInChannel);
iterator.Reset();
while(iterator.MoreElements())
iterator.RemoveCurrent(false);
}
ChatChannel* ChatChannelList::CreateChannel(std::string Name, std::string Owner, std::string Password, bool Permanent, int MinimumStatus) {
ChatChannel *NewChannel = new ChatChannel(CapitaliseName(Name), Owner, Password, Permanent, MinimumStatus);
ChatChannels.Insert(NewChannel);
return NewChannel;
}
ChatChannel* ChatChannelList::FindChannel(std::string Name) {
std::string NormalisedName = CapitaliseName(Name);
LinkedListIterator<ChatChannel*> iterator(ChatChannels);
iterator.Reset();
while(iterator.MoreElements()) {
ChatChannel *CurrentChannel = iterator.GetData();
if(CurrentChannel && (CurrentChannel->Name == NormalisedName))
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[10];
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("RemoveChannel ([{}])", 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(ClientsInChannel);
iterator.Reset();
while(iterator.MoreElements()) {
Client *ChannelClient = iterator.GetData();
if(ChannelClient && (!ChannelClient->GetHideMe() || (ChannelClient->GetAccountStatus() < Status)))
Count++;
iterator.Advance();
}
return Count;
}
void ChatChannel::SetPassword(std::string inPassword) {
Password = inPassword;
if(Permanent)
{
RemoveApostrophes(Password);
database.SetChannelPassword(Name, Password);
}
}
void ChatChannel::SetOwner(std::string inOwner) {
Owner = inOwner;
if(Permanent)
database.SetChannelOwner(Name, Owner);
}
void ChatChannel::AddClient(Client *c) {
if(!c) return;
DeleteTimer.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(), Name.c_str());
LinkedListIterator<Client*> iterator(ClientsInChannel);
iterator.Reset();
while(iterator.MoreElements()) {
Client *CurrentClient = iterator.GetData();
if(CurrentClient && CurrentClient->IsAnnounceOn())
if(!HideMe || (CurrentClient->GetAccountStatus() > AccountStatus))
CurrentClient->AnnounceJoin(this, c);
iterator.Advance();
}
ClientsInChannel.Insert(c);
}
bool ChatChannel::RemoveClient(Client *c) {
if(!c) return false;
LogDebug("RemoveClient [{}] from channel [{}]", c->GetName().c_str(), GetName().c_str());
bool HideMe = c->GetHideMe();
int AccountStatus = c->GetAccountStatus();
int PlayersInChannel = 0;
LinkedListIterator<Client*> iterator(ClientsInChannel);
iterator.Reset();
while(iterator.MoreElements()) {
Client *CurrentClient = iterator.GetData();
if(CurrentClient == c) {
iterator.RemoveCurrent(false);
}
else if(CurrentClient) {
PlayersInChannel++;
if(CurrentClient->IsAnnounceOn())
if(!HideMe || (CurrentClient->GetAccountStatus() > AccountStatus))
CurrentClient->AnnounceLeave(this, c);
iterator.Advance();
}
}
if((PlayersInChannel == 0) && !Permanent) {
if((Password.length() == 0) || (RuleI(Channels, DeleteTimer) == 0))
return false;
LogDebug("Starting delete timer for empty password protected channel [{}]", Name.c_str());
DeleteTimer.Start(RuleI(Channels, DeleteTimer) * 60000);
}
return true;
}
void ChatChannel::SendOPList(Client *c)
{
if (!c)
return;
c->GeneralChannelMessage("Channel " + Name + " op-list: (Owner=" + Owner + ")");
for (auto &&m : Moderators)
c->GeneralChannelMessage(m);
}
void ChatChannel::SendChannelMembers(Client *c) {
if(!c) return;
char CountString[10];
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(ClientsInChannel);
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(std::string Message, Client* Sender) {
if(!Sender) return;
std::string cv_messages[EQ::versions::ClientVersionCount];
ChatMessagesSent++;
LinkedListIterator<Client*> iterator(ClientsInChannel);
iterator.Reset();
while(iterator.MoreElements()) {
Client *ChannelClient = iterator.GetData();
if(ChannelClient)
{
LogDebug("Sending message to [{}] from [{}]",
ChannelClient->GetName().c_str(), Sender->GetName().c_str());
if (cv_messages[static_cast<uint32>(ChannelClient->GetClientVersion())].length() == 0) {
switch (ChannelClient->GetClientVersion()) {
case EQ::versions::ClientVersion::Titanium:
ServerToClient45SayLink(cv_messages[static_cast<uint32>(ChannelClient->GetClientVersion())], Message);
break;
case EQ::versions::ClientVersion::SoF:
case EQ::versions::ClientVersion::SoD:
case EQ::versions::ClientVersion::UF:
ServerToClient50SayLink(cv_messages[static_cast<uint32>(ChannelClient->GetClientVersion())], Message);
break;
case EQ::versions::ClientVersion::RoF:
ServerToClient55SayLink(cv_messages[static_cast<uint32>(ChannelClient->GetClientVersion())], Message);
break;
case EQ::versions::ClientVersion::RoF2:
default:
cv_messages[static_cast<uint32>(ChannelClient->GetClientVersion())] = Message;
break;
}
}
ChannelClient->SendChannelMessage(Name, cv_messages[static_cast<uint32>(ChannelClient->GetClientVersion())], Sender);
}
iterator.Advance();
}
}
void ChatChannel::SetModerated(bool inModerated) {
Moderated = inModerated;
LinkedListIterator<Client*> iterator(ClientsInChannel);
iterator.Reset();
while(iterator.MoreElements()) {
Client *ChannelClient = iterator.GetData();
if(ChannelClient) {
if(Moderated)
ChannelClient->GeneralChannelMessage("Channel " + Name + " is now moderated.");
else
ChannelClient->GeneralChannelMessage("Channel " + Name + " is no longer moderated.");
}
iterator.Advance();
}
}
bool ChatChannel::IsClientInChannel(Client *c) {
if(!c) return false;
LinkedListIterator<Client*> iterator(ClientsInChannel);
iterator.Reset();
while(iterator.MoreElements()) {
if(iterator.GetData() == c)
return true;
iterator.Advance();
}
return false;
}
ChatChannel *ChatChannelList::AddClientToChannel(std::string ChannelName, Client *c) {
if(!c) return nullptr;
if((ChannelName.length() > 0) && (isdigit(ChannelName[0]))) {
c->GeneralChannelMessage("The channel name can not begin with a number.");
return nullptr;
}
std::string NormalisedName, Password;
std::string::size_type Colon = ChannelName.find_first_of(":");
if(Colon == std::string::npos)
NormalisedName = CapitaliseName(ChannelName);
else {
NormalisedName = CapitaliseName(ChannelName.substr(0, Colon));
Password = ChannelName.substr(Colon + 1);
}
if((NormalisedName.length() > 64) || (Password.length() > 64)) {
c->GeneralChannelMessage("The channel name or password cannot exceed 64 characters.");
return nullptr;
}
LogDebug("AddClient to channel [[{}]] with password [[{}]]", NormalisedName.c_str(), Password.c_str());
ChatChannel *RequiredChannel = FindChannel(NormalisedName);
if(!RequiredChannel)
RequiredChannel = CreateChannel(NormalisedName, c->GetName(), Password, false, 0);
if(RequiredChannel->GetMinStatus() > c->GetAccountStatus()) {
std::string Message = "You do not have the required account status to join channel " + NormalisedName;
c->GeneralChannelMessage(Message);
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 " + (NormalisedName));
return nullptr;
}
ChatChannel *ChatChannelList::RemoveClientFromChannel(std::string inChannelName, Client *c) {
if(!c) return nullptr;
std::string ChannelName = inChannelName;
if((inChannelName.length() > 0) && isdigit(ChannelName[0]))
ChannelName = c->ChannelSlotName(atoi(inChannelName.c_str()));
ChatChannel *RequiredChannel = FindChannel(ChannelName);
if(!RequiredChannel)
return nullptr;
// RemoveClient will return false if there is no-one left in the channel, and the channel is not permanent and has
// no password.
//
if(!RequiredChannel->RemoveClient(c))
RemoveChannel(RequiredChannel);
return RequiredChannel;
}
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());
RemoveChannel(CurrentChannel);
}
iterator.Advance();
}
}
void ChatChannel::AddInvitee(const std::string &Invitee)
{
if (!IsInvitee(Invitee)) {
Invitees.push_back(Invitee);
LogDebug("Added [{}] as invitee to channel [{}]", Invitee.c_str(), Name.c_str());
}
}
void ChatChannel::RemoveInvitee(std::string Invitee)
{
auto it = std::find(std::begin(Invitees), std::end(Invitees), Invitee);
if(it != std::end(Invitees)) {
Invitees.erase(it);
LogDebug("Removed [{}] as invitee to channel [{}]", Invitee.c_str(), Name.c_str());
}
}
bool ChatChannel::IsInvitee(std::string Invitee)
{
return std::find(std::begin(Invitees), std::end(Invitees), Invitee) != std::end(Invitees);
}
void ChatChannel::AddModerator(const std::string &Moderator)
{
if (!IsModerator(Moderator)) {
Moderators.push_back(Moderator);
LogInfo("Added [{}] as moderator to channel [{}]", Moderator.c_str(), Name.c_str());
}
}
void ChatChannel::RemoveModerator(const std::string &Moderator)
{
auto it = std::find(std::begin(Moderators), std::end(Moderators), Moderator);
if (it != std::end(Moderators)) {
Moderators.erase(it);
LogInfo("Removed [{}] as moderator to channel [{}]", Moderator.c_str(), Name.c_str());
}
}
bool ChatChannel::IsModerator(std::string Moderator)
{
return std::find(std::begin(Moderators), std::end(Moderators), Moderator) != std::end(Moderators);
}
void ChatChannel::AddVoice(const std::string &inVoiced)
{
if (!HasVoice(inVoiced)) {
Voiced.push_back(inVoiced);
LogInfo("Added [{}] as voiced to channel [{}]", inVoiced.c_str(), Name.c_str());
}
}
void ChatChannel::RemoveVoice(const std::string &inVoiced)
{
auto it = std::find(std::begin(Voiced), std::end(Voiced), inVoiced);
if (it != std::end(Voiced)) {
Voiced.erase(it);
LogInfo("Removed [{}] as voiced to channel [{}]", inVoiced.c_str(), Name.c_str());
}
}
bool ChatChannel::HasVoice(std::string inVoiced)
{
return std::find(std::begin(Voiced), std::end(Voiced), inVoiced) != std::end(Voiced);
}
std::string CapitaliseName(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;
}
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]);
}
}
}