Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/game/StarChatProcessor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/game/StarChatProcessor.cpp')
-rw-r--r--source/game/StarChatProcessor.cpp257
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;
+}
+
+}