diff options
Diffstat (limited to 'source/game/StarChatProcessor.cpp')
-rw-r--r-- | source/game/StarChatProcessor.cpp | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/source/game/StarChatProcessor.cpp b/source/game/StarChatProcessor.cpp new file mode 100644 index 0000000..c298e13 --- /dev/null +++ b/source/game/StarChatProcessor.cpp @@ -0,0 +1,257 @@ +#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; +} + +} |