mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-16 22:58:34 +00:00
[Rules] Add rule to allow players to permanently save chat channels to database, up to a limit. (#2706)
* Initial code * Tweak * Rule description tweak * More channel work * More adjustments * Auto-join saved permanent player channels * Fix UCS crash if player has no channels to load from table. * Implemented channel blocking feature * Update database when player channel's owner or password change * First round of requested changes. * Logic tweak to ensure player channels are sets to permanent when appropraite * name_filter table integration and some refactoring * Use new `reserved_channel_names` table to block specific channel names. * Remove some legacy channel block code * Setup required SQL update to create `reserved_channel_names` table. * Update db_update_manifest.txt * Update db_update_manifest.txt * Update chatchannel.cpp * Code review * Database to UCSDatabase * Repository SaveChatChannel * CurrentPlayerChannelCount repository * Cleanup name filter * CreateChannel * Update websocketpp * Increment CURRENT_BINARY_DATABASE_VERSION Set to 9216 * Minor tweaks to blocked channel name checks & other related areas. - Enforce blocked channel names on channel creation. - Also enforce blocked channel names on channel join. - Add channel status check to Debug logging. - Minor formatting adjustments. - Add single quotes to column name value in query. * Minor log change * Increment DB Version * Formatting Tweaks - Made formatting adjustments consistent with KinglyKrab's recommended changes. - This compiles successfully with these changes, but unable to test the changes until this weekend. Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
+132
-92
@@ -48,8 +48,10 @@ int LookupCommand(const char *ChatCommand) {
|
||||
|
||||
for (int i = 0; i < CommandEndOfList; i++) {
|
||||
|
||||
if (!strcasecmp(Commands[i].CommandString, ChatCommand))
|
||||
if (!strcasecmp(Commands[i].CommandString, ChatCommand)) {
|
||||
return Commands[i].CommandCode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return -1;
|
||||
@@ -594,6 +596,28 @@ void Clientlist::CheckForStaleConnections(Client *c) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string RemoveDuplicateChannels(const std::string& in_channels) {
|
||||
// Split the string by ", " and store the names in a vector
|
||||
std::vector<std::string> channel_names = Strings::Split(in_channels, ", ");
|
||||
|
||||
// Remove duplicates by inserting the names of the channels into an unordered set
|
||||
// and then copying the unique elements back into the original vector
|
||||
std::unordered_set<std::string> unique_channels;
|
||||
channel_names.erase(
|
||||
std::remove_if(
|
||||
channel_names.begin(), channel_names.end(),
|
||||
[&unique_channels](const std::string &channel) {
|
||||
return !unique_channels.insert(channel).second;
|
||||
}
|
||||
), channel_names.end()
|
||||
);
|
||||
|
||||
// Concatenate the names of the unique channels into a single string
|
||||
std::string unique_channels_string = Strings::Implode(", ", channel_names);
|
||||
|
||||
return unique_channels_string;
|
||||
}
|
||||
|
||||
void Clientlist::Process()
|
||||
{
|
||||
auto it = ClientChatConnections.begin();
|
||||
@@ -682,10 +706,18 @@ void Clientlist::Process()
|
||||
CheckForStaleConnections((*it));
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_Mail: {
|
||||
std::string CommandString = (const char *)app->pBuffer + 1;
|
||||
ProcessOPMailCommand((*it), CommandString);
|
||||
std::string command_string = (const char *)app->pBuffer + 1;
|
||||
bool command_directed = false;
|
||||
if (command_string.empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (Strings::Contains(Strings::ToLower(command_string), "leave")) {
|
||||
command_directed = true;
|
||||
}
|
||||
|
||||
ProcessOPMailCommand((*it), command_string, command_directed);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -716,106 +748,112 @@ void Clientlist::Process()
|
||||
}
|
||||
}
|
||||
|
||||
void Clientlist::ProcessOPMailCommand(Client *c, std::string CommandString)
|
||||
void Clientlist::ProcessOPMailCommand(Client *c, std::string command_string, bool command_directed)
|
||||
{
|
||||
|
||||
if (CommandString.length() == 0)
|
||||
if (command_string.length() == 0)
|
||||
return;
|
||||
|
||||
if (isdigit(CommandString[0]))
|
||||
if (isdigit(command_string[0]))
|
||||
{
|
||||
|
||||
c->SendChannelMessageByNumber(CommandString);
|
||||
c->SendChannelMessageByNumber(command_string);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (CommandString[0] == '#') {
|
||||
if (command_string[0] == '#') {
|
||||
|
||||
c->SendChannelMessage(CommandString);
|
||||
c->SendChannelMessage(command_string);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::string Command, Parameters;
|
||||
std::string command, parameters;
|
||||
|
||||
std::string::size_type Space = CommandString.find_first_of(" ");
|
||||
std::string::size_type Space = command_string.find_first_of(" ");
|
||||
|
||||
if (Space != std::string::npos) {
|
||||
|
||||
Command = CommandString.substr(0, Space);
|
||||
command = command_string.substr(0, Space);
|
||||
|
||||
std::string::size_type ParametersStart = CommandString.find_first_not_of(" ", Space);
|
||||
std::string::size_type parameters_start = command_string.find_first_not_of(" ", Space);
|
||||
|
||||
if (ParametersStart != std::string::npos)
|
||||
Parameters = CommandString.substr(ParametersStart);
|
||||
if (parameters_start != std::string::npos)
|
||||
parameters = command_string.substr(parameters_start);
|
||||
}
|
||||
else {
|
||||
command = command_string;
|
||||
}
|
||||
else
|
||||
Command = CommandString;
|
||||
|
||||
int CommandCode = LookupCommand(Command.c_str());
|
||||
|
||||
switch (CommandCode) {
|
||||
auto command_code = LookupCommand(command.c_str());
|
||||
switch (command_code) {
|
||||
|
||||
case CommandJoin:
|
||||
c->JoinChannels(Parameters);
|
||||
if (!command_directed) {
|
||||
//Append saved channels to params
|
||||
parameters = parameters + ", " + database.CurrentPlayerChannels(c->GetName());
|
||||
parameters = RemoveDuplicateChannels(parameters);
|
||||
}
|
||||
c->JoinChannels(parameters, command_directed);
|
||||
break;
|
||||
|
||||
case CommandLeaveAll:
|
||||
c->LeaveAllChannels();
|
||||
c->LeaveAllChannels(true, true);
|
||||
break;
|
||||
|
||||
case CommandLeave:
|
||||
c->LeaveChannels(Parameters);
|
||||
c->LeaveChannels(parameters, command_directed);
|
||||
break;
|
||||
|
||||
case CommandListAll:
|
||||
|
||||
ChannelList->SendAllChannels(c);
|
||||
break;
|
||||
|
||||
case CommandList:
|
||||
c->ProcessChannelList(Parameters);
|
||||
c->ProcessChannelList(parameters);
|
||||
break;
|
||||
|
||||
case CommandSet:
|
||||
c->LeaveAllChannels(false);
|
||||
c->JoinChannels(Parameters);
|
||||
c->JoinChannels(parameters, command_directed);
|
||||
break;
|
||||
|
||||
case CommandAnnounce:
|
||||
c->ToggleAnnounce(Parameters);
|
||||
c->ToggleAnnounce(parameters);
|
||||
break;
|
||||
|
||||
case CommandSetOwner:
|
||||
c->SetChannelOwner(Parameters);
|
||||
c->SetChannelOwner(parameters);
|
||||
break;
|
||||
|
||||
case CommandOPList:
|
||||
c->OPList(Parameters);
|
||||
c->OPList(parameters);
|
||||
break;
|
||||
|
||||
case CommandInvite:
|
||||
c->ChannelInvite(Parameters);
|
||||
c->ChannelInvite(parameters);
|
||||
break;
|
||||
|
||||
case CommandGrant:
|
||||
c->ChannelGrantModerator(Parameters);
|
||||
c->ChannelGrantModerator(parameters);
|
||||
break;
|
||||
|
||||
case CommandModerate:
|
||||
c->ChannelModerate(Parameters);
|
||||
c->ChannelModerate(parameters);
|
||||
break;
|
||||
|
||||
case CommandVoice:
|
||||
c->ChannelGrantVoice(Parameters);
|
||||
c->ChannelGrantVoice(parameters);
|
||||
break;
|
||||
|
||||
case CommandKick:
|
||||
c->ChannelKick(Parameters);
|
||||
c->ChannelKick(parameters);
|
||||
break;
|
||||
|
||||
case CommandPassword:
|
||||
c->SetChannelPassword(Parameters);
|
||||
c->SetChannelPassword(parameters);
|
||||
break;
|
||||
|
||||
case CommandToggleInvites:
|
||||
@@ -834,43 +872,44 @@ void Clientlist::ProcessOPMailCommand(Client *c, std::string CommandString)
|
||||
break;
|
||||
|
||||
case CommandGetBody:
|
||||
database.SendBody(c, atoi(Parameters.c_str()));
|
||||
database.SendBody(c, atoi(parameters.c_str()));
|
||||
break;
|
||||
|
||||
case CommandMailTo:
|
||||
ProcessMailTo(c, Parameters);
|
||||
ProcessMailTo(c, parameters);
|
||||
break;
|
||||
|
||||
case CommandSetMessageStatus:
|
||||
LogInfo("Set Message Status, Params: [{}]", Parameters.c_str());
|
||||
ProcessSetMessageStatus(Parameters);
|
||||
LogInfo("Set Message Status, Params: [{}]", parameters.c_str());
|
||||
ProcessSetMessageStatus(parameters);
|
||||
break;
|
||||
|
||||
case CommandSelectMailBox:
|
||||
{
|
||||
std::string::size_type NumStart = Parameters.find_first_of("0123456789");
|
||||
c->ChangeMailBox(atoi(Parameters.substr(NumStart).c_str()));
|
||||
std::string::size_type NumStart = parameters.find_first_of("0123456789");
|
||||
c->ChangeMailBox(atoi(parameters.substr(NumStart).c_str()));
|
||||
break;
|
||||
}
|
||||
case CommandSetMailForwarding:
|
||||
break;
|
||||
|
||||
case CommandBuddy:
|
||||
RemoveApostrophes(Parameters);
|
||||
ProcessCommandBuddy(c, Parameters);
|
||||
RemoveApostrophes(parameters);
|
||||
ProcessCommandBuddy(c, parameters);
|
||||
break;
|
||||
|
||||
case CommandIgnorePlayer:
|
||||
RemoveApostrophes(Parameters);
|
||||
ProcessCommandIgnore(c, Parameters);
|
||||
RemoveApostrophes(parameters);
|
||||
ProcessCommandIgnore(c, parameters);
|
||||
break;
|
||||
|
||||
default:
|
||||
c->SendHelp();
|
||||
LogInfo("Unhandled OP_Mail command: [{}]", CommandString.c_str());
|
||||
LogInfo("Unhandled OP_Mail command: [{}]", command_string.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Clientlist::CloseAllConnections() {
|
||||
|
||||
|
||||
@@ -992,51 +1031,51 @@ int Client::ChannelCount() {
|
||||
|
||||
}
|
||||
|
||||
void Client::JoinChannels(std::string ChannelNameList) {
|
||||
void Client::JoinChannels(std::string& channel_name_list, bool command_directed) {
|
||||
|
||||
for (auto &elem : ChannelNameList) {
|
||||
for (auto &elem : channel_name_list) {
|
||||
if (elem == '%') {
|
||||
elem = '/';
|
||||
}
|
||||
}
|
||||
|
||||
LogInfo("Client: [{}] joining channels [{}]", GetName().c_str(), ChannelNameList.c_str());
|
||||
LogInfo("Client: [{}] joining channels [{}]", GetName().c_str(), channel_name_list.c_str());
|
||||
|
||||
int NumberOfChannels = ChannelCount();
|
||||
auto number_of_channels = ChannelCount();
|
||||
|
||||
std::string::size_type CurrentPos = ChannelNameList.find_first_not_of(" ");
|
||||
auto current_pos = channel_name_list.find_first_not_of(" ");
|
||||
|
||||
while (CurrentPos != std::string::npos) {
|
||||
while (current_pos != std::string::npos) {
|
||||
|
||||
if (NumberOfChannels == MAX_JOINED_CHANNELS) {
|
||||
if (number_of_channels == MAX_JOINED_CHANNELS) {
|
||||
|
||||
GeneralChannelMessage("You have joined the maximum number of channels. /leave one before trying to join another.");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
std::string::size_type Comma = ChannelNameList.find_first_of(", ", CurrentPos);
|
||||
auto comma = channel_name_list.find_first_of(", ", current_pos);
|
||||
|
||||
if (Comma == std::string::npos) {
|
||||
if (comma == std::string::npos) {
|
||||
auto* joined_channel = ChannelList->AddClientToChannel(channel_name_list.substr(current_pos), this, command_directed);
|
||||
|
||||
ChatChannel* JoinedChannel = ChannelList->AddClientToChannel(ChannelNameList.substr(CurrentPos), this);
|
||||
|
||||
if (JoinedChannel)
|
||||
AddToChannelList(JoinedChannel);
|
||||
if (joined_channel) {
|
||||
AddToChannelList(joined_channel);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ChatChannel* JoinedChannel = ChannelList->AddClientToChannel(ChannelNameList.substr(CurrentPos, Comma - CurrentPos), this);
|
||||
auto* joined_channel = ChannelList->AddClientToChannel(channel_name_list.substr(current_pos, comma - current_pos), this, command_directed);
|
||||
|
||||
if (JoinedChannel) {
|
||||
if (joined_channel) {
|
||||
|
||||
AddToChannelList(JoinedChannel);
|
||||
AddToChannelList(joined_channel);
|
||||
|
||||
NumberOfChannels++;
|
||||
number_of_channels++;
|
||||
}
|
||||
|
||||
CurrentPos = ChannelNameList.find_first_not_of(", ", Comma);
|
||||
current_pos = channel_name_list.find_first_not_of(", ", comma);
|
||||
}
|
||||
|
||||
std::string JoinedChannelsList, ChannelMessage;
|
||||
@@ -1097,37 +1136,36 @@ void Client::JoinChannels(std::string ChannelNameList) {
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
void Client::LeaveChannels(std::string ChannelNameList) {
|
||||
void Client::LeaveChannels(std::string& channel_name_list, bool command_directed) {
|
||||
LogInfo("Client: [{}] leaving channels [{}]", GetName().c_str(), channel_name_list.c_str());
|
||||
|
||||
LogInfo("Client: [{}] leaving channels [{}]", GetName().c_str(), ChannelNameList.c_str());
|
||||
auto current_pos = 0;
|
||||
|
||||
std::string::size_type CurrentPos = 0;
|
||||
while (current_pos != std::string::npos) {
|
||||
|
||||
while (CurrentPos != std::string::npos) {
|
||||
|
||||
std::string::size_type Comma = ChannelNameList.find_first_of(", ", CurrentPos);
|
||||
std::string::size_type Comma = channel_name_list.find_first_of(", ", current_pos);
|
||||
|
||||
if (Comma == std::string::npos) {
|
||||
|
||||
ChatChannel* JoinedChannel = ChannelList->RemoveClientFromChannel(ChannelNameList.substr(CurrentPos), this);
|
||||
auto* joined_channel = ChannelList->RemoveClientFromChannel(channel_name_list.substr(current_pos), this, command_directed);
|
||||
|
||||
if (JoinedChannel)
|
||||
RemoveFromChannelList(JoinedChannel);
|
||||
if (joined_channel)
|
||||
RemoveFromChannelList(joined_channel);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ChatChannel* JoinedChannel = ChannelList->RemoveClientFromChannel(ChannelNameList.substr(CurrentPos, Comma - CurrentPos), this);
|
||||
auto* joined_channel = ChannelList->RemoveClientFromChannel(channel_name_list.substr(current_pos, Comma - current_pos), this, command_directed);
|
||||
|
||||
if (JoinedChannel)
|
||||
RemoveFromChannelList(JoinedChannel);
|
||||
if (joined_channel)
|
||||
RemoveFromChannelList(joined_channel);
|
||||
|
||||
CurrentPos = ChannelNameList.find_first_not_of(", ", Comma);
|
||||
current_pos = channel_name_list.find_first_not_of(", ", Comma);
|
||||
}
|
||||
|
||||
std::string JoinedChannelsList, ChannelMessage;
|
||||
std::string joined_channels_list, channel_message;
|
||||
|
||||
ChannelMessage = "Channels: ";
|
||||
channel_message = "Channels: ";
|
||||
|
||||
char tmp[200];
|
||||
|
||||
@@ -1139,26 +1177,26 @@ void Client::LeaveChannels(std::string ChannelNameList) {
|
||||
|
||||
if (ChannelCount) {
|
||||
|
||||
JoinedChannelsList = JoinedChannelsList + ",";
|
||||
joined_channels_list = joined_channels_list + ",";
|
||||
|
||||
ChannelMessage = ChannelMessage + ",";
|
||||
channel_message = channel_message + ",";
|
||||
}
|
||||
|
||||
JoinedChannelsList = JoinedChannelsList + JoinedChannels[i]->GetName();
|
||||
joined_channels_list = joined_channels_list + JoinedChannels[i]->GetName();
|
||||
|
||||
sprintf(tmp, "%i=%s(%i)", i + 1, JoinedChannels[i]->GetName().c_str(), JoinedChannels[i]->MemberCount(Status));
|
||||
|
||||
ChannelMessage += tmp;
|
||||
channel_message += tmp;
|
||||
|
||||
ChannelCount++;
|
||||
}
|
||||
}
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_Mail, JoinedChannelsList.length() + 1);
|
||||
auto outapp = new EQApplicationPacket(OP_Mail, joined_channels_list.length() + 1);
|
||||
|
||||
char *PacketBuffer = (char *)outapp->pBuffer;
|
||||
|
||||
sprintf(PacketBuffer, "%s", JoinedChannelsList.c_str());
|
||||
sprintf(PacketBuffer, "%s", joined_channels_list.c_str());
|
||||
|
||||
|
||||
QueuePacket(outapp);
|
||||
@@ -1166,15 +1204,15 @@ void Client::LeaveChannels(std::string ChannelNameList) {
|
||||
safe_delete(outapp);
|
||||
|
||||
if (ChannelCount == 0)
|
||||
ChannelMessage = "You are not on any channels.";
|
||||
channel_message = "You are not on any channels.";
|
||||
|
||||
outapp = new EQApplicationPacket(OP_ChannelMessage, ChannelMessage.length() + 3);
|
||||
outapp = new EQApplicationPacket(OP_ChannelMessage, channel_message.length() + 3);
|
||||
|
||||
PacketBuffer = (char *)outapp->pBuffer;
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 0x00);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 0x00);
|
||||
VARSTRUCT_ENCODE_STRING(PacketBuffer, ChannelMessage.c_str());
|
||||
VARSTRUCT_ENCODE_STRING(PacketBuffer, channel_message.c_str());
|
||||
|
||||
|
||||
QueuePacket(outapp);
|
||||
@@ -1182,19 +1220,19 @@ void Client::LeaveChannels(std::string ChannelNameList) {
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
void Client::LeaveAllChannels(bool SendUpdatedChannelList) {
|
||||
void Client::LeaveAllChannels(bool send_updated_channel_list, bool command_directed) {
|
||||
|
||||
for (auto &elem : JoinedChannels) {
|
||||
|
||||
if (elem) {
|
||||
|
||||
ChannelList->RemoveClientFromChannel(elem->GetName(), this);
|
||||
ChannelList->RemoveClientFromChannel(elem->GetName(), this, command_directed);
|
||||
|
||||
elem = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (SendUpdatedChannelList)
|
||||
if (send_updated_channel_list)
|
||||
SendChannelList();
|
||||
}
|
||||
|
||||
@@ -1643,6 +1681,7 @@ void Client::SetChannelPassword(std::string ChannelPassword) {
|
||||
}
|
||||
|
||||
RequiredChannel->SetPassword(Password);
|
||||
database.SaveChatChannel(RequiredChannel->GetName(), RequiredChannel->GetOwnerName(), Password, RequiredChannel->GetMinStatus()); // Update DB with new password
|
||||
|
||||
GeneralChannelMessage(Message);
|
||||
|
||||
@@ -1703,6 +1742,7 @@ void Client::SetChannelOwner(std::string CommandString) {
|
||||
}
|
||||
|
||||
RequiredChannel->SetOwner(NewOwner);
|
||||
database.SaveChatChannel(RequiredChannel->GetName(), NewOwner, RequiredChannel->GetPassword(), RequiredChannel->GetMinStatus()); // Update DB with new owner
|
||||
|
||||
if (RequiredChannel->IsModerator(NewOwner))
|
||||
RequiredChannel->RemoveModerator(NewOwner);
|
||||
@@ -2121,7 +2161,7 @@ void Client::ChannelKick(std::string CommandString) {
|
||||
|
||||
GeneralChannelMessage("Kicked " + Kickee + " from channel " + ChannelName);
|
||||
|
||||
RequiredClient->LeaveChannels(ChannelName);
|
||||
RequiredClient->LeaveChannels(ChannelName, false);
|
||||
}
|
||||
|
||||
void Client::ToggleInvites() {
|
||||
|
||||
Reference in New Issue
Block a user