#include "character_creation_worker.h" #include "connection_guard.h" #include #include #include #include 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 nobility_stands = {1, 2, 3}; std::vector 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 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 CharacterCreationWorker::getTownRegionIds() { try { ConnectionGuard connGuard(pool); auto &db = connGuard.get(); auto rows = db.query(QUERY_GET_TOWN_REGION_IDS); std::vector 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 &name_set) { if (name_set.empty()) { return -1; } auto it = name_set.begin(); std::advance(it, std::uniform_int_distribution(0, name_set.size() - 1)(gen)); return *it; }