osb/source/game/StarChatProcessor.cpp
2023-06-20 14:33:09 +10:00

258 lines
7.0 KiB
C++

#include "StarChatProcessor.hpp"
namespace Star {
char const* ChatProcessor::ServerNick = "server";
String ChatProcessor::connectClient(ConnectionId clientId, String nick) {
RecursiveMutexLocker locker(m_mutex);
if (nick.empty())
nick = strf("Player_%s", clientId);
nick = makeNickUnique(nick);
for (auto& pair : m_clients) {
pair.second.pendingMessages.append({
{MessageContext::Broadcast},
ServerConnectionId,
ServerNick,
strf("Player '%s' connected", nick)
});
}
m_clients.add(clientId, ClientInfo(clientId, nick));
m_nicks[nick] = clientId;
return nick;
}
List<ChatReceivedMessage> ChatProcessor::disconnectClient(ConnectionId clientId) {
RecursiveMutexLocker locker(m_mutex);
for (auto channel : clientChannels(clientId))
leaveChannel(clientId, channel);
auto clientInfo = m_clients.take(clientId);
m_nicks.remove(clientInfo.nick);
for (auto& pair : m_clients) {
pair.second.pendingMessages.append({
{MessageContext::Broadcast},
ServerConnectionId,
ServerNick,
strf("Player '%s' disconnected", clientInfo.nick)
});
}
return clientInfo.pendingMessages;
}
List<ConnectionId> ChatProcessor::clients() const {
RecursiveMutexLocker locker(m_mutex);
return m_clients.keys();
}
bool ChatProcessor::hasClient(ConnectionId clientId) const {
RecursiveMutexLocker locker(m_mutex);
return m_clients.contains(clientId);
}
Maybe<ConnectionId> ChatProcessor::findNick(String const& nick) const {
RecursiveMutexLocker locker(m_mutex);
if (auto m = m_nicks.maybe(nick))
return m;
if (nick == ServerNick)
return ServerConnectionId;
return {};
}
String ChatProcessor::connectionNick(ConnectionId clientId) const {
RecursiveMutexLocker locker(m_mutex);
if (clientId == ServerConnectionId)
return ServerNick;
else
return m_clients.get(clientId).nick;
}
String ChatProcessor::renick(ConnectionId clientId, String const& nick) {
RecursiveMutexLocker locker(m_mutex);
auto& clientInfo = m_clients.get(clientId);
m_nicks.remove(clientInfo.nick);
clientInfo.nick = makeNickUnique(nick);
m_clients.get(clientId).nick = nick;
m_nicks[nick] = clientId;
return nick;
}
bool ChatProcessor::joinChannel(ConnectionId clientId, String const& channelName) {
RecursiveMutexLocker locker(m_mutex);
// Right now channels are simply created on join if they don't exist.
return m_channels[channelName].add(clientId);
}
bool ChatProcessor::leaveChannel(ConnectionId clientId, String const& channelName) {
RecursiveMutexLocker locker(m_mutex);
return m_channels[channelName].remove(clientId);
}
StringList ChatProcessor::clientChannels(ConnectionId clientId) const {
RecursiveMutexLocker locker(m_mutex);
StringList channels;
for (auto const& pair : m_channels) {
if (pair.second.contains(clientId))
channels.append(pair.first);
}
return channels;
}
StringList ChatProcessor::activeChannels() const {
RecursiveMutexLocker locker(m_mutex);
StringList channels;
for (auto const& pair : m_channels) {
if (!pair.second.empty())
channels.append(pair.first);
}
return channels;
}
void ChatProcessor::broadcast(ConnectionId sourceConnectionId, String const& text) {
RecursiveMutexLocker locker(m_mutex);
ChatReceivedMessage message = {
{MessageContext::Broadcast},
sourceConnectionId,
connectionNick(sourceConnectionId),
text
};
if (handleCommand(message))
return;
for (auto& pair : m_clients)
pair.second.pendingMessages.append(message);
}
void ChatProcessor::message(ConnectionId sourceConnectionId, MessageContext::Mode mode, String const& channelName, String const& text) {
RecursiveMutexLocker locker(m_mutex);
ChatReceivedMessage message = {
{mode, channelName},
sourceConnectionId,
connectionNick(sourceConnectionId),
text
};
if (handleCommand(message))
return;
for (auto clientId : m_channels[channelName]) {
auto& clientInfo = m_clients.get(clientId);
clientInfo.pendingMessages.append(message);
}
}
void ChatProcessor::whisper(ConnectionId sourceConnectionId, ConnectionId targetClientId, String const& text) {
RecursiveMutexLocker locker(m_mutex);
ChatReceivedMessage message = {
{MessageContext::Whisper}, sourceConnectionId, connectionNick(sourceConnectionId), text};
if (handleCommand(message))
return;
if (sourceConnectionId != ServerConnectionId)
m_clients.get(sourceConnectionId).pendingMessages.append(message);
m_clients.get(targetClientId).pendingMessages.append(message);
}
void ChatProcessor::adminBroadcast(String const& text) {
RecursiveMutexLocker locker(m_mutex);
broadcast(ServerConnectionId, text);
}
void ChatProcessor::adminMessage(MessageContext::Mode context, String const& channelName, String const& text) {
RecursiveMutexLocker locker(m_mutex);
ChatProcessor::message(ServerConnectionId, context, channelName, text);
}
void ChatProcessor::adminWhisper(ConnectionId targetClientId, String const& text) {
RecursiveMutexLocker locker(m_mutex);
whisper(ServerConnectionId, targetClientId, text);
}
List<ChatReceivedMessage> ChatProcessor::pullPendingMessages(ConnectionId clientId) {
RecursiveMutexLocker locker(m_mutex);
if (m_clients.contains(clientId))
return take(m_clients.get(clientId).pendingMessages);
return {};
}
void ChatProcessor::setCommandHandler(CommandHandler commandHandler) {
RecursiveMutexLocker locker(m_mutex);
m_commandHandler = commandHandler;
}
void ChatProcessor::clearCommandHandler() {
RecursiveMutexLocker locker(m_mutex);
m_commandHandler = {};
}
ChatProcessor::ClientInfo::ClientInfo(ConnectionId clientId, String const& nick) : clientId(clientId), nick(nick) {}
String ChatProcessor::makeNickUnique(String nick) {
while (m_nicks.contains(nick) || nick == ServerNick)
nick.append("_");
return nick;
}
bool ChatProcessor::handleCommand(ChatReceivedMessage& message) {
if (!message.text.beginsWith("/")) {
return false;
} else if (message.text.beginsWith("//")) {
message.text = message.text.substr(1);
return false;
}
String commandLine = message.text.substr(1);
String command = commandLine.extract();
String response;
if (command == "nick") {
auto newNick = renick(message.fromConnection, commandLine.trim());
response = strf("Nick changed to %s", newNick);
} else if (command == "w") {
String target = commandLine.extract();
if (m_nicks.contains(target))
whisper(message.fromConnection, m_nicks.get(target), commandLine.trim());
else
response = strf("No such nick %s", target);
} else if (m_commandHandler) {
response = m_commandHandler(message.fromConnection, command, commandLine);
} else {
response = strf("No such command %s", command);
}
if (!response.empty()) {
m_clients.get(message.fromConnection).pendingMessages.append({
MessageContext(MessageContext::CommandResult),
ServerConnectionId,
connectionNick(ServerConnectionId),
response
});
}
return true;
}
}