diff options
| author | littleblack111 <littleblack11111@gmail.com> | 2025-06-12 21:41:41 +0800 |
|---|---|---|
| committer | littleblack111 <littleblack11111@gmail.com> | 2025-06-13 20:22:51 +0800 |
| commit | 6f7b994684032060f0a2d7af82f4f3572293b8d6 (patch) | |
| tree | 793d328cbcd6761365267550b55db45aee9b21fc /src/core | |
| parent | 6a51ab4525c59e9fe2bdbd92493019b0a98c6b94 (diff) | |
internal!: organized internal structure, using cmake now
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/chatManager.cpp | 36 | ||||
| -rw-r--r-- | src/core/chatManager.hpp | 25 | ||||
| -rw-r--r-- | src/core/chatServer.cpp | 68 | ||||
| -rw-r--r-- | src/core/chatServer.hpp | 20 | ||||
| -rw-r--r-- | src/core/server.cpp | 39 | ||||
| -rw-r--r-- | src/core/server.hpp | 19 | ||||
| -rw-r--r-- | src/core/session.cpp | 273 | ||||
| -rw-r--r-- | src/core/session.hpp | 88 | ||||
| -rw-r--r-- | src/core/sessionManager.cpp | 141 | ||||
| -rw-r--r-- | src/core/sessionManager.hpp | 16 |
10 files changed, 717 insertions, 8 deletions
diff --git a/src/core/chatManager.cpp b/src/core/chatManager.cpp new file mode 100644 index 0000000..e981e7f --- /dev/null +++ b/src/core/chatManager.cpp @@ -0,0 +1,36 @@ +#include "chatManager.hpp" +#include "../io/IOManager.hpp" +#include "../io/inputManager.hpp" +#include "../debug/log.hpp" +#include "sessionManager.hpp" + +CChatManager::CChatManager() { + log(LOG, "ChatManager: initialized"); +} + +CChatManager::~CChatManager() { + log(SYS, "ChatManager: bye"); +} + +void CChatManager::newMessage(const SMessage &msg) { + log(LOG, "{}: {}", msg.username, msg.msg); + const auto session = msg.sender ? msg.sender->lock() : g_pSessionManager->getByName(msg.username); + if (session && session->isMuted()) { + session->write("You are muted"); + return; + } + g_pSessionManager->broadcast(msg); +} + +std::string CChatManager::fmtBroadcastMessage(const SMessage &msg) { + return NFormatter::fmt(NONE, "{}: {}", msg.username, msg.msg); +} + +std::vector<CChatManager::SMessage> CChatManager::getChat() const { + std::vector<SMessage> chat; + + for (const auto &[msg, _, _] : g_pIOManager->getIO()) + if (msg) + chat.push_back(*msg); + return chat; +} diff --git a/src/core/chatManager.hpp b/src/core/chatManager.hpp new file mode 100644 index 0000000..bd305de --- /dev/null +++ b/src/core/chatManager.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include <memory> +#include <optional> +#include <string> +#include <vector> + +class CSession; + +class CChatManager { + public: + CChatManager(); + ~CChatManager(); + struct SMessage { + std::string msg; + std::string username; + std::optional<std::weak_ptr<CSession>> sender = std::nullopt; + bool admin = false; + }; + void newMessage(const SMessage &msg); + std::string fmtBroadcastMessage(const SMessage &msg); + + std::vector<SMessage> getChat() const; +}; +inline std::unique_ptr<CChatManager> g_pChatManager; diff --git a/src/core/chatServer.cpp b/src/core/chatServer.cpp new file mode 100644 index 0000000..4a16c56 --- /dev/null +++ b/src/core/chatServer.cpp @@ -0,0 +1,68 @@ +#include "chatServer.hpp" +#include "../io/IOManager.hpp" +#include "chatManager.hpp" +#include "../command/commandHandler.hpp" +#include "../command/commands.hpp" +#include "../io/inputManager.hpp" +#include "../debug/log.hpp" +#include "../io/renderManager.hpp" +#include "server.hpp" +#include "sessionManager.hpp" + +CChatServer::CChatServer(uint16_t port) + : m_port(port) { + if (std::atexit([]() { g_pChatServer->cleanup(); })) + log(ERR, "Failed to register CChatServer atexit handler, dtors will not be called"); + + log(LOG, "ChatServer: initialized"); +}; + +CChatServer::~CChatServer() { + cleanup(); + log(SYS, "ChatServer: bye"); +} + +void CChatServer::cleanup() { + if (g_pSessionManager) { + g_pSessionManager->shutdownSessions(); + g_pSessionManager.reset(); + } + + g_pChatManager.reset(); + g_pServer.reset(); + g_pCommandHandler.reset(); + g_pInputManager.reset(); + g_pIOManager.reset(); + g_pRenderManager.reset(); +} + +void CChatServer::initManagers() { + log(LOG, "ChatServer: initializing managers"); + + log(LOG, "Creating RenderManager"); + g_pRenderManager = std::make_unique<CRenderManager>(); + log(LOG, "Creating InputManager"); + g_pInputManager = std::make_unique<CInputManager>(); + log(LOG, "Creating Server"); + g_pServer = std::make_unique<CServer>(m_port); + log(LOG, "Creating IOManager"); + g_pIOManager = std::make_unique<CIOManager>(); + log(LOG, "Creating SessionManager"); + g_pSessionManager = std::make_shared<CSessionManager>(); + log(LOG, "Creating ChatManager"); + g_pChatManager = std::make_unique<CChatManager>(); +} + +void CChatServer::start() { + initManagers(); + + log(LOG, "Creating CommandHandler"); + g_pCommandHandler = std::make_unique<CCommandHandler>(); + registerCommands(); + + // will await while waiting for accept() to return a new client + m_sessionManagerThread = std::jthread(&CSessionManager::run, g_pSessionManager.get()); + m_sessionManagerThread.detach(); + + g_pInputManager->inputLoop(); +} diff --git a/src/core/chatServer.hpp b/src/core/chatServer.hpp new file mode 100644 index 0000000..00d2979 --- /dev/null +++ b/src/core/chatServer.hpp @@ -0,0 +1,20 @@ +#include <memory> +#include <thread> + +class CChatServer { + public: + CChatServer(uint16_t port); + ~CChatServer(); + void start(); + void cleanup(); + + private: + uint16_t m_port; + + void initManagers(); + void initServer(); + + std::jthread m_sessionManagerThread; +}; + +inline std::unique_ptr<CChatServer> g_pChatServer; diff --git a/src/core/server.cpp b/src/core/server.cpp new file mode 100644 index 0000000..a78c217 --- /dev/null +++ b/src/core/server.cpp @@ -0,0 +1,39 @@ +#include "server.hpp" +#include "../debug/log.hpp" +#include <cstring> +#include <memory> +#include <sys/socket.h> + +CServer::CServer(uint16_t port) + : m_sockfd(std::make_shared<CFileDescriptor>(socket(AF_INET, SOCK_STREAM, 0))) { + if (!m_sockfd->isValid()) + throw std::runtime_error("Failed to create socket"); + + constexpr int opt = 1; + constexpr int size = sizeof(opt); + if (setsockopt(m_sockfd->get(), SOL_SOCKET, SO_REUSEADDR, &opt, size) < 0) + log(stderr, WARN, "setsockopt(SO_REUSEADDR) failed"); + + if (setsockopt(m_sockfd->get(), SOL_SOCKET, SO_KEEPALIVE, &opt, size) < 0) + log(stderr, WARN, "setsockopt(SO_KEEPALIVE) failed"); + + memset(&m_addr, 0, sizeof(m_addr)); + m_addr.sin_family = AF_INET; + m_addr.sin_port = htons(port); + m_addr.sin_addr.s_addr = INADDR_ANY; + bind(m_sockfd->get(), reinterpret_cast<sockaddr *>(&m_addr), sizeof(m_addr)) || + listen(m_sockfd->get(), 5) + ? throw std::runtime_error("Failed to bind or listen") + : log(LOG, "Server started on port: {}", port); + + log(LOG, "Server: initialized"); +}; + +CServer::~CServer() { + m_sockfd.reset(); + log(SYS, "Server: bye"); +} + +std::shared_ptr<CFileDescriptor> CServer::getSocket() const { + return m_sockfd; +} diff --git a/src/core/server.hpp b/src/core/server.hpp new file mode 100644 index 0000000..c42192b --- /dev/null +++ b/src/core/server.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "../misc/FileDescriptor.hpp" +#include <memory> +#include <netinet/in.h> + +class CServer { + public: + CServer(uint16_t port); + ~CServer(); + std::shared_ptr<CFileDescriptor> getSocket() const; + + private: + std::shared_ptr<CFileDescriptor> m_sockfd; + sockaddr_in m_addr; + + mutable std::mutex m_mutex; +}; +inline std::unique_ptr<CServer> g_pServer; diff --git a/src/core/session.cpp b/src/core/session.cpp new file mode 100644 index 0000000..7b0b459 --- /dev/null +++ b/src/core/session.cpp @@ -0,0 +1,273 @@ +#include "session.hpp" +#include "chatManager.hpp" +#include "../command/commandHandler.hpp" +#include "../debug/format.hpp" +#include "../debug/log.hpp" +#include "server.hpp" +#include "sessionManager.hpp" +#include <algorithm> +#include <arpa/inet.h> +#include <cerrno> +#include <cstring> +#include <memory> +#include <optional> +#include <sys/socket.h> + +CSession::CSession() { + m_sockfd = std::make_shared<CFileDescriptor>(accept(g_pServer->getSocket()->get(), reinterpret_cast<sockaddr *>(&m_addr), &m_addrLen)); // if this is in the init list, it will run before m_addrLen, so it won't work :/ + + if (!m_sockfd->isValid()) + throw std::runtime_error("session: Failed to create socket"); + + m_ip.resize(INET_ADDRSTRLEN); + inet_ntop(AF_INET, &m_addr.sin_addr, &m_ip[0], INET_ADDRSTRLEN); + m_ip.resize(strlen(m_ip.c_str())); + m_port = ntohs(m_addr.sin_port); + m_isAdmin = std::ranges::any_of(m_adminIps, [this](const char *ip) { return m_ip == ip; }); + + log(LOG, "Client {} connected on port {}", m_ip, m_port); + + log(TRACE, "session: initialized"); +} + +CSession::~CSession() { +#ifdef DEBUG + onDisconnect(); +#endif + m_sockfd.reset(); + + log(LOG, "Client {}@{} disconnected", m_name, m_ip); + + log(TRACE, "session({}@{}): bye", m_name, m_ip); +} + +#ifdef DEBUG +void CSession::onConnect() { + log(TRACE, "Client {}@{} connected", m_name, m_ip); +} + +void CSession::onDisconnect() { + log(TRACE, "Client {}@{} disconnected", m_name, m_ip); +} +#endif + +void CSession::onErrno(eEventType eventType) { + char errbuf[256]; + const char *err = strerror_r(errno, errbuf, sizeof(errbuf)); + + switch (eventType) { + case READ: + log(WARN, "Error while reading: {}", err); + break; + case WRITE: + log(WARN, "Error while writing: {}", err); + break; + } +} + +#ifdef DEBUG +void CSession::onRecv(const SRecvData &data) { + log(TRACE, "Received: {}", data.data); +} + +void CSession::onSend(const std::string &msg) { + log(LOG, "Sent: {}", msg); +} +#endif + +void CSession::recvLoop() { + while (true) { + auto recvData = read(NFormatter::fmt(NONEWLINE, "{}: ", m_name)); + if (!recvData->good) + break; + + recvData->sanitize(); + if (recvData->isEmpty()) { + log(TRACE, "Empty message from {}", m_name); + continue; + } + + const auto isCommand = g_pCommandHandler->isCommand(recvData->data); + if (isCommand) + g_pCommandHandler->handleCommand(recvData->data, self.lock()); + + g_pChatManager->newMessage({.msg = recvData->data, .username = m_name, .sender = self, .admin = isCommand}); + } +} + +bool CSession::SRecvData::isEmpty() const { + return data.empty() || std::ranges::all_of(data, [](char c) { return std::isspace(c); }); +} + +void CSession::SRecvData::sanitize() { + if (!data.empty() && data.back() == '\n') + data.pop_back(); + + data.erase(std::remove(data.begin(), data.end(), asciiEscape), data.end()); // don't accept ASCII code, might mess up terminal // NOLINT + + if (const auto start = data.find_first_not_of(" \t\r\n"); start != std::string::npos) { + const auto end = data.find_last_not_of(" \t\r\n"); + data = data.substr(start, end - start + 1); + } else + data.clear(); +} + +std::unique_ptr<CSession::SRecvData> CSession::read() { + auto recvData = std::make_unique<SRecvData>(); + + recvData->data.resize(recvData->size); + ssize_t size = recv(m_sockfd->get(), &recvData->data[0], recvData->size, 0); + + if (size < 0) { + recvData->good = false; + onErrno(READ); + } else if (size == 0) + recvData->good = false; + else { + recvData->data.resize(size); + recvData->good = true; + } + +#ifdef DEBUG + onRecv(*recvData); +#endif + m_szReading.reset(); + return recvData; +} + +std::unique_ptr<CSession::SRecvData> CSession::read(const std::string &msg, bool bypassDeaf) { + if (bypassDeaf && m_bDeaf) + m_bDeaf = false; + write(NONEWLINE, "{}", msg); + if (bypassDeaf && !m_bDeaf) + m_bDeaf = true; + m_szReading = msg; + return read(); +} + +void CSession::run() { +#ifdef DEBUG + onConnect(); +#endif + if (registerSession()) { + for (const auto &chat : g_pChatManager->getChat()) + writeChat(chat); + recvLoop(); + } else + log(LOG, "Client {} exited without even registering", m_ip); + + g_pSessionManager->kick(self); +} + +bool CSession::isMuted() const { + return m_bMuted; +} + +void CSession::setMuted(bool mute) { + m_bMuted = mute; +} + +bool CSession::isDeaf() const { + return m_bDeaf; +} + +void CSession::setDeaf(bool deaf) { + m_bDeaf = deaf; +} + +bool CSession::registerSession() { + auto recvData = read("Name: ", true); + if (!recvData->good) + return false; + + recvData->sanitize(); + if (recvData->isEmpty()) { + m_bDeaf = false; + write("Name cannot be empty"); + m_bDeaf = true; + return registerSession(); + } + + if (g_pSessionManager->nameExists(recvData->data)) { + m_bDeaf = false; + write("Name already exists"); + m_bDeaf = true; + return registerSession(); + } + + this->m_name = recvData->data; + + log(LOG, "Client {} registered as: {}", m_ip, m_name); + + g_pSessionManager->broadcast(NFormatter::fmt(NONEWLINE, "{} has joined the chat", m_name), self); + + m_bDeaf = false; + return true; +} + +bool CSession::isValid() { + if (!m_sockfd->isValid() || m_name.empty()) + return false; + + int err = 0; + socklen_t size = sizeof(err); + return getsockopt(m_sockfd->get(), SOL_SOCKET, SO_ERROR, &err, &size) >= 0 && err == 0; +} + +bool CSession::write(const std::string &msg) { + return write("{}", msg); +} + +bool CSession::writeChat(const CChatManager::SMessage &msg) { + const auto sender = msg.sender ? msg.sender->lock() : nullptr; + if (sender ? sender != self.lock() : msg.username != m_name) { + if (msg.admin && !m_isAdmin) + return false; + + return write(g_pChatManager->fmtBroadcastMessage(msg)); + } + return false; +} + +const std::string &CSession::getName() const { + return m_name; +} + +const std::string &CSession::getIp() const { + return m_ip; +} + +bool CSession::isAdmin() const { + return m_isAdmin; +} + +template <typename... Args> +bool CSession::write(std::format_string<Args...> fmt, Args &&...args) { + if (m_bDeaf) + return false; + return write(NONE, fmt, std::forward<Args>(args)...); +} + +template <typename... Args> +bool CSession::write(eFormatType type, std::format_string<Args...> fmt, Args &&...args) { + if (m_bDeaf) + return false; + + std::string msg = NFormatter::fmt(type, fmt, std::forward<Args>(args)...); + if (m_szReading) { + msg.insert(0, "\r"); + msg.append(*m_szReading); + } + + if (send(m_sockfd->get(), msg.c_str(), msg.size(), 0) < 0) { + onErrno(WRITE); + return false; + } +#ifdef DEBUG + onSend(msg); +#endif + return true; +} + +template bool CSession::write<const std::string &>(std::format_string<const std::string &>, const std::string &); +template bool CSession::write<const std::string &>(eFormatType, std::format_string<const std::string &>, const std::string &); diff --git a/src/core/session.hpp b/src/core/session.hpp new file mode 100644 index 0000000..ca951d1 --- /dev/null +++ b/src/core/session.hpp @@ -0,0 +1,88 @@ +#pragma once + +#include "../misc/FileDescriptor.hpp" +#include "chatManager.hpp" +#include "../debug/format.hpp" +#include <format> +#include <memory> +#include <netinet/in.h> +#include <optional> +#include <string> +#include <sys/socket.h> +#include <thread> + +class CSession { + public: + CSession(); + ~CSession(); + + struct SRecvData { + std::string data; + const size_t size = 1024; + bool good = false; + + bool isEmpty() const; + void sanitize(); + + static constexpr char asciiEscape = 0x1B; // + }; + + const std::string &getName() const; + const std::string &getIp() const; + bool isAdmin() const; + + std::unique_ptr<SRecvData> read(); + std::unique_ptr<SRecvData> read(const std::string &msg, bool bypassDeaf = false); + bool write(const std::string &msg); + template <typename... Args> + bool write(std::format_string<Args...> fmt, Args &&...args); + template <typename... Args> + bool write(eFormatType type, std::format_string<Args...> fmt, Args &&...args); + bool writeChat(const CChatManager::SMessage &msg); + + bool isMuted() const; + void setMuted(bool mute); + bool isDeaf() const; + void setDeaf(bool deaf); + + void run(); + + bool isValid(); + + private: + constexpr static char m_adminIps[1][INET_ADDRSTRLEN] = {"127.0.0.1"}; + + enum eEventType : std::uint8_t { + READ, + WRITE + }; + + std::weak_ptr<CSession> self; + std::shared_ptr<CFileDescriptor> m_sockfd; + sockaddr_in m_addr; + socklen_t m_addrLen = sizeof(m_addr); + std::string m_name; + std::string m_ip; + int m_port; + bool m_isAdmin = false; + + std::optional<std::string> m_szReading = std::nullopt; + bool m_bMuted = false; + bool m_bDeaf = true; // init as true as we don't want anything during registerSession + + void recvLoop(); + bool registerSession(); + +#ifdef DEBUG + void onConnect(); + void onDisconnect(); +#endif + void onErrno(eEventType); +#ifdef DEBUG + void onRecv(const SRecvData &data); + void onSend(const std::string &msg); +#endif + + friend class CSessionManager; + friend void registerCommands(); +}; diff --git a/src/core/sessionManager.cpp b/src/core/sessionManager.cpp new file mode 100644 index 0000000..6c002a5 --- /dev/null +++ b/src/core/sessionManager.cpp @@ -0,0 +1,141 @@ +#include "sessionManager.hpp" +#include "../io/IOManager.hpp" +#include "chatServer.hpp" +#include "../debug/format.hpp" +#include "../debug/log.hpp" +#include "session.hpp" +#include <algorithm> +#include <ranges> +#include <unistd.h> +#include <utility> + +void CSessionManager::shutdownSessions() { + if (m_vSessions.empty()) + return; + + for (auto &[thread, session] : m_vSessions) { + if (session) { + session->setDeaf(false); + session->write("Server shutting down"); + if (!session->getName().empty()) + g_pSessionManager->broadcast(NFormatter::fmt(NONEWLINE, "{} has left the chat", session->getName()), std::weak_ptr<CSession>(session)); + } + } + + for (auto &[thread, session] : m_vSessions) { + if (thread.joinable()) + thread.detach(); + session.reset(); + } + + m_vSessions.clear(); +} + +CSessionManager::CSessionManager() { + if (std::atexit([]() { + if (g_pSessionManager) + g_pSessionManager->shutdownSessions(); + })) + log(ERR, "Failed to register SessionManager atexit handler, will fallback to quick_exit after hang"); + + log(LOG, "SessionManager: initialized"); +}; + +CSessionManager::~CSessionManager() { + for (auto &[thread, session] : m_vSessions) { + if (thread.joinable()) + thread.detach(); + kick(std::weak_ptr<CSession>(session)); + } + + log(SYS, "SessionManager: bye"); +} + +void CSessionManager::newSession() { + std::shared_ptr session = std::make_shared<CSession>(); + auto &instance = m_vSessions.emplace_back(std::jthread([session]() { session->run(); }), session); + instance.second->self = std::weak_ptr<CSession>(instance.second); +} + +void CSessionManager::run() { + while (true) + newSession(); +} + +void CSessionManager::broadcast(const std::string &msg, std::optional<std::weak_ptr<CSession>> self) const { + for (const auto &[thread, session] : m_vSessions) + if (!self || (self && session != self->lock())) + session->write(msg); + + g_pIOManager->addCustom({"", msg}); +} + +void CSessionManager::broadcast(const CChatManager::SMessage &msg) const { + for (const auto &[thread, session] : m_vSessions) { + if (!session) + continue; + + session->writeChat(msg); + } + + g_pIOManager->addMessage(msg); +} + +bool CSessionManager::nameExists(const std::string &name) { + auto it = std::ranges::find_if(m_vSessions, [&name](const auto &s) { return s.second->getName() == name; }); + + if (it != m_vSessions.end()) { + if (!it->second->isValid()) { + kick(std::weak_ptr<CSession>(it->second), true, "Connection lost"); + return false; + } + return true; + } + return false; +} + +std::shared_ptr<CSession> CSessionManager::getByName(const std::string &name) const { + auto it = std::ranges::find_if(m_vSessions, [&name](const auto &s) { return s.second->getName() == name; }); + return it != m_vSessions.end() ? it->second : nullptr; +} + +std::shared_ptr<CSession> CSessionManager::getByIp(const std::string &ip) const { + auto it = std::ranges::find_if(m_vSessions, [&ip](const auto &s) { log(LOG, s.second->getIp()); return s.second->getIp() == ip; }); + return it != m_vSessions.end() ? it->second : nullptr; +} + +std::vector<std::shared_ptr<CSession>> CSessionManager::getSessions() const { + return m_vSessions | std::views::transform([](const auto &s) { return s.second; }) | std::ranges::to<std::vector>(); +} + +void CSessionManager::kick(std::weak_ptr<CSession> session_weak, const bool kill, const std::string &reason) { + auto session = session_weak.lock(); + if (!session) + return; + + for (auto it = m_vSessions.begin(); it != m_vSessions.end(); ++it) { + if (it->second == session) { + if (session) + // this only exist when the session is registered + // not sure why the second is null, but m_name definately is since it's setted during register + // but we don't need to notify people if the session didn't even "join"/register anyways + g_pSessionManager->broadcast(NFormatter::fmt(NONEWLINE, "{} has left the chat", session->getName()), session_weak); + + if (!reason.empty()) { + session->setDeaf(false); + session->write(reason); + } + const auto native_handle = it->first.native_handle(); + if (it->first.joinable()) + it->first.detach(); // .detach the thread since it's removing itself + it->second.reset(); + m_vSessions.erase(it); + if (kill && native_handle != 0) + // cancel(terminal/kill) the thread if it doesn't exit on its own + // pthread_cancel cuz it's the safest https://stackoverflow.com/a/3438576 + // need a condition because it might crash if the thread is already dead + pthread_cancel(native_handle); + return; + } + } +} diff --git a/src/core/sessionManager.hpp b/src/core/sessionManager.hpp index e84226c..71bcc5f 100644 --- a/src/core/sessionManager.hpp +++ b/src/core/sessionManager.hpp @@ -1,9 +1,9 @@ #pragma once #include "../debug/format.hpp" -#include "../misc/memory.hpp" #include "chatManager.hpp" #include "session.hpp" +#include <memory> #include <thread> #include <vector> @@ -15,22 +15,22 @@ class CSessionManager { void newSession(); void run(); - void broadcast(const std::string &msg, std::optional<WP<CSession>> self = std::nullopt) const; // second param only specified when we want to exclude the sender - void kick(WP<CSession> session, const bool kill = false, const std::string &reason = ""); + void broadcast(const std::string &msg, std::optional<std::weak_ptr<CSession>> self = std::nullopt) const; // second param only specified when we want to exclude the sender + void kick(std::weak_ptr<CSession> session, const bool kill = false, const std::string &reason = ""); void addSession(const CSession &session); bool nameExists(const std::string &name); void shutdownSessions(); - SP<CSession> getByName(const std::string &name) const; - SP<CSession> getByIp(const std::string &ip) const; - std::vector<SP<CSession>> getSessions() const; + std::shared_ptr<CSession> getByName(const std::string &name) const; + std::shared_ptr<CSession> getByIp(const std::string &ip) const; + std::vector<std::shared_ptr<CSession>> getSessions() const; private: void broadcast(const CChatManager::SMessage &msg) const; - std::vector<std::pair<std::jthread, SP<CSession>>> m_vSessions; + std::vector<std::pair<std::jthread, std::shared_ptr<CSession>>> m_vSessions; friend class CChatManager; }; -inline SP<CSessionManager> g_pSessionManager; +inline std::shared_ptr<CSessionManager> g_pSessionManager; |
