- Add queries and logic to delete associated data when a character dies, including directors, relationships, child relations, knowledge, debtors prism, political offices, and election candidates. - Enhance error handling to log issues during the deletion process.
428 lines
16 KiB
C++
428 lines
16 KiB
C++
#include "usercharacterworker.h"
|
|
#include "connection_guard.h"
|
|
#include <iostream>
|
|
#include <chrono>
|
|
#include <thread>
|
|
#include <vector>
|
|
#include <cmath>
|
|
#include "utils.h"
|
|
|
|
UserCharacterWorker::UserCharacterWorker(ConnectionPool &pool, MessageBroker &broker)
|
|
: Worker(pool, broker, "UserCharacterWorker"),
|
|
gen(rd()), dist(0.0, 1.0) {}
|
|
|
|
UserCharacterWorker::~UserCharacterWorker() {}
|
|
|
|
void UserCharacterWorker::run() {
|
|
using namespace std::chrono;
|
|
|
|
auto lastExecutionTime = steady_clock::now();
|
|
int lastPregnancyDay = -1;
|
|
|
|
while (runningWorker) {
|
|
signalActivity();
|
|
|
|
// 1h-Block
|
|
auto nowSteady = steady_clock::now();
|
|
auto elapsed = duration_cast<seconds>(nowSteady - lastExecutionTime).count();
|
|
if (elapsed >= 3600) {
|
|
try {
|
|
processCharacterEvents();
|
|
updateCharactersMood();
|
|
handleCredits();
|
|
} catch (const std::exception &e) {
|
|
std::cerr << "[UserCharacterWorker] Fehler in processCharacterEvents: " << e.what() << std::endl;
|
|
}
|
|
lastExecutionTime = nowSteady;
|
|
}
|
|
|
|
// Schwangerschaftsverarbeitung: initial oder täglich um 06:00 einmal pro Tag
|
|
auto nowSys = system_clock::now();
|
|
std::time_t t = system_clock::to_time_t(nowSys);
|
|
std::tm local_tm;
|
|
localtime_r(&t, &local_tm);
|
|
|
|
if (lastPregnancyDay == -1 || (local_tm.tm_hour == 6 && local_tm.tm_yday != lastPregnancyDay)) {
|
|
try {
|
|
processPregnancies();
|
|
} catch (const std::exception &e) {
|
|
std::cerr << "[UserCharacterWorker] Fehler in processPregnancies: " << e.what() << std::endl;
|
|
}
|
|
lastPregnancyDay = local_tm.tm_yday;
|
|
}
|
|
|
|
std::this_thread::sleep_for(seconds(1));
|
|
recalculateKnowledge();
|
|
}
|
|
}
|
|
|
|
void UserCharacterWorker::processCharacterEvents() {
|
|
setCurrentStep("Get character data");
|
|
ConnectionGuard connGuard(pool);
|
|
auto &db = connGuard.get();
|
|
db.prepare(QUERY_GET_USERS_TO_UPDATE, QUERY_GET_USERS_TO_UPDATE);
|
|
auto rows = db.execute(QUERY_GET_USERS_TO_UPDATE);
|
|
std::vector<Character> characters;
|
|
for (const auto &row : rows) {
|
|
characters.push_back({ std::stoi(row.at("id")), std::stoi(row.at("age")), std::stoi(row.at("health")) });
|
|
}
|
|
for (auto &character : characters) {
|
|
updateCharacterHealth(character);
|
|
}
|
|
}
|
|
|
|
void UserCharacterWorker::updateCharacterHealth(Character& character) {
|
|
int healthChange = calculateHealthChange(character.age);
|
|
|
|
if (healthChange != 0) {
|
|
character.health = std::max(0, character.health + healthChange);
|
|
if (character.health == 0) {
|
|
handleCharacterDeath(character.id);
|
|
return;
|
|
}
|
|
ConnectionGuard connGuard(pool);
|
|
auto &db = connGuard.get();
|
|
db.prepare("QUERY_UPDATE_CHARACTERS_HEALTH", QUERY_UPDATE_CHARACTERS_HEALTH);
|
|
db.execute("QUERY_UPDATE_CHARACTERS_HEALTH",
|
|
{ std::to_string(character.health), std::to_string(character.id) });
|
|
}
|
|
}
|
|
|
|
void UserCharacterWorker::updateCharactersMood() {
|
|
ConnectionGuard connGuard(pool);
|
|
auto &db = connGuard.get();
|
|
db.prepare("QUERY_UPDATE_MOOD", QUERY_UPDATE_MOOD);
|
|
db.execute("QUERY_UPDATE_MOOD");
|
|
}
|
|
|
|
int UserCharacterWorker::calculateHealthChange(int age) {
|
|
if (age < 30) {
|
|
return 0;
|
|
}
|
|
if (age >= 45) {
|
|
double probability = std::min(1.0, 0.1 + (age - 45) * 0.02);
|
|
if (dist(gen) < probability) {
|
|
return -std::uniform_int_distribution<int>(1, 10)(gen);
|
|
}
|
|
return 0;
|
|
}
|
|
double probability = (age - 30) / 30.0;
|
|
return (dist(gen) < probability) ? -1 : 0;
|
|
}
|
|
|
|
void UserCharacterWorker::handleCharacterDeath(int characterId) {
|
|
setHeir(characterId);
|
|
|
|
nlohmann::json deathEvent = {
|
|
{"event", "CharacterDeath"},
|
|
{"character_id", characterId}
|
|
};
|
|
|
|
broker.publish(deathEvent.dump());
|
|
|
|
ConnectionGuard connGuard(pool);
|
|
auto &db = connGuard.get();
|
|
|
|
try {
|
|
// 1) Director löschen (falls Character ein Director ist)
|
|
db.prepare("delete_director", QUERY_DELETE_DIRECTOR);
|
|
db.execute("delete_director", { std::to_string(characterId) });
|
|
|
|
// 2) Relationships löschen (Ehepartner, etc.)
|
|
db.prepare("delete_relationship", QUERY_DELETE_RELATIONSHIP);
|
|
db.execute("delete_relationship", { std::to_string(characterId) });
|
|
|
|
// 3) Child-Relations löschen (als Kind, Vater oder Mutter)
|
|
db.prepare("delete_child_relation", QUERY_DELETE_CHILD_RELATION);
|
|
db.execute("delete_child_relation", { std::to_string(characterId) });
|
|
|
|
// 4) Knowledge löschen
|
|
db.prepare("delete_knowledge", QUERY_DELETE_KNOWLEDGE);
|
|
db.execute("delete_knowledge", { std::to_string(characterId) });
|
|
|
|
// 5) Debtors_prism löschen
|
|
db.prepare("delete_debtors_prism", QUERY_DELETE_DEBTORS_PRISM);
|
|
db.execute("delete_debtors_prism", { std::to_string(characterId) });
|
|
|
|
// 6) Political Office löschen
|
|
db.prepare("delete_political_office", QUERY_DELETE_POLITICAL_OFFICE);
|
|
db.execute("delete_political_office", { std::to_string(characterId) });
|
|
|
|
// 7) Election Candidate löschen
|
|
db.prepare("delete_election_candidate", QUERY_DELETE_ELECTION_CANDIDATE);
|
|
db.execute("delete_election_candidate", { std::to_string(characterId) });
|
|
|
|
// 8) Character löschen
|
|
db.prepare("delete_character", "DELETE FROM falukant_data.character WHERE id = $1");
|
|
db.execute("delete_character", { std::to_string(characterId) });
|
|
} catch (const std::exception &e) {
|
|
std::cerr << "[UserCharacterWorker] Fehler beim Löschen der Character-Verknüpfungen: "
|
|
<< e.what() << std::endl;
|
|
}
|
|
}
|
|
|
|
void UserCharacterWorker::setHeir(int characterId) {
|
|
auto falukantUserId = getFalukantUserId(characterId);
|
|
auto heirId = getHeirFromChildren(characterId);
|
|
auto newMoney = calculateNewMoney(falukantUserId, true);
|
|
if (heirId < 1) {
|
|
getRandomHeir(characterId);
|
|
newMoney = calculateNewMoney(falukantUserId, false);
|
|
}
|
|
setNewCharacter(falukantUserId, heirId);
|
|
setNewMoney(falukantUserId, newMoney);
|
|
}
|
|
|
|
int UserCharacterWorker::getFalukantUserId(int characterId) {
|
|
ConnectionGuard guard(pool);
|
|
auto &db = guard.get();
|
|
|
|
db.prepare("QUERY_GET_FALUKANT_USER_ID", QUERY_GET_FALUKANT_USER_ID);
|
|
const auto rows = db.execute("QUERY_GET_FALUKANT_USER_ID", { std::to_string(characterId) });
|
|
if (!rows.empty() && !rows.front().at("user_id").empty()) {
|
|
return std::stoi(rows.front().at("user_id"));
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int UserCharacterWorker::getHeirFromChildren(int deceasedCharacterId) {
|
|
ConnectionGuard guard(pool);
|
|
auto &db = guard.get();
|
|
|
|
db.prepare("QUERY_GET_HEIR", QUERY_GET_HEIR);
|
|
const auto rows = db.execute("QUERY_GET_HEIR", { std::to_string(deceasedCharacterId) });
|
|
if (!rows.empty()) {
|
|
return std::stoi(rows.front().at("child_character_id"));
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int UserCharacterWorker::getRandomHeir(int deceasedCharacterId) {
|
|
ConnectionGuard guard(pool);
|
|
auto &db = guard.get();
|
|
|
|
db.prepare("QUERY_RANDOM_HEIR", QUERY_RANDOM_HEIR);
|
|
const auto rows = db.execute("QUERY_RANDOM_HEIR", { std::to_string(deceasedCharacterId) });
|
|
if (!rows.empty()) {
|
|
return std::stoi(rows.front().at("child_character_id"));
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void UserCharacterWorker::setNewCharacter(int falukantUserId, int heirCharacterId) {
|
|
if (heirCharacterId < 1) return;
|
|
|
|
ConnectionGuard guard(pool);
|
|
auto &db = guard.get();
|
|
|
|
db.prepare("QUERY_SET_CHARACTER_USER", QUERY_SET_CHARACTER_USER);
|
|
db.execute("QUERY_SET_CHARACTER_USER", {
|
|
std::to_string(falukantUserId),
|
|
std::to_string(heirCharacterId)
|
|
});
|
|
}
|
|
|
|
void UserCharacterWorker::setNewMoney(int falukantUserId, double newAmount) {
|
|
ConnectionGuard guard(pool);
|
|
auto &db = guard.get();
|
|
|
|
db.prepare("QUERY_UPDATE_USER_MONEY", QUERY_UPDATE_USER_MONEY);
|
|
db.execute("QUERY_UPDATE_USER_MONEY", {
|
|
std::to_string(newAmount),
|
|
std::to_string(falukantUserId)
|
|
});
|
|
}
|
|
|
|
void UserCharacterWorker::recalculateKnowledge() {
|
|
setCurrentStep("Get character data");
|
|
ConnectionGuard connGuard(pool);
|
|
auto &db = connGuard.get();
|
|
db.prepare("QUERY_UPDATE_GET_ITEMS_TO_UPDATE", QUERY_UPDATE_GET_ITEMS_TO_UPDATE);
|
|
auto rows = db.execute("QUERY_UPDATE_GET_ITEMS_TO_UPDATE");
|
|
for (const auto &updateItem: rows) {
|
|
if (std::stoi(updateItem.at("quantity")) >= 10) {
|
|
db.prepare("QUERY_UPDATE_GET_CHARACTER_IDS", QUERY_UPDATE_GET_CHARACTER_IDS);
|
|
auto charactersData = db.execute("QUERY_UPDATE_GET_CHARACTER_IDS", { updateItem.at("producer_id") });
|
|
for (const auto &characterRow: charactersData) {
|
|
db.prepare("QUERY_UPDATE_KNOWLEDGE", QUERY_UPDATE_KNOWLEDGE);
|
|
if (characterRow.at("director_id") == "") {
|
|
db.execute("QUERY_UPDATE_KNOWLEDGE", { characterRow.at("character_id"), updateItem.at("product_id"), "2" });
|
|
} else {
|
|
db.execute("QUERY_UPDATE_KNOWLEDGE", { characterRow.at("character_id"), updateItem.at("product_id"), "1" });
|
|
db.execute("QUERY_UPDATE_KNOWLEDGE", { characterRow.at("director_id"), updateItem.at("product_id"), "1" });
|
|
}
|
|
}
|
|
}
|
|
db.prepare("QUERY_DELETE_LOG_ENTRY", QUERY_DELETE_LOG_ENTRY);
|
|
db.execute("QUERY_DELETE_LOG_ENTRY", { updateItem.at("id") });
|
|
const nlohmann::json message = {
|
|
{"event", "knowledge_update"},
|
|
};
|
|
sendMessageToFalukantUsers(std::stoi(updateItem.at("producer_id")), message);
|
|
}
|
|
}
|
|
|
|
void UserCharacterWorker::processPregnancies() {
|
|
ConnectionGuard connGuard(pool);
|
|
auto &db = connGuard.get();
|
|
|
|
db.prepare("QUERY_AUTOBATISM", QUERY_AUTOBATISM);
|
|
db.execute("QUERY_AUTOBATISM");
|
|
|
|
db.prepare("get_candidates", QUERY_GET_PREGNANCY_CANDIDATES);
|
|
auto rows = db.execute("get_candidates");
|
|
|
|
const nlohmann::json message = {
|
|
{"event", "children_update"},
|
|
};
|
|
|
|
for (const auto &row : rows) {
|
|
int fatherCid = Utils::optionalStoiOrDefault(row, "father_cid", -1);
|
|
int motherCid = Utils::optionalStoiOrDefault(row, "mother_cid", -1);
|
|
if (fatherCid < 0 || motherCid < 0) {
|
|
continue; // ungültige Daten überspringen
|
|
}
|
|
|
|
int titleOfNobility = Utils::optionalStoiOrDefault(row, "title_of_nobility", 0);
|
|
int lastName = Utils::optionalStoiOrDefault(row, "last_name", 0);
|
|
int regionId = Utils::optionalStoiOrDefault(row, "region_id", 0);
|
|
|
|
auto fatherUidOpt = Utils::optionalUid(row.at("father_uid"));
|
|
auto motherUidOpt = Utils::optionalUid(row.at("mother_uid"));
|
|
|
|
// Geschlecht zufällig
|
|
std::string gender = (dist(gen) < 0.5) ? "male" : "female";
|
|
|
|
db.prepare("insert_child", QUERY_INSERT_CHILD);
|
|
auto resChild = db.execute("insert_child", {
|
|
std::to_string(regionId), // $1
|
|
gender, // $2
|
|
std::to_string(lastName), // $3
|
|
std::to_string(titleOfNobility) // $4
|
|
});
|
|
|
|
if (resChild.empty()) continue;
|
|
int childCid = Utils::optionalStoiOrDefault(resChild.front(), "child_cid", -1);
|
|
if (childCid < 0) continue;
|
|
|
|
db.prepare("insert_relation", QUERY_INSERT_CHILD_RELATION);
|
|
db.execute("insert_relation", {
|
|
std::to_string(fatherCid),
|
|
std::to_string(motherCid),
|
|
std::to_string(childCid)
|
|
});
|
|
|
|
if (fatherUidOpt) {
|
|
sendMessageToFalukantUsers(*fatherUidOpt, message);
|
|
// Sende falukantUpdateStatus nach dem Erstellen des Kindes
|
|
nlohmann::json updateMessage = { { "event", "falukantUpdateStatus" } };
|
|
sendMessageToFalukantUsers(*fatherUidOpt, updateMessage);
|
|
}
|
|
if (motherUidOpt) {
|
|
sendMessageToFalukantUsers(*motherUidOpt, message);
|
|
// Sende falukantUpdateStatus nach dem Erstellen des Kindes
|
|
nlohmann::json updateMessage = { { "event", "falukantUpdateStatus" } };
|
|
sendMessageToFalukantUsers(*motherUidOpt, updateMessage);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UserCharacterWorker::handleCredits() {
|
|
ConnectionGuard connGuard(pool);
|
|
auto &db = connGuard.get();
|
|
db.prepare("QUERY_GET_OPEN_CREDITS", QUERY_GET_OPEN_CREDITS);
|
|
const auto &credits = db.execute("QUERY_GET_OPEN_CREDITS");
|
|
const nlohmann::json message = {
|
|
{ "event", "falukantUpdateStatus" }
|
|
};
|
|
db.prepare("QUERY_UPDATE_CREDIT", QUERY_UPDATE_CREDIT);
|
|
db.prepare("QUERY_ADD_CHARACTER_TO_DEBTORS_PRISM", QUERY_ADD_CHARACTER_TO_DEBTORS_PRISM);
|
|
for (const auto &credit: credits) {
|
|
const auto userMoney = std::stod(credit.at("money"));
|
|
auto remainingAmount = std::stod(credit.at("remaining_amount"));
|
|
const auto amount = std::stod(credit.at("amount"));
|
|
const auto fee = std::stoi(credit.at("interest_rate"));
|
|
const auto falukantUserId = std::stoi(credit.at("user_id"));
|
|
const auto payRate = amount / 10 + amount * fee / 100;
|
|
remainingAmount -= payRate;
|
|
if (payRate <= userMoney - (payRate * 3)) {
|
|
changeFalukantUserMoney(falukantUserId, -payRate, "credit pay rate", message);
|
|
} else {
|
|
if (credit.at("prism_started_previously") == "t") {
|
|
changeFalukantUserMoney(falukantUserId, payRate, "debitor_prism", message);
|
|
} else {
|
|
db.execute("QUERY_ADD_CHARACTER_TO_DEBTORS_PRISM", { credit.at("character_id") });
|
|
}
|
|
}
|
|
db.execute("QUERY_UPDATE_CREDIT", { std::to_string(remainingAmount), std::to_string(falukantUserId) });
|
|
}
|
|
db.prepare("QUERY_CLEANUP_CREDITS", QUERY_CLEANUP_CREDITS);
|
|
db.execute("QUERY_CLEANUP_CREDITS");
|
|
}
|
|
|
|
double UserCharacterWorker::getCurrentMoney(int falukantUserId) {
|
|
ConnectionGuard g(pool); auto &db = g.get();
|
|
db.prepare("GET_CURRENT_MONEY", QUERY_GET_CURRENT_MONEY);
|
|
auto rows = db.execute("GET_CURRENT_MONEY", {std::to_string(falukantUserId)});
|
|
return rows.empty()? 0.0 : std::stod(rows.front().at("sum"));
|
|
}
|
|
|
|
double UserCharacterWorker::getHouseValue(int falukantUserId) {
|
|
ConnectionGuard g(pool); auto &db = g.get();
|
|
db.prepare("HOUSE_VALUE", QUERY_HOUSE_VALUE);
|
|
auto rows = db.execute("HOUSE_VALUE", {std::to_string(falukantUserId)});
|
|
return rows.empty()? 0.0 : std::stod(rows.front().at("sum"));
|
|
}
|
|
|
|
double UserCharacterWorker::getSettlementValue(int falukantUserId) {
|
|
ConnectionGuard g(pool); auto &db = g.get();
|
|
db.prepare("SETTLEMENT_VALUE", QUERY_SETTLEMENT_VALUE);
|
|
auto rows = db.execute("SETTLEMENT_VALUE", {std::to_string(falukantUserId)});
|
|
return rows.empty()? 0.0 : std::stod(rows.front().at("sum"));
|
|
}
|
|
|
|
double UserCharacterWorker::getInventoryValue(int falukantUserId) {
|
|
ConnectionGuard g(pool); auto &db = g.get();
|
|
db.prepare("INVENTORY_VALUE", QUERY_INVENTORY_VALUE);
|
|
auto rows = db.execute("INVENTORY_VALUE", {std::to_string(falukantUserId)});
|
|
return rows.empty()? 0.0 : std::stod(rows.front().at("sum"));
|
|
}
|
|
|
|
double UserCharacterWorker::getCreditDebt(int falukantUserId) {
|
|
ConnectionGuard guard(pool);
|
|
auto &db = guard.get();
|
|
|
|
db.prepare("CREDIT_DEBT", QUERY_CREDIT_DEBT);
|
|
auto rows = db.execute("CREDIT_DEBT", { std::to_string(falukantUserId) });
|
|
return rows.empty()
|
|
? 0.0
|
|
: std::stod(rows.front().at("sum"));
|
|
}
|
|
|
|
int UserCharacterWorker::getChildCount(int deceasedUserId) {
|
|
ConnectionGuard g(pool); auto &db = g.get();
|
|
db.prepare("COUNT_CHILDREN", QUERY_COUNT_CHILDREN);
|
|
auto rows = db.execute("COUNT_CHILDREN", {std::to_string(deceasedUserId)});
|
|
return rows.empty()? 0 : std::stoi(rows.front().at("cnt"));
|
|
}
|
|
|
|
double UserCharacterWorker::calculateNewMoney(int falukantUserId, bool hasHeir) {
|
|
if (!hasHeir) {
|
|
return 800.0;
|
|
}
|
|
double cash = getCurrentMoney(falukantUserId);
|
|
double houses = getHouseValue(falukantUserId);
|
|
double sets = getSettlementValue(falukantUserId);
|
|
double inv = getInventoryValue(falukantUserId);
|
|
double debt = getCreditDebt(falukantUserId);
|
|
double totalAssets = cash + houses + sets + inv - debt;
|
|
int childCount = getChildCount(falukantUserId);
|
|
bool single = (childCount <= 1);
|
|
double heirShare = single ? totalAssets : totalAssets * 0.8;
|
|
double net = heirShare - (houses + sets + inv + debt);
|
|
if (net <= 1000.0) {
|
|
return 1000.0;
|
|
}
|
|
return net;
|
|
}
|