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:
297
src/core/chat_room.cpp
Executable file
297
src/core/chat_room.cpp
Executable file
@@ -0,0 +1,297 @@
|
||||
#include "chat_room.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include "server.h"
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
#include <time.h>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <thread>
|
||||
|
||||
namespace Yc
|
||||
{
|
||||
namespace Lib
|
||||
{
|
||||
|
||||
ChatRoom::ChatRoom(std::shared_ptr<Server> parent, Json::Value roomParams)
|
||||
: _room(roomParams),
|
||||
_parent(std::move(parent)),
|
||||
_blocked(false),
|
||||
_stop(false),
|
||||
_roundRunning(false)
|
||||
{
|
||||
_name = roomParams["name"].asString();
|
||||
_password = roomParams["password"].asString();
|
||||
for (auto &user : roomParams["allowed"])
|
||||
{
|
||||
_allowedUsers.push_back(user.asString());
|
||||
}
|
||||
_type = (RoomType)roomParams["type"].asInt();
|
||||
_roundLength = roomParams["roundlength"].asInt();
|
||||
_lastRoundEnd = std::time(NULL);
|
||||
thread = std::make_unique<std::thread>(&ChatRoom::run, this);
|
||||
}
|
||||
|
||||
ChatRoom::~ChatRoom() = default;
|
||||
|
||||
void ChatRoom::run()
|
||||
{
|
||||
while (!_stop)
|
||||
{
|
||||
if (_msgQueue.size() > 0 && !_blocked)
|
||||
{
|
||||
_blocked = true;
|
||||
while (_msgQueue.size() > 0)
|
||||
{
|
||||
Message message = _msgQueue.front();
|
||||
for (auto &user : _users)
|
||||
{
|
||||
user->sendMsg(message.type, message.messageTr, message.userName, message.color);
|
||||
}
|
||||
_msgQueue.pop();
|
||||
}
|
||||
_blocked = false;
|
||||
}
|
||||
if ((_type & dice) == dice)
|
||||
{
|
||||
_handleDice();
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
}
|
||||
|
||||
bool ChatRoom::addUser(std::string _userName, std::string color, std::string _password, int socket)
|
||||
{
|
||||
if (_password != "" && _password == _password && std::find(std::begin(_allowedUsers), std::end(_allowedUsers), _userName) == std::end(_allowedUsers))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto newUser = std::make_shared<ChatUser>(shared_from_this(), _userName, color, socket);
|
||||
_users.push_back(newUser);
|
||||
newUser->sendMsg(ChatUser::roomList, _parent->jsonRoomList(), "", "");
|
||||
addMessage(ChatUser::system, "room_entered", newUser->name(), newUser->color());
|
||||
_initRound();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChatRoom::addUser(std::shared_ptr<ChatUser> user, std::string password)
|
||||
{
|
||||
if (password == _password)
|
||||
{
|
||||
_users.push_back(user);
|
||||
user->setParent(shared_from_this());
|
||||
_initRound();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ChatRoom::userNameExists(std::string userName)
|
||||
{
|
||||
for (const auto &user : _users)
|
||||
{
|
||||
if (user->name() == userName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ChatRoom::removeUser(std::string _token, bool silent)
|
||||
{
|
||||
for (auto it = _users.begin(); it != _users.end(); ++it)
|
||||
{
|
||||
if ((*it)->validateToken(_token))
|
||||
{
|
||||
if (!silent)
|
||||
{
|
||||
addMessage(ChatUser::system, (*it)->name(), (*it)->color());
|
||||
}
|
||||
_users.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChatRoom::removeUser(std::shared_ptr<ChatUser> userToRemove, bool silent)
|
||||
{
|
||||
for (auto it = _users.begin(); it != _users.end(); ++it)
|
||||
{
|
||||
if (*it == userToRemove)
|
||||
{
|
||||
if (!silent)
|
||||
{
|
||||
addMessage(ChatUser::system, (*it)->name(), (*it)->color());
|
||||
}
|
||||
_users.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChatRoom::setStop()
|
||||
{
|
||||
_stop = true;
|
||||
}
|
||||
|
||||
void ChatRoom::addMessage(ChatUser::MsgType type, const char *messageText, std::string userName, std::string color)
|
||||
{
|
||||
addMessage(type, (std::string)messageText, userName, color);
|
||||
}
|
||||
|
||||
void ChatRoom::addMessage(ChatUser::MsgType type, std::string messageText, std::string userName, std::string color)
|
||||
{
|
||||
Message message;
|
||||
message.type = type;
|
||||
message.messageTr = messageText;
|
||||
message.userName = userName;
|
||||
message.color = color;
|
||||
_msgQueue.push(message);
|
||||
}
|
||||
|
||||
void ChatRoom::addMessage(ChatUser::MsgType type, Json::Value messageText, std::string userName, std::string color)
|
||||
{
|
||||
addMessage(type, getJsonString(messageText), userName, color);
|
||||
}
|
||||
|
||||
void ChatRoom::addUserWhenQueueEmpty(std::shared_ptr<ChatUser> user)
|
||||
{
|
||||
while (_msgQueue.size() > 0)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
_users.push_back(user);
|
||||
user->setParent(shared_from_this());
|
||||
}
|
||||
|
||||
bool ChatRoom::userToNewRoom(std::shared_ptr<ChatUser> user, std::string newRoom, std::string password)
|
||||
{
|
||||
return _parent->changeRoom(user, newRoom, password);
|
||||
}
|
||||
|
||||
unsigned int ChatRoom::flags()
|
||||
{
|
||||
unsigned int value = (unsigned int)_type;
|
||||
if (_password != "")
|
||||
{
|
||||
value += (unsigned int)ChatRoom::password;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
Json::Value ChatRoom::userList()
|
||||
{
|
||||
Json::Value users = Json::arrayValue;
|
||||
for (auto &user : _users)
|
||||
{
|
||||
Json::Value jsonUser = Json::objectValue;
|
||||
jsonUser["name"] = user->name();
|
||||
users.append(jsonUser);
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
ChatRoom::RoomType ChatRoom::type()
|
||||
{
|
||||
return _type;
|
||||
}
|
||||
|
||||
bool ChatRoom::isType(ChatRoom::RoomType type)
|
||||
{
|
||||
return ((_type & type) == type);
|
||||
}
|
||||
|
||||
bool ChatRoom::canDice()
|
||||
{
|
||||
return (_roundRunning || (_type & rounds) == rounds) && (_type & dice) == dice;
|
||||
}
|
||||
|
||||
unsigned int ChatRoom::addDice(std::shared_ptr<ChatUser> user, int diceValue)
|
||||
{
|
||||
if (!canDice())
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
for (auto &listUser : _diceValues)
|
||||
{
|
||||
if (listUser.first == user)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
_diceValues.push_back(std::make_pair(user, diceValue));
|
||||
addMessage(ChatUser::dice, std::to_string(diceValue), user->name(), user->color());
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ChatRoom::accessAllowed(std::string userName, std::string password)
|
||||
{
|
||||
return (_allowedUsers.size() == 0 || _password == "" || _password == password || std::find(_allowedUsers.begin(), _allowedUsers.end(), userName) != _allowedUsers.end());
|
||||
}
|
||||
|
||||
bool ChatRoom::userIsInRoom(std::string userName)
|
||||
{
|
||||
for (auto &user : _users)
|
||||
{
|
||||
if (userName == user->name())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ChatRoom::_handleDice()
|
||||
{
|
||||
if (((_type & rounds) == rounds))
|
||||
{
|
||||
if (_roundRunning && (_users.size() < 2 || _roundStart + _roundLength >= time(NULL)))
|
||||
{
|
||||
_lastRoundEnd = time(NULL);
|
||||
_roundRunning = false;
|
||||
addMessage(ChatUser::system, "round_ends");
|
||||
_showDiceRoundResults();
|
||||
}
|
||||
else if (!_roundRunning && _lastRoundEnd <= time(NULL) - 15 && _users.size() >= 2)
|
||||
{
|
||||
_roundStart = time(NULL);
|
||||
_roundRunning = true;
|
||||
_diceValues.clear();
|
||||
addMessage(ChatUser::system, "next_round_starts_now");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChatRoom::_initRound()
|
||||
{
|
||||
if (_users.size() == 2)
|
||||
{
|
||||
_lastRoundEnd = time(NULL);
|
||||
addMessage(ChatUser::system, "next_round_starts_soon");
|
||||
}
|
||||
}
|
||||
|
||||
void ChatRoom::_showDiceRoundResults()
|
||||
{
|
||||
std::sort(_diceValues.begin(), _diceValues.end(), [=](const std::pair<std::shared_ptr<ChatUser>, int> &val1, const std::pair<std::shared_ptr<ChatUser>, int> &val2)
|
||||
{ return (val1.second > val2.second); });
|
||||
Json::Value userList = Json::arrayValue;
|
||||
for (auto &user : _diceValues)
|
||||
{
|
||||
Json::Value entry = Json::objectValue;
|
||||
entry["name"] = user.first->name();
|
||||
entry["value"] = user.second;
|
||||
userList.append(entry);
|
||||
}
|
||||
addMessage(ChatUser::result, userList);
|
||||
}
|
||||
|
||||
std::string ChatRoom::name()
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
} // namespace Lib
|
||||
} // namespace Yc
|
||||
96
src/core/chat_room.h
Executable file
96
src/core/chat_room.h
Executable file
@@ -0,0 +1,96 @@
|
||||
// renamed from room.h
|
||||
#ifndef YC_LIB_CHAT_ROOM_H
|
||||
#define YC_LIB_CHAT_ROOM_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <future>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/base.h"
|
||||
#include "chat_user.h"
|
||||
#include "object/room.h"
|
||||
|
||||
namespace Yc
|
||||
{
|
||||
namespace Lib
|
||||
{
|
||||
|
||||
class Server;
|
||||
|
||||
class ChatRoom : public Base, public std::enable_shared_from_this<ChatRoom>
|
||||
{
|
||||
public:
|
||||
enum RoomType
|
||||
{
|
||||
none = 0,
|
||||
dice = 1,
|
||||
poker = 2,
|
||||
rounds = 4,
|
||||
password = 8
|
||||
};
|
||||
|
||||
ChatRoom(std::shared_ptr<Server> parent, Json::Value roomParams);
|
||||
~ChatRoom();
|
||||
std::shared_ptr<Server> getServer() const { return _parent; }
|
||||
void run();
|
||||
std::string name();
|
||||
bool addUser(std::string userName, std::string color, std::string password, int socket);
|
||||
bool addUser(std::shared_ptr<ChatUser> user, std::string password);
|
||||
bool userNameExists(std::string userName);
|
||||
void removeUser(std::string _token, bool silent = false);
|
||||
void removeUser(std::shared_ptr<ChatUser> user, bool silent = false);
|
||||
void setStop();
|
||||
void addMessage(ChatUser::MsgType type, const char *messageText, std::string userName = "", std::string color = "");
|
||||
void addMessage(ChatUser::MsgType type, std::string messageText, std::string userName = "", std::string color = "");
|
||||
void addMessage(ChatUser::MsgType type, Json::Value messageText, std::string userName = "", std::string color = "");
|
||||
RoomType type();
|
||||
bool isType(RoomType type);
|
||||
bool canDice();
|
||||
unsigned int addDice(std::shared_ptr<ChatUser> user, int diceValue);
|
||||
bool accessAllowed(std::string userName, std::string password);
|
||||
bool userIsInRoom(std::string userName);
|
||||
void addUserWhenQueueEmpty(std::shared_ptr<ChatUser> user);
|
||||
bool userToNewRoom(std::shared_ptr<ChatUser> user, std::string newRoom, std::string password);
|
||||
unsigned int flags();
|
||||
Json::Value userList();
|
||||
|
||||
private:
|
||||
struct Message
|
||||
{
|
||||
ChatUser::MsgType type;
|
||||
std::string messageTr;
|
||||
std::string userName;
|
||||
std::string color;
|
||||
};
|
||||
|
||||
std::shared_ptr<Server> _parent;
|
||||
std::string _name;
|
||||
std::string _password;
|
||||
std::vector<std::string> _allowedUsers;
|
||||
RoomType _type;
|
||||
std::vector<std::shared_ptr<ChatUser>> _users;
|
||||
bool _blocked;
|
||||
bool _stop;
|
||||
std::queue<Message> _msgQueue;
|
||||
std::unique_ptr<std::thread> thread;
|
||||
bool _roundRunning;
|
||||
time_t _roundStart;
|
||||
time_t _lastRoundEnd;
|
||||
int _roundLength;
|
||||
std::vector<std::pair<std::shared_ptr<ChatUser>, int>> _diceValues;
|
||||
Yc::Object::Room _room;
|
||||
void _handleDice();
|
||||
void _startDiceRound();
|
||||
void _endDiceRound();
|
||||
void _initRound();
|
||||
void _showDiceRoundResults();
|
||||
};
|
||||
|
||||
} // namespace Lib
|
||||
} // namespace Yc
|
||||
|
||||
#endif // YC_LIB_CHAT_ROOM_H
|
||||
275
src/core/chat_user.cpp
Normal file
275
src/core/chat_user.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
// entfernt: #include "user.h"
|
||||
|
||||
#include "chat_user.h"
|
||||
#include "server.h"
|
||||
#include "lib/tools.h"
|
||||
#include <json/json.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include "chat_room.h"
|
||||
#include <iostream>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sstream>
|
||||
|
||||
namespace Yc
|
||||
{
|
||||
namespace Lib
|
||||
{
|
||||
|
||||
ChatUser::ChatUser(std::shared_ptr<ChatRoom> parent, std::string name, std::string color, int socket)
|
||||
: _parent(std::move(parent)),
|
||||
_name(name),
|
||||
_color(color),
|
||||
_socket(socket),
|
||||
_stop(false)
|
||||
{
|
||||
// Hole DB-Connection
|
||||
auto server = _parent->getServer();
|
||||
auto db = server->_database;
|
||||
// Suche Community-User
|
||||
std::string query = "SELECT * FROM community.\"user\" WHERE username = '" + name + "' LIMIT 1;";
|
||||
auto result = db->exec(query);
|
||||
Json::Value userJson;
|
||||
if (result.empty()) {
|
||||
// Kein Community-User, lege Dummy an
|
||||
userJson["display_name"] = name;
|
||||
userJson["color"] = "#000000";
|
||||
} else {
|
||||
const auto& row = result[0];
|
||||
int falukant_user_id = row["id"].as<int>();
|
||||
// Suche Chat-User
|
||||
std::string chatUserQuery = "SELECT * FROM chat.\"user\" WHERE falukant_user_id = " + std::to_string(falukant_user_id) + " LIMIT 1;";
|
||||
auto chatUserResult = db->exec(chatUserQuery);
|
||||
if (chatUserResult.empty()) {
|
||||
// Chat-User anlegen
|
||||
std::string insert = "INSERT INTO chat.\"user\" (falukant_user_id, display_name, color, show_gender, show_age, created_at, updated_at) VALUES (" +
|
||||
std::to_string(falukant_user_id) + ", '" + name + "', '#000000', true, true, NOW(), NOW()) RETURNING *;";
|
||||
auto newUser = db->exec(insert);
|
||||
if (!newUser.empty()) {
|
||||
const auto& u = newUser[0];
|
||||
userJson["id"] = u["id"].as<int>();
|
||||
userJson["falukant_user_id"] = u["falukant_user_id"].as<int>();
|
||||
userJson["display_name"] = u["display_name"].c_str();
|
||||
userJson["color"] = u["color"].c_str();
|
||||
userJson["show_gender"] = u["show_gender"].as<bool>();
|
||||
userJson["show_age"] = u["show_age"].as<bool>();
|
||||
userJson["created_at"] = u["created_at"].c_str();
|
||||
userJson["updated_at"] = u["updated_at"].c_str();
|
||||
}
|
||||
} else {
|
||||
const auto& u = chatUserResult[0];
|
||||
userJson["id"] = u["id"].as<int>();
|
||||
userJson["falukant_user_id"] = u["falukant_user_id"].as<int>();
|
||||
userJson["display_name"] = u["display_name"].c_str();
|
||||
userJson["color"] = u["color"].c_str();
|
||||
userJson["show_gender"] = u["show_gender"].as<bool>();
|
||||
userJson["show_age"] = u["show_age"].as<bool>();
|
||||
userJson["created_at"] = u["created_at"].c_str();
|
||||
userJson["updated_at"] = u["updated_at"].c_str();
|
||||
}
|
||||
// Rechte laden
|
||||
std::string rightsQuery = "SELECT r.tr FROM chat.user_rights ur JOIN chat.rights r ON ur.chat_right_id = r.id WHERE ur.chat_user_id = " + std::to_string(userJson["id"].asInt()) + ";";
|
||||
auto rightsResult = db->exec(rightsQuery);
|
||||
Json::Value rights(Json::arrayValue);
|
||||
for (const auto& r : rightsResult) {
|
||||
rights.append(r["tr"].c_str());
|
||||
}
|
||||
userJson["rights"] = rights;
|
||||
}
|
||||
_user = Yc::Object::User(userJson);
|
||||
_token = Yc::Lib::Tools::generateRandomString(32);
|
||||
sendMsg(token, _token, "", "");
|
||||
thread = std::make_unique<std::thread>(&ChatUser::checkerTask, this);
|
||||
}
|
||||
|
||||
ChatUser::~ChatUser()
|
||||
{
|
||||
_parent->addMessage(ChatUser::system, std::string("leaved_chat"), std::string(_name), std::string(_color));
|
||||
}
|
||||
|
||||
std::string ChatUser::name() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
bool ChatUser::validateToken(std::string token)
|
||||
{
|
||||
return (token == _token);
|
||||
}
|
||||
|
||||
bool ChatUser::isUser(std::shared_ptr<ChatUser> toValidate)
|
||||
{
|
||||
return (toValidate.get() == this);
|
||||
}
|
||||
|
||||
void ChatUser::sendMsg(MsgType type, const char *message, std::string userName, std::string color)
|
||||
{
|
||||
sendMsg(type, std::string(message), userName, color);
|
||||
}
|
||||
|
||||
void ChatUser::sendMsg(MsgType type, std::string message, std::string userName, std::string color)
|
||||
{
|
||||
Json::Value sendMessage;
|
||||
sendMessage["type"] = type;
|
||||
sendMessage["message"] = message;
|
||||
sendMessage["userName"] = userName;
|
||||
sendMessage["color"] = color;
|
||||
send(sendMessage);
|
||||
}
|
||||
|
||||
void ChatUser::sendMsg(MsgType type, Json::Value message, std::string userName, std::string color)
|
||||
{
|
||||
Json::Value sendMessage;
|
||||
sendMessage["type"] = type;
|
||||
sendMessage["message"] = message;
|
||||
sendMessage["userName"] = userName;
|
||||
sendMessage["color"] = color;
|
||||
send(sendMessage);
|
||||
}
|
||||
|
||||
void ChatUser::checkerTask()
|
||||
{
|
||||
while (!_stop)
|
||||
{
|
||||
fd_set readSd;
|
||||
FD_ZERO(&readSd);
|
||||
FD_SET(_socket, &readSd);
|
||||
timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 500;
|
||||
int selectResult = select(_socket + 1, &readSd, NULL, NULL, &tv);
|
||||
if (selectResult == 1 && FD_ISSET(_socket, &readSd) == 1)
|
||||
{
|
||||
_parent->removeUser(_token);
|
||||
_stop = true;
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
std::string msg = readSocket(_socket);
|
||||
if (msg == "")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
handleMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatUser::stop()
|
||||
{
|
||||
_stop = true;
|
||||
}
|
||||
|
||||
std::string ChatUser::color() const
|
||||
{
|
||||
return _color;
|
||||
}
|
||||
|
||||
void ChatUser::setParent(std::shared_ptr<ChatRoom> parent)
|
||||
{
|
||||
_parent = std::move(parent);
|
||||
}
|
||||
|
||||
void ChatUser::send(std::string out)
|
||||
{
|
||||
Base::send(_socket, out);
|
||||
}
|
||||
|
||||
void ChatUser::send(Json::Value out)
|
||||
{
|
||||
Base::send(_socket, out);
|
||||
}
|
||||
|
||||
void ChatUser::handleMessage(std::string message)
|
||||
{
|
||||
Json::Value jsonTree = getJsonTree(message);
|
||||
if (jsonTree["token"].asString() != _token)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (jsonTree["type"].asString() == "message")
|
||||
{
|
||||
checkString(jsonTree["message"].asString());
|
||||
}
|
||||
else if (jsonTree["type"].asString() == "dice")
|
||||
{
|
||||
doDice();
|
||||
}
|
||||
else if (jsonTree["type"].asString() == "scream")
|
||||
{
|
||||
_parent->addMessage(ChatUser::scream, jsonTree["message"].asString(), std::string(_name), std::string(_color));
|
||||
}
|
||||
else if (jsonTree["type"].asString() == "do")
|
||||
{
|
||||
_parent->addMessage(ChatUser::dosomething, jsonTree["message"].asString(), std::string(_name), std::string(_color));
|
||||
}
|
||||
else if (jsonTree["type"].asString() == "join")
|
||||
{
|
||||
changeRoom(jsonTree["newroom"].asString(), jsonTree["password"].asString());
|
||||
}
|
||||
else if (jsonTree["type"].asString() == "userlist")
|
||||
{
|
||||
sendUserList();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatUser::checkString(std::string message)
|
||||
{
|
||||
if (message.substr(0, 6) == "/join ")
|
||||
{
|
||||
message = message.substr(6);
|
||||
if (message.find(" ") == std::string::npos)
|
||||
{
|
||||
changeRoom(message, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string room = message.substr(0, message.find(" "));
|
||||
std::string password = message.substr(message.find(" ") + 1);
|
||||
changeRoom(room, password);
|
||||
}
|
||||
}
|
||||
else if (message == "/dice")
|
||||
{
|
||||
doDice();
|
||||
}
|
||||
else
|
||||
{
|
||||
_parent->addMessage(ChatUser::message, std::string(message), std::string(_name), std::string(_color));
|
||||
}
|
||||
}
|
||||
|
||||
void ChatUser::sendUserList()
|
||||
{
|
||||
Json::Value userList = _parent->userList();
|
||||
Json::Value msg = Json::objectValue;
|
||||
msg["userlist"] = userList;
|
||||
sendMsg(userListe, msg, "", "");
|
||||
}
|
||||
|
||||
void ChatUser::doDice()
|
||||
{
|
||||
switch (_parent->addDice(shared_from_this(), (rand() % 6) + 1))
|
||||
{
|
||||
case 1:
|
||||
sendMsg(system, "dice_not_possible", "", "");
|
||||
break;
|
||||
case 2:
|
||||
sendMsg(system, "dice_allready_done", "", "");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ChatUser::changeRoom(std::string newRoom, std::string password)
|
||||
{
|
||||
if (!_parent->userToNewRoom(shared_from_this(), newRoom, password))
|
||||
{
|
||||
sendMsg(ChatUser::system, "room_not_possible", "", "");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Lib
|
||||
} // namespace Yc
|
||||
71
src/core/chat_user.h
Normal file
71
src/core/chat_user.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#ifndef YC_LIB_CHAT_USER_H
|
||||
#define YC_LIB_CHAT_USER_H
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <json/json.h>
|
||||
#include <thread>
|
||||
#include "lib/base.h"
|
||||
#include "object/user.h"
|
||||
|
||||
namespace Yc
|
||||
{
|
||||
namespace Lib
|
||||
{
|
||||
|
||||
class ChatRoom;
|
||||
|
||||
class ChatUser : public Base, public std::enable_shared_from_this<ChatUser>
|
||||
{
|
||||
public:
|
||||
enum MsgType
|
||||
{
|
||||
error = -1,
|
||||
token = 1,
|
||||
userListe = 2,
|
||||
roomList = 3,
|
||||
message = 4,
|
||||
system = 5,
|
||||
scream = 6,
|
||||
dosomething = 7,
|
||||
dice = 8,
|
||||
result = 9
|
||||
};
|
||||
|
||||
ChatUser(std::shared_ptr<ChatRoom> parent, std::string name, std::string color, int socket);
|
||||
~ChatUser();
|
||||
std::string name() const;
|
||||
bool validateToken(std::string token);
|
||||
bool isUser(std::shared_ptr<ChatUser> toValidate);
|
||||
void sendMsg(MsgType type, std::string message, std::string userName, std::string color);
|
||||
void sendMsg(MsgType type, const char *message, std::string userName, std::string color);
|
||||
void sendMsg(MsgType type, Json::Value message, std::string userName, std::string color);
|
||||
void checkerTask();
|
||||
void stop();
|
||||
std::string color() const;
|
||||
void setParent(std::shared_ptr<ChatRoom> parent);
|
||||
|
||||
private:
|
||||
std::shared_ptr<ChatRoom> _parent;
|
||||
Yc::Object::User _user;
|
||||
std::string _name;
|
||||
std::string _color;
|
||||
int _socket;
|
||||
std::string _token;
|
||||
bool _stop;
|
||||
std::unique_ptr<std::thread> thread;
|
||||
|
||||
void send(std::string out);
|
||||
void send(Json::Value out);
|
||||
void handleMessage(std::string message);
|
||||
void doDice();
|
||||
void changeRoom(std::string newRoom, std::string password);
|
||||
void checkString(std::string message);
|
||||
void sendUserList();
|
||||
};
|
||||
|
||||
} // namespace Lib
|
||||
} // namespace Yc
|
||||
|
||||
#endif // YC_LIB_CHAT_USER_H
|
||||
31
src/core/config.cpp
Executable file
31
src/core/config.cpp
Executable file
@@ -0,0 +1,31 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <json/json.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
namespace Yc {
|
||||
namespace Lib {
|
||||
|
||||
Config::Config() {
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
void Config::loadConfig() {
|
||||
std::ifstream configStream("/etc/yourpart/chatconfig.json", std::ifstream::binary);
|
||||
configStream >> jsonConfig;
|
||||
}
|
||||
|
||||
Json::Value Config::value(std::string groupName, std::string field) {
|
||||
if (jsonConfig[groupName].isNull()) {
|
||||
return "";
|
||||
}
|
||||
return jsonConfig[groupName][field];
|
||||
}
|
||||
|
||||
Json::Value Config::group(std::string groupName) {
|
||||
return jsonConfig[groupName];
|
||||
}
|
||||
|
||||
} // namespace Lib
|
||||
} // namespace Yc
|
||||
24
src/core/config.h
Executable file
24
src/core/config.h
Executable file
@@ -0,0 +1,24 @@
|
||||
#ifndef YC_LIB_CONFIG_H
|
||||
#define YC_LIB_CONFIG_H
|
||||
|
||||
#include <string>
|
||||
#include <json/value.h>
|
||||
|
||||
namespace Yc {
|
||||
namespace Lib {
|
||||
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
Config();
|
||||
void loadConfig();
|
||||
Json::Value value(std::string group, std::string field);
|
||||
Json::Value group(std::string groupName);
|
||||
private:
|
||||
Json::Value jsonConfig;
|
||||
};
|
||||
|
||||
} // namespace Lib
|
||||
} // namespace Yc
|
||||
|
||||
#endif // YC_LIB_CONFIG_H
|
||||
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
|
||||
39
src/core/server.h
Executable file
39
src/core/server.h
Executable file
@@ -0,0 +1,39 @@
|
||||
#ifndef YP_LIB_SERVER_H
|
||||
#define YP_LIB_SERVER_H
|
||||
|
||||
#include "config.h"
|
||||
#include "lib/database.h"
|
||||
#include <vector>
|
||||
#include "chat_room.h"
|
||||
#include "lib/base.h"
|
||||
#include <json/value.h>
|
||||
|
||||
namespace Yc {
|
||||
namespace Lib {
|
||||
|
||||
class Server: public Base, public std::enable_shared_from_this<Server>
|
||||
{
|
||||
public:
|
||||
Server(std::shared_ptr<Yc::Lib::Config> config, std::shared_ptr<Yc::Lib::Database> database);
|
||||
void run();
|
||||
std::vector<std::string> roomList();
|
||||
Json::Value jsonRoomList();
|
||||
bool roomAllowed(std::string roomName, std::string userName, std::string password);
|
||||
bool changeRoom(std::shared_ptr<ChatUser> user, std::string newRoom, std::string password);
|
||||
int _socket;
|
||||
std::shared_ptr<Yc::Lib::Config> _config;
|
||||
std::shared_ptr<Yc::Lib::Database> _database;
|
||||
bool _stop;
|
||||
std::vector<std::shared_ptr<Yc::Lib::ChatRoom>> _rooms;
|
||||
void createRooms(Json::Value roomList);
|
||||
private:
|
||||
void handleRequest();
|
||||
void inputSwitcher(int userSocket, std::string input);
|
||||
bool userExists(std::string userName);
|
||||
void initUser(int userSocket, Json::Value data);
|
||||
};
|
||||
|
||||
} // namespace Lib
|
||||
} // namespace Yp
|
||||
|
||||
#endif // YP_LIB_SERVER_H
|
||||
Reference in New Issue
Block a user