Refactor chat system: Introduce ChatRoom and ChatUser classes
- Created ChatRoom class to manage chat room functionalities, including user management, message handling, and game mechanics. - Developed ChatUser class to represent individual users, handling user-specific actions and interactions within chat rooms. - Implemented a Config class for loading configuration settings from a JSON file. - Established a Server class to manage connections, handle requests, and facilitate communication between users and chat rooms. - Introduced a Database class for database interactions, utilizing PostgreSQL for user and room data management. - Added utility functions in the Base class for JSON handling and socket communication. - Created Object classes for Room and User to encapsulate their properties and behaviors. - Updated main function to initialize server and load chat rooms from configuration.
This commit is contained in:
201
src/core/server.cpp
Executable file
201
src/core/server.cpp
Executable file
@@ -0,0 +1,201 @@
|
||||
#include "server.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <thread>
|
||||
#include <future>
|
||||
#include <functional>
|
||||
#include <unistd.h>
|
||||
#include <json/json.h>
|
||||
#include <iostream>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sstream>
|
||||
|
||||
namespace Yc {
|
||||
namespace Lib {
|
||||
|
||||
Server::Server(std::shared_ptr<Yc::Lib::Config> config, std::shared_ptr<Yc::Lib::Database> database) :
|
||||
_config(std::move(config)),
|
||||
_database(std::move(database)),
|
||||
_stop(false) {
|
||||
struct sockaddr_in serverAddr;
|
||||
int opt = true;
|
||||
_socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt));
|
||||
int flags = 1;
|
||||
setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));
|
||||
serverAddr.sin_family = AF_INET;
|
||||
serverAddr.sin_addr.s_addr = INADDR_ANY;
|
||||
serverAddr.sin_port = htons(1235);
|
||||
if (bind(_socket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) {
|
||||
std::cout << "bind not possible" << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void Server::run() {
|
||||
if (listen(_socket, 5) < 0) {
|
||||
std::cout << "listen not possible" << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
timeval origTv;
|
||||
origTv.tv_sec = 5;
|
||||
origTv.tv_usec = 0;
|
||||
int _maxSd = _socket;
|
||||
while (!_stop) {
|
||||
timeval tv(origTv);
|
||||
fd_set fd;
|
||||
FD_ZERO(&fd);
|
||||
FD_SET(_socket, &fd);
|
||||
if (select(_maxSd + 1, &fd, NULL, NULL, &tv) > 0) {
|
||||
std::thread(&Server::handleRequest, this).detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> Server::roomList() {
|
||||
std::vector<std::string> list;
|
||||
for (const auto &room: _rooms) {
|
||||
list.push_back(room->name());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
Json::Value Server::jsonRoomList() {
|
||||
Json::Value list;
|
||||
for (auto &room: _rooms) {
|
||||
Json::Value roomJson = Json::objectValue;
|
||||
roomJson["name"] = room->name();
|
||||
roomJson["flags"] = room->flags();
|
||||
list.append(roomJson);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
bool Server::roomAllowed(std::string roomName, std::string userName, std::string password){
|
||||
for (auto &room: _rooms) {
|
||||
if (room->name() == roomName && room->accessAllowed(userName, password)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Server::changeRoom(std::shared_ptr<ChatUser> user, std::string newRoom, std::string password) {
|
||||
if (!roomAllowed(newRoom, user->name(), password)) {
|
||||
return false;
|
||||
}
|
||||
Json::Value userMsg = Json::objectValue;
|
||||
userMsg["tr"] = "room_change_user";
|
||||
userMsg["to"] = newRoom;
|
||||
for (auto &room: _rooms) {
|
||||
if (room->userIsInRoom(user->name())) {
|
||||
room->removeUser(user);
|
||||
Json::Value msg = Json::objectValue;
|
||||
msg["tr"] = "room_change_to";
|
||||
msg["to"] = newRoom;
|
||||
userMsg["from"] = room->name();
|
||||
room->addMessage(ChatUser::system, msg, user->name(), user->color());
|
||||
}
|
||||
}
|
||||
user->sendMsg(ChatUser::system, userMsg, "", "");
|
||||
for (auto &room: _rooms) {
|
||||
if (room->name() == newRoom) {
|
||||
Json::Value msg = Json::objectValue;
|
||||
msg["tr"] = "room_change_to";
|
||||
msg["from"] = userMsg["from"];
|
||||
room->addMessage(ChatUser::system, msg, user->name(), user->color());
|
||||
room->addUserWhenQueueEmpty(user);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Server::createRooms(Json::Value roomList) {
|
||||
// Ignoriere roomList, lade stattdessen aus der Datenbank
|
||||
auto self = shared_from_this();
|
||||
std::string query = R"(
|
||||
SELECT r.id, r.title, r.password_hash, r.room_type_id, r.is_public, r.owner_id, r.min_age, r.max_age, r.created_at, r.updated_at, rt.tr as room_type
|
||||
FROM chat.room r
|
||||
LEFT JOIN chat.room_type rt ON r.room_type_id = rt.id
|
||||
)";
|
||||
auto result = _database->exec(query);
|
||||
for (const auto& row : result) {
|
||||
Json::Value room;
|
||||
room["id"] = row["id"].as<int>();
|
||||
room["name"] = row["title"].c_str();
|
||||
room["password"] = row["password_hash"].is_null() ? "" : row["password_hash"].c_str();
|
||||
room["type"] = row["room_type_id"].is_null() ? 0 : row["room_type_id"].as<int>();
|
||||
room["is_public"] = row["is_public"].as<bool>();
|
||||
room["owner_id"] = row["owner_id"].is_null() ? 0 : row["owner_id"].as<int>();
|
||||
room["min_age"] = row["min_age"].is_null() ? 0 : row["min_age"].as<int>();
|
||||
room["max_age"] = row["max_age"].is_null() ? 0 : row["max_age"].as<int>();
|
||||
room["created_at"] = row["created_at"].c_str();
|
||||
room["updated_at"] = row["updated_at"].c_str();
|
||||
room["room_type"] = row["room_type"].is_null() ? "" : row["room_type"].c_str();
|
||||
// Platzhalter für Felder, die im Konstruktor benötigt werden
|
||||
room["allowed"] = Json::arrayValue; // ggf. später befüllen
|
||||
room["roundlength"] = 60; // Default-Wert
|
||||
auto newRoom = std::make_shared<ChatRoom>(self, room);
|
||||
_rooms.push_back(newRoom);
|
||||
}
|
||||
}
|
||||
|
||||
void Server::handleRequest() {
|
||||
struct sockaddr_in sockAddr;
|
||||
socklen_t sockAddrLen = sizeof(sockAddr);
|
||||
int userSock = accept(_socket, (struct sockaddr *)&sockAddr, &sockAddrLen);
|
||||
if (userSock < 0) {
|
||||
return;
|
||||
}
|
||||
int flags = 1;
|
||||
setsockopt(userSock, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));
|
||||
std::string msg = readSocket(userSock);
|
||||
if (msg == "") {
|
||||
return;
|
||||
}
|
||||
inputSwitcher(userSock, msg);
|
||||
}
|
||||
|
||||
void Server::inputSwitcher(int userSocket, std::string input) {
|
||||
Json::Value inputTree = getJsonTree(input);
|
||||
if (inputTree["type"] == "init") {
|
||||
initUser(userSocket, inputTree);
|
||||
}
|
||||
}
|
||||
|
||||
bool Server::userExists(std::string userName) {
|
||||
for (const auto &room: _rooms) {
|
||||
if (room->userNameExists(userName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Server::initUser(int userSocket, Json::Value data) {
|
||||
if (userExists(data["name"].asString())) {
|
||||
Json::Value errorJson;
|
||||
errorJson["type"] = ChatUser::error;
|
||||
errorJson["message"] = "loggedin";
|
||||
send(userSocket, errorJson);
|
||||
close(userSocket);
|
||||
return;
|
||||
}
|
||||
std::string roomName = data["room"].asString();
|
||||
bool added(false);
|
||||
for (auto &room: _rooms) {
|
||||
if (room->name() == roomName) {
|
||||
if (room->addUser(data["name"].asString(), data["color"].asString(), data["password"].asString(), userSocket)) {
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
close(userSocket);
|
||||
}
|
||||
}
|
||||
} // namespace Lib
|
||||
} // namespace Yp
|
||||
Reference in New Issue
Block a user