261 lines
9.4 KiB
C++
261 lines
9.4 KiB
C++
#include "character_creation_worker.h"
|
|
#include "connection_guard.h"
|
|
#include <iostream>
|
|
#include <chrono>
|
|
#include <thread>
|
|
#include <random>
|
|
|
|
CharacterCreationWorker::CharacterCreationWorker(ConnectionPool &pool, MessageBroker &broker)
|
|
: Worker(pool, broker, "CharacterCreationWorker"),
|
|
gen(std::random_device{}()),
|
|
dist(2, 3),
|
|
deathCheckRunning(true),
|
|
deathThread(&CharacterCreationWorker::monitorCharacterDeaths, this) {
|
|
}
|
|
|
|
CharacterCreationWorker::~CharacterCreationWorker() {
|
|
deathCheckRunning.store(false);
|
|
if (deathThread.joinable()) {
|
|
deathThread.join();
|
|
}
|
|
}
|
|
|
|
void CharacterCreationWorker::run() {
|
|
while (runningWorker) {
|
|
setCurrentStep("Check if previous day character was created");
|
|
if (!isTodayCharacterCreated()) {
|
|
setCurrentStep("Create characters for today");
|
|
createCharactersForToday();
|
|
}
|
|
setCurrentStep("Sleep for 60 seconds");
|
|
for (int i = 0; i < 60 && runningWorker; ++i) {
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
setCurrentStep("signalActivity()");
|
|
signalActivity();
|
|
}
|
|
setCurrentStep("Loop done");
|
|
}
|
|
}
|
|
|
|
bool CharacterCreationWorker::isTodayCharacterCreated() {
|
|
try {
|
|
setCurrentStep("Get Database Connection");
|
|
ConnectionGuard connGuard(pool);
|
|
auto &db = connGuard.get();
|
|
setCurrentStep("Execute Query");
|
|
auto results = db.query(QUERY_IS_PREVIOUS_DAY_CHARACTER_CREATED);
|
|
return !results.empty();
|
|
} catch (const std::exception &e) {
|
|
std::cerr << "[CharacterCreationWorker] Fehler in isTodayCharacterCreated: " << e.what() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void CharacterCreationWorker::createCharactersForToday() {
|
|
loadNames();
|
|
if (first_name_cache.empty() || last_name_cache.empty()) {
|
|
std::cerr << "[CharacterCreationWorker] Fehler: Namen konnten nicht geladen werden." << std::endl;
|
|
return;
|
|
}
|
|
|
|
auto town_ids = getTownRegionIds();
|
|
for (auto region_id : town_ids) {
|
|
createCharactersForRegion(region_id);
|
|
}
|
|
}
|
|
|
|
void CharacterCreationWorker::createCharactersForRegion(int region_id) {
|
|
std::vector<int> nobility_stands = {1, 2, 3};
|
|
std::vector<std::string> genders = {"male", "female"};
|
|
for (auto nobility : nobility_stands) {
|
|
for (const auto &gender : genders) {
|
|
int num_chars = dist(gen);
|
|
for (int i = 0; i < num_chars; ++i) {
|
|
createCharacter(region_id, gender, nobility);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CharacterCreationWorker::createCharacter(int region_id, const std::string &gender, int title_of_nobility) {
|
|
int first_name_id = getRandomFromSet(first_name_cache[gender]);
|
|
if (first_name_id == -1) {
|
|
std::cerr << "Fehler: Kein passender Vorname gefunden." << std::endl;
|
|
return;
|
|
}
|
|
int last_name_id = getRandomFromSet(last_name_cache);
|
|
if (last_name_id == -1) {
|
|
std::cerr << "Fehler: Kein passender Nachname gefunden." << std::endl;
|
|
return;
|
|
}
|
|
try {
|
|
ConnectionGuard connGuard(pool);
|
|
auto &db = connGuard.get();
|
|
|
|
db.prepare("insert_character", QUERY_INSERT_CHARACTER);
|
|
db.execute("insert_character", {std::to_string(region_id),
|
|
std::to_string(first_name_id),
|
|
std::to_string(last_name_id),
|
|
gender,
|
|
std::to_string(title_of_nobility)});
|
|
} catch (const std::exception &e) {
|
|
std::cerr << "[CharacterCreationWorker] Fehler in createCharacter: " << e.what() << std::endl;
|
|
}
|
|
}
|
|
|
|
void CharacterCreationWorker::monitorCharacterDeaths() {
|
|
while (deathCheckRunning) {
|
|
try {
|
|
ConnectionGuard connGuard(pool);
|
|
auto &db = connGuard.get();
|
|
|
|
auto results = db.query(QUERY_GET_ELIGIBLE_NPC_FOR_DEATH);
|
|
for (const auto &row : results) {
|
|
int characterId = std::stoi(row.at("id"));
|
|
int age = std::stoi(row.at("age"));
|
|
if (calculateDeathProbability(age)) {
|
|
handleCharacterDeath(characterId);
|
|
}
|
|
}
|
|
} catch (const std::exception &e) {
|
|
std::cerr << "[CharacterCreationWorker] Fehler beim Überprüfen von Todesfällen: " << e.what() << std::endl;
|
|
}
|
|
|
|
std::this_thread::sleep_for(std::chrono::hours(1));
|
|
}
|
|
}
|
|
|
|
bool CharacterCreationWorker::calculateDeathProbability(int age) {
|
|
if (age < 60) {
|
|
return false;
|
|
}
|
|
|
|
double baseProbability = 0.01;
|
|
double increasePerYear = 0.01;
|
|
double deathProbability = baseProbability + (increasePerYear * (age - 60));
|
|
|
|
std::uniform_real_distribution<double> deathDist(0.0, 1.0);
|
|
return deathDist(gen) < deathProbability;
|
|
}
|
|
|
|
void CharacterCreationWorker::handleCharacterDeath(int characterId) {
|
|
try {
|
|
ConnectionGuard connGuard(pool);
|
|
auto &db = connGuard.get();
|
|
|
|
// 1) Director löschen und User benachrichtigen
|
|
db.prepare("delete_director", QUERY_DELETE_DIRECTOR);
|
|
auto dirResult = db.execute("delete_director", { std::to_string(characterId) });
|
|
if (!dirResult.empty()) {
|
|
int userId = std::stoi(dirResult[0].at("user_id"));
|
|
notifyUser(userId, "director_death");
|
|
}
|
|
|
|
// 2) Relationships löschen und betroffene User benachrichtigen
|
|
db.prepare("delete_relationship", QUERY_DELETE_RELATIONSHIP);
|
|
auto relResult = db.execute("delete_relationship", { std::to_string(characterId) });
|
|
for (auto &row : relResult) {
|
|
int relatedUserId = std::stoi(row.at("related_user_id"));
|
|
notifyUser(relatedUserId, "relationship_death");
|
|
}
|
|
|
|
// 3) Child-Relations löschen und Eltern benachrichtigen
|
|
db.prepare("delete_child_relation", QUERY_DELETE_CHILD_RELATION);
|
|
auto childResult = db.execute("delete_child_relation", { std::to_string(characterId) });
|
|
for (auto &row : childResult) {
|
|
int fatherUserId = std::stoi(row.at("father_user_id"));
|
|
int motherUserId = std::stoi(row.at("mother_user_id"));
|
|
notifyUser(fatherUserId, "child_death");
|
|
notifyUser(motherUserId, "child_death");
|
|
}
|
|
|
|
// 4) Charakter als verstorben markieren
|
|
markCharacterAsDeceased(characterId);
|
|
|
|
} catch (const std::exception &e) {
|
|
std::cerr << "[CharacterCreationWorker] Fehler beim Bearbeiten des Todes: "
|
|
<< e.what() << std::endl;
|
|
}
|
|
}
|
|
|
|
void CharacterCreationWorker::notifyUser(int userId, const std::string &eventType) {
|
|
try {
|
|
ConnectionGuard connGuard(pool);
|
|
auto &db = connGuard.get();
|
|
|
|
db.prepare("insert_notification", QUERY_INSERT_NOTIFICATION);
|
|
db.execute("insert_notification", { std::to_string(userId) });
|
|
|
|
// Sende falukantUpdateStatus nach dem Einfügen der Benachrichtigung
|
|
nlohmann::json updateMessage = {
|
|
{"event", "falukantUpdateStatus"},
|
|
{"user_id", userId}
|
|
};
|
|
broker.publish(updateMessage.dump());
|
|
|
|
// Sende auch die ursprüngliche Benachrichtigung
|
|
nlohmann::json message = {
|
|
{"event", eventType},
|
|
{"user_id", userId}
|
|
};
|
|
broker.publish(message.dump());
|
|
} catch (const std::exception &e) {
|
|
std::cerr << "[CharacterCreationWorker] Fehler beim Senden der Benachrichtigung: "
|
|
<< e.what() << std::endl;
|
|
}
|
|
}
|
|
|
|
void CharacterCreationWorker::markCharacterAsDeceased(int characterId) {
|
|
try {
|
|
ConnectionGuard connGuard(pool);
|
|
auto &db = connGuard.get();
|
|
|
|
db.prepare("mark_character_deceased", QUERY_MARK_CHARACTER_DECEASED);
|
|
db.execute("mark_character_deceased", {std::to_string(characterId)});
|
|
} catch (const std::exception &e) {
|
|
std::cerr << "[CharacterCreationWorker] Fehler beim Markieren des Charakters als verstorben: " << e.what() << std::endl;
|
|
}
|
|
}
|
|
|
|
std::vector<int> CharacterCreationWorker::getTownRegionIds() {
|
|
try {
|
|
ConnectionGuard connGuard(pool);
|
|
auto &db = connGuard.get();
|
|
auto rows = db.query(QUERY_GET_TOWN_REGION_IDS);
|
|
std::vector<int> ids;
|
|
for (const auto &row : rows) {
|
|
ids.push_back(std::stoi(row.at("id")));
|
|
}
|
|
return ids;
|
|
} catch (const std::exception &e) {
|
|
std::cerr << "[CharacterCreationWorker] Fehler in getTownRegionIds: " << e.what() << std::endl;
|
|
return {};
|
|
}
|
|
}
|
|
|
|
void CharacterCreationWorker::loadNames() {
|
|
try {
|
|
ConnectionGuard connGuard(pool);
|
|
auto &db = connGuard.get();
|
|
auto firstNameRows = db.query(QUERY_LOAD_FIRST_NAMES);
|
|
for (const auto &row : firstNameRows) {
|
|
first_name_cache[row.at("gender")].insert(std::stoi(row.at("id")));
|
|
}
|
|
auto lastNameRows = db.query(QUERY_LOAD_LAST_NAMES);
|
|
for (const auto &row : lastNameRows) {
|
|
last_name_cache.insert(std::stoi(row.at("id")));
|
|
}
|
|
} catch (const std::exception &e) {
|
|
std::cerr << "[CharacterCreationWorker] Fehler in loadNames: " << e.what() << std::endl;
|
|
}
|
|
}
|
|
|
|
int CharacterCreationWorker::getRandomFromSet(const std::unordered_set<int> &name_set) {
|
|
if (name_set.empty()) {
|
|
return -1;
|
|
}
|
|
auto it = name_set.begin();
|
|
std::advance(it, std::uniform_int_distribution<int>(0, name_set.size() - 1)(gen));
|
|
return *it;
|
|
}
|