summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/chatManager.cpp2
-rw-r--r--src/chatManager.hpp8
-rw-r--r--src/commandHandler.cpp35
-rw-r--r--src/commandHandler.hpp10
-rw-r--r--src/commands.cpp4
-rw-r--r--src/core/sessionManager.hpp36
-rw-r--r--src/misc/macros.hpp4
-rw-r--r--src/session.cpp13
-rw-r--r--src/session.hpp18
-rw-r--r--src/sessionManager.cpp98
-rw-r--r--src/sessionManager.hpp15
11 files changed, 149 insertions, 94 deletions
diff --git a/src/chatManager.cpp b/src/chatManager.cpp
index e5a6398..8439673 100644
--- a/src/chatManager.cpp
+++ b/src/chatManager.cpp
@@ -14,7 +14,7 @@ CChatManager::~CChatManager() {
void CChatManager::newMessage(const SMessage &msg) {
log(LOG, "{}: {}", msg.username, msg.msg);
- const auto session = msg.sender ? msg.sender->get() : g_pSessionManager->getByName(msg.username);
+ const auto session = msg.sender ? msg.sender->lock() : g_pSessionManager->getByName(msg.username);
if (session && session->isMuted()) {
session->write("You are muted");
return;
diff --git a/src/chatManager.hpp b/src/chatManager.hpp
index 227ef6c..bd305de 100644
--- a/src/chatManager.hpp
+++ b/src/chatManager.hpp
@@ -12,10 +12,10 @@ class CChatManager {
CChatManager();
~CChatManager();
struct SMessage {
- std::string msg;
- std::string username;
- std::optional<std::shared_ptr<CSession>> sender = std::nullopt;
- bool admin = false;
+ 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);
diff --git a/src/commandHandler.cpp b/src/commandHandler.cpp
index 731ea3f..e459ee0 100644
--- a/src/commandHandler.cpp
+++ b/src/commandHandler.cpp
@@ -1,8 +1,8 @@
#include "commandHandler.hpp"
#include "log.hpp"
#include "session.hpp"
-#include "sessionManager.hpp"
#include <algorithm>
+#include <memory>
CCommandHandler::CCommandHandler() {
log(LOG, "CommandHandler: initialized");
@@ -18,8 +18,8 @@ bool CCommandHandler::isCommand(const std::string &str) const {
};
bool CCommandHandler::validCommand(const std::string &command) const {
- return std::ranges::any_of(m_vCommands, [&command](const SCommand &cmd) {
- return cmd.name == command;
+ return std::ranges::any_of(m_vCommands, [&command](const std::shared_ptr<SCommand> &cmd) {
+ return cmd->name == command;
});
}
@@ -27,24 +27,24 @@ bool CCommandHandler::registerCommand(const SCommand &command) {
if (command.name.empty() || !command.exe || !command.parser)
return false;
- m_vCommands.push_back(command);
+ m_vCommands.push_back(std::make_shared<SCommand>(command));
log(TRACE, "CommandHandler: registered command: {}", command.name);
return true;
}
-const CCommandHandler::SCommand *CCommandHandler::getCommand(const std::string &command) const {
- auto it = std::ranges::find_if(m_vCommands, [&command](const SCommand &cmd) { return cmd.name == command; });
+std::shared_ptr<CCommandHandler::SCommand> CCommandHandler::getCommand(const std::string &command) const {
+ auto it = std::ranges::find_if(m_vCommands, [&command](const std::shared_ptr<SCommand> &cmd) { return cmd->name == command; });
if (it != m_vCommands.end())
- return &*it;
+ return *it;
return nullptr;
}
-CCommandHandler::SResult CCommandHandler::exeCommand(const SCommand &command, const std::string &args) const {
- const auto [parsed, good] = command.parser(args);
+CCommandHandler::SResult CCommandHandler::exeCommand(const std::shared_ptr<SCommand> command, const std::string &args) const {
+ const auto [parsed, good] = command->parser(args);
if (!good)
try {
return {.result = std::any_cast<std::string>(parsed), .good = false};
@@ -52,22 +52,25 @@ CCommandHandler::SResult CCommandHandler::exeCommand(const SCommand &command, co
return {.result = "", .good = false};
}
- log(TRACE, "CommandHandler: executing command: {}", command.name);
- return command.exe(parsed);
+ log(TRACE, "CommandHandler: executing command: {}", command->name);
+ return command->exe(parsed);
}
-CCommandHandler::SResult CCommandHandler::newCommand(const std::string &command, const std::string &args, CSession *const user) const {
+CCommandHandler::SResult CCommandHandler::newCommand(const std::string &command, const std::string &args, std::shared_ptr<CSession> session) const {
if (!validCommand(command))
return {.result = "Invalid command", .good = false};
- const auto cmd = getCommand(command);
- if (cmd->requireAdmin && !user->isAdmin())
+ auto cmd = getCommand(command);
+ if (!cmd)
+ return {.result = "Command not found", .good = false};
+
+ if (cmd->requireAdmin && !session->isAdmin())
return {.result = "Permission denied", .good = false};
- return exeCommand(*cmd, args);
+ return exeCommand(cmd, args);
}
-void CCommandHandler::handleCommand(std::string input, CSession *const user) const {
+void CCommandHandler::handleCommand(std::string input, std::shared_ptr<CSession> user) const {
input = input.substr(1);
std::string command;
diff --git a/src/commandHandler.hpp b/src/commandHandler.hpp
index bce93d2..89d19d6 100644
--- a/src/commandHandler.hpp
+++ b/src/commandHandler.hpp
@@ -34,8 +34,8 @@ class CCommandHandler {
bool validCommand(const std::string &command) const;
// return false if command struct is invalid
bool registerCommand(const SCommand &command);
- SResult newCommand(const std::string &command, const std::string &args, CSession *const user) const;
- void handleCommand(std::string input, CSession *const user = nullptr) const;
+ SResult newCommand(const std::string &command, const std::string &args, std::shared_ptr<CSession> session) const;
+ void handleCommand(std::string input, std::shared_ptr<CSession> user = nullptr) const;
template <typename T>
struct SMakeCommandArgs {
@@ -63,9 +63,9 @@ class CCommandHandler {
}
private:
- const SCommand *getCommand(const std::string &command) const;
- SResult exeCommand(const SCommand &command, const std::string &args) const;
+ std::shared_ptr<CCommandHandler::SCommand> getCommand(const std::string &command) const;
+ SResult exeCommand(const std::shared_ptr<SCommand> command, const std::string &args) const;
- std::vector<SCommand> m_vCommands;
+ std::vector<std::shared_ptr<SCommand>> m_vCommands;
};
inline std::unique_ptr<CCommandHandler> g_pCommandHandler;
diff --git a/src/commands.cpp b/src/commands.cpp
index ff20b70..e7894db 100644
--- a/src/commands.cpp
+++ b/src/commands.cpp
@@ -41,8 +41,8 @@ void registerCommands() {
if (!session)
return {.result = "User '" + data.target + "' not found", .good = false};
- const auto reason = data.reason ? *data.reason : NOREASON;
- g_pSessionManager->kick(session, true, reason);
+ const auto reason = data.reason ? *data.reason : NOREASON;
+ g_pSessionManager->kick(session->self, true, reason);
return {.result = "Kicked '" + data.target + "': " + reason, .good = true}; }}));
g_pCommandHandler->registerCommand({.requireAdmin = false, .name = "list", .parser = [](const std::string &args) -> CCommandHandler::SParseResult {
diff --git a/src/core/sessionManager.hpp b/src/core/sessionManager.hpp
new file mode 100644
index 0000000..e84226c
--- /dev/null
+++ b/src/core/sessionManager.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "../debug/format.hpp"
+#include "../misc/memory.hpp"
+#include "chatManager.hpp"
+#include "session.hpp"
+#include <thread>
+#include <vector>
+
+class CSessionManager {
+ public:
+ CSessionManager();
+ ~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 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;
+
+ private:
+ void broadcast(const CChatManager::SMessage &msg) const;
+
+ std::vector<std::pair<std::jthread, SP<CSession>>> m_vSessions;
+
+ friend class CChatManager;
+};
+inline SP<CSessionManager> g_pSessionManager;
diff --git a/src/misc/macros.hpp b/src/misc/macros.hpp
new file mode 100644
index 0000000..02f5048
--- /dev/null
+++ b/src/misc/macros.hpp
@@ -0,0 +1,4 @@
+#pragma once
+
+#define UNLIKELY(expr) (expr) [[unlikely]]
+#define LIKELY(expr) (expr) [[likely]]
diff --git a/src/session.cpp b/src/session.cpp
index 84f1824..126d547 100644
--- a/src/session.cpp
+++ b/src/session.cpp
@@ -89,9 +89,9 @@ void CSession::recvLoop() {
const auto isCommand = g_pCommandHandler->isCommand(recvData->data);
if (isCommand)
- g_pCommandHandler->handleCommand(recvData->data, this);
+ g_pCommandHandler->handleCommand(recvData->data, self.lock());
- g_pChatManager->newMessage({.msg = recvData->data, .username = m_name, .sender = self->second, .admin = isCommand});
+ g_pChatManager->newMessage({.msg = recvData->data, .username = m_name, .sender = self, .admin = isCommand});
}
}
@@ -199,7 +199,7 @@ bool CSession::registerSession() {
log(LOG, "Client {} registered as: {}", m_ip, m_name);
- g_pSessionManager->broadcast(NFormatter::fmt(NONEWLINE, "{} has joined the chat", m_name), this);
+ g_pSessionManager->broadcast(NFormatter::fmt(NONEWLINE, "{} has joined the chat", m_name), self);
m_bDeaf = false;
return true;
@@ -219,7 +219,8 @@ bool CSession::write(const std::string &msg) {
}
bool CSession::writeChat(const CChatManager::SMessage &msg) {
- if (msg.sender ? this != msg.sender->get() : msg.username != m_name) {
+ 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;
@@ -240,10 +241,6 @@ bool CSession::isAdmin() const {
return m_isAdmin;
}
-void CSession::setSelf(std::pair<std::jthread, std::shared_ptr<CSession>> *self) {
- this->self = self;
-}
-
template <typename... Args>
bool CSession::write(std::format_string<Args...> fmt, Args &&...args) {
if (m_bDeaf)
diff --git a/src/session.hpp b/src/session.hpp
index 3ec04a3..2ca274e 100644
--- a/src/session.hpp
+++ b/src/session.hpp
@@ -30,7 +30,6 @@ class CSession {
const std::string &getName() const;
const std::string &getIp() const;
bool isAdmin() const;
- void setSelf(std::pair<std::jthread, std::shared_ptr<CSession>> *self);
std::unique_ptr<SRecvData> read();
std::unique_ptr<SRecvData> read(const std::string &msg, bool bypassDeaf = false);
@@ -58,14 +57,14 @@ class CSession {
WRITE
};
- std::pair<std::jthread, std::shared_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::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;
@@ -85,4 +84,5 @@ class CSession {
#endif
friend class CSessionManager;
+ friend void registerCommands();
};
diff --git a/src/sessionManager.cpp b/src/sessionManager.cpp
index de55480..7ed1a8e 100644
--- a/src/sessionManager.cpp
+++ b/src/sessionManager.cpp
@@ -13,8 +13,22 @@ void CSessionManager::shutdownSessions() {
if (m_vSessions.empty())
return;
- for (auto &session : m_vSessions)
- kick(&session, false, "Server shutting down");
+ 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() {
@@ -31,17 +45,16 @@ CSessionManager::~CSessionManager() {
for (auto &[thread, session] : m_vSessions) {
if (thread.joinable())
thread.detach();
- kick(session.get());
+ kick(std::weak_ptr<CSession>(session));
}
log(SYS, "SessionManager: bye");
}
-std::pair<std::jthread, std::shared_ptr<CSession>> *CSessionManager::newSession() {
+void CSessionManager::newSession() {
std::shared_ptr session = std::make_shared<CSession>();
- const auto instance = &m_vSessions.emplace_back(std::jthread(&CSession::run, session.get()), session);
- instance->second->setSelf(instance);
- return instance;
+ auto &instance = m_vSessions.emplace_back(std::jthread([session]() { session->run(); }), session);
+ instance.second->self = std::weak_ptr<CSession>(instance.second);
}
void CSessionManager::run() {
@@ -49,9 +62,9 @@ void CSessionManager::run() {
newSession();
}
-void CSessionManager::broadcast(const std::string &msg, std::optional<const CSession *const> self) const {
+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.get() != *self))
+ if (!self || (self && session != self->lock()))
session->write(msg);
g_pIOManager->addCustom({"", msg});
@@ -73,7 +86,7 @@ bool CSessionManager::nameExists(const std::string &name) {
if (it != m_vSessions.end()) {
if (!it->second->isValid()) {
- kick(it->second.get(), true, "Connection lost");
+ kick(std::weak_ptr<CSession>(it->second), true, "Connection lost");
return false;
}
return true;
@@ -81,49 +94,48 @@ bool CSessionManager::nameExists(const std::string &name) {
return false;
}
-CSession *CSessionManager::getByName(const std::string &name) const {
+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.get() : nullptr;
+ return it != m_vSessions.end() ? it->second : nullptr;
}
-CSession *CSessionManager::getByIp(const std::string &ip) const {
- auto it = std::ranges::find_if(m_vSessions, [&ip](const auto &s) { log(LOG, s.second->m_ip); return s.second->m_ip == ip; });
- return it != m_vSessions.end() ? it->second.get() : 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(CSession *session, const bool kill, const std::string &reason) {
- for (auto &_session : m_vSessions)
- if (_session.second.get() == session)
- kick(&_session, kill, reason);
-}
-
-void CSessionManager::kick(std::pair<std::jthread, std::shared_ptr<CSession>> *session, const bool kill, const std::string &reason) {
- auto it = std::ranges::find_if(m_vSessions, [session](const auto &s) { return s.second.get() == session->second.get(); });
-
- if (session->second)
- // 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->second->m_name), session->second.get());
+void CSessionManager::kick(std::weak_ptr<CSession> session_weak, const bool kill, const std::string &reason) {
+ auto session = session_weak.lock();
+ if (!session)
+ return;
- if (it != m_vSessions.end()) {
- if (!reason.empty()) {
- it->second->setDeaf(false);
- it->second->write(reason);
+ 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;
}
- 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);
}
}
diff --git a/src/sessionManager.hpp b/src/sessionManager.hpp
index eb3705e..179d7d8 100644
--- a/src/sessionManager.hpp
+++ b/src/sessionManager.hpp
@@ -12,21 +12,24 @@ class CSessionManager {
CSessionManager();
~CSessionManager();
- std::pair<std::jthread, std::shared_ptr<CSession>> *newSession();
+ void newSession();
void run();
- void broadcast(const std::string &msg, std::optional<const CSession *const> self = std::nullopt) const; // second param only specified when we want to exclude the sender
- void kick(CSession *session, const bool kill = false, const std::string &reason = "");
- void kick(std::pair<std::jthread, std::shared_ptr<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();
- CSession *getByName(const std::string &name) const;
- CSession *getByIp(const std::string &ip) 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;
+ // Safer weak_ptr versions - recommended for most use cases to avoid accidental lifetime extension
+ std::weak_ptr<CSession> getByNameWeak(const std::string &name) const;
+ std::weak_ptr<CSession> getByIpWeak(const std::string &ip) const;
+
private:
void broadcast(const CChatManager::SMessage &msg) const;