Füge UndergroundWorker hinzu und implementiere Logik für unterirdische Aufgaben. Aktualisiere CMakeLists.txt, um neue Quell- und Header-Dateien einzuschließen. Verbessere die Fehlerbehandlung in der Datenbank und sende Benachrichtigungen nach bestimmten Ereignissen. Integriere Hilfsfunktionen zur sicheren Verarbeitung von Daten.
This commit is contained in:
committed by
Torsten (PC)
parent
1451225978
commit
23c07a3570
445
src/underground_worker.cpp
Normal file
445
src/underground_worker.cpp
Normal file
@@ -0,0 +1,445 @@
|
||||
#include "underground_worker.h"
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
UndergroundWorker::~UndergroundWorker() = default;
|
||||
|
||||
static std::mt19937& rng() {
|
||||
static thread_local std::mt19937 g{std::random_device{}()};
|
||||
return g;
|
||||
}
|
||||
|
||||
int UndergroundWorker::randomInt(int lo,int hi){
|
||||
std::uniform_int_distribution<int> d(lo,hi);
|
||||
return d(rng());
|
||||
}
|
||||
|
||||
long long UndergroundWorker::randomLL(long long lo,long long hi){
|
||||
std::uniform_int_distribution<long long> d(lo,hi);
|
||||
return d(rng());
|
||||
}
|
||||
|
||||
std::vector<size_t> UndergroundWorker::randomIndices(size_t n,size_t k){
|
||||
std::vector<size_t> idx(n);
|
||||
std::iota(idx.begin(),idx.end(),0);
|
||||
std::shuffle(idx.begin(),idx.end(),rng());
|
||||
if(k<idx.size()) idx.resize(k);
|
||||
return idx;
|
||||
}
|
||||
|
||||
void UndergroundWorker::run(){
|
||||
using namespace std::chrono;
|
||||
while(runningWorker){
|
||||
setCurrentStep("Process underground jobs");
|
||||
signalActivity();
|
||||
tick();
|
||||
setCurrentStep("Idle");
|
||||
for(int i=0;i<60 && runningWorker;++i){
|
||||
std::this_thread::sleep_for(seconds(1));
|
||||
signalActivity();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UndergroundWorker::tick(){
|
||||
setCurrentStep("Fetch pending underground jobs");
|
||||
ConnectionGuard g(pool);
|
||||
auto& db=g.get();
|
||||
db.prepare("UG_SELECT_PENDING",Q_SELECT_PENDING);
|
||||
db.prepare("UG_UPDATE_RESULT",Q_UPDATE_RESULT);
|
||||
const auto rows=db.execute("UG_SELECT_PENDING");
|
||||
for(const auto& r:rows){
|
||||
try{
|
||||
auto res=executeRow(r);
|
||||
int id=std::stoi(r.at("id"));
|
||||
updateResult(id,res);
|
||||
broker.publish(json{{"event","underground_processed"},{"id",id},{"type",r.at("underground_type")}}.dump());
|
||||
}catch(const std::exception& e){
|
||||
try{
|
||||
int id=std::stoi(r.at("id"));
|
||||
updateResult(id,json{{"status","error"},{"message",e.what()}});
|
||||
}catch(...){}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<UndergroundWorker::Row> UndergroundWorker::fetchPending(){
|
||||
ConnectionGuard g(pool);
|
||||
auto& db=g.get();
|
||||
db.prepare("UG_SELECT_PENDING",Q_SELECT_PENDING);
|
||||
return db.execute("UG_SELECT_PENDING");
|
||||
}
|
||||
|
||||
nlohmann::json UndergroundWorker::executeRow(const Row& r){
|
||||
int performerId=std::stoi(r.at("performer_id"));
|
||||
int victimId=std::stoi(r.at("victim_id"));
|
||||
std::string type=r.at("underground_type");
|
||||
std::string params=r.at("parameters");
|
||||
return handleTask(type,performerId,victimId,params);
|
||||
}
|
||||
|
||||
nlohmann::json UndergroundWorker::handleTask(const std::string& type,int performerId,int victimId,const std::string& paramsJson){
|
||||
json p; try{ p=json::parse(paramsJson);} catch(...){ p=json::object(); }
|
||||
if(type=="spyin") return spyIn(performerId,victimId,p);
|
||||
if(type=="assassin") return assassin(performerId,victimId,p);
|
||||
if(type=="sabotage") return sabotage(performerId,victimId,p);
|
||||
if(type=="corrupt_politician") return corruptPolitician(performerId,victimId,p);
|
||||
if(type=="rob") return rob(performerId,victimId,p);
|
||||
return {{"status","unknown_type"},{"type",type}};
|
||||
}
|
||||
|
||||
nlohmann::json UndergroundWorker::spyIn(int performerId,int victimId,const json& p){
|
||||
ConnectionGuard g(pool);
|
||||
auto& db=g.get();
|
||||
db.prepare("UG_SELECT_BY_PERFORMER",Q_SELECT_BY_PERFORMER);
|
||||
const auto rows = db.execute("UG_SELECT_BY_PERFORMER",{ std::to_string(victimId) });
|
||||
|
||||
json activities = json::array();
|
||||
for(const auto& r : rows){
|
||||
json params = json::object();
|
||||
try{ params = json::parse(r.at("parameters")); }catch(...){}
|
||||
json result = nullptr;
|
||||
auto it = r.find("result_text");
|
||||
if(it != r.end()){
|
||||
try{ result = json::parse(it->second); }catch(...){}
|
||||
}
|
||||
std::string status = "pending";
|
||||
if(result.is_object()){
|
||||
if(auto s = result.find("status"); s!=result.end() && s->is_string()) status = *s;
|
||||
else status = "done";
|
||||
}
|
||||
activities.push_back({
|
||||
{"id", std::stoi(r.at("id"))},
|
||||
{"type", r.at("underground_type")},
|
||||
{"performed_by", std::stoi(r.at("performer_id"))},
|
||||
{"victim_id", std::stoi(r.at("victim_id"))},
|
||||
{"created_at", r.at("created_at")},
|
||||
{"parameters", params},
|
||||
{"result", result},
|
||||
{"status", status}
|
||||
});
|
||||
}
|
||||
return {
|
||||
{"status","success"},
|
||||
{"action","spyin"},
|
||||
{"performer_id", performerId},
|
||||
{"victim_id", victimId},
|
||||
{"details", p},
|
||||
{"victim_illegal_activity_count", activities.size()},
|
||||
{"victim_illegal_activities", activities}
|
||||
};
|
||||
}
|
||||
|
||||
nlohmann::json UndergroundWorker::assassin(int performerId,int victimId,const json& p){
|
||||
ConnectionGuard g(pool);
|
||||
auto& db=g.get();
|
||||
db.prepare("UG_SELECT_CHAR_HEALTH",Q_SELECT_CHAR_HEALTH);
|
||||
db.prepare("UG_UPDATE_CHAR_HEALTH",Q_UPDATE_CHAR_HEALTH);
|
||||
const auto rows=db.execute("UG_SELECT_CHAR_HEALTH",{std::to_string(victimId)});
|
||||
if(rows.empty()) return {{"status","error"},{"action","assassin"},{"performer_id",performerId},{"victim_id",victimId},{"message","victim_not_found"},{"details",p}};
|
||||
int current=std::stoi(rows.front().at("health"));
|
||||
std::uniform_int_distribution<int> dist(0,current);
|
||||
int new_health=dist(rng());
|
||||
db.execute("UG_UPDATE_CHAR_HEALTH",{std::to_string(victimId),std::to_string(new_health)});
|
||||
return {{"status","success"},{"action","assassin"},{"performer_id",performerId},{"victim_id",victimId},{"details",p},{"previous_health",current},{"new_health",new_health},{"reduced_by",current-new_health}};
|
||||
}
|
||||
|
||||
nlohmann::json UndergroundWorker::sabotage(int performerId,int victimId,const json& p){
|
||||
const auto target=p.value("target",std::string{});
|
||||
if(target=="house") return sabotageHouse(performerId,victimId,p);
|
||||
if(target=="storage") return sabotageStorage(performerId,victimId,p);
|
||||
return {{"status","error"},{"action","sabotage"},{"message","unknown_target"},{"performer_id",performerId},{"victim_id",victimId},{"details",p}};
|
||||
}
|
||||
|
||||
int UndergroundWorker::getUserIdForCharacter(int characterId){
|
||||
ConnectionGuard g(pool);
|
||||
auto& db=g.get();
|
||||
db.prepare("UG_SELECT_CHAR_USER",Q_SELECT_CHAR_USER);
|
||||
const auto r=db.execute("UG_SELECT_CHAR_USER",{std::to_string(characterId)});
|
||||
if(r.empty()) return -1;
|
||||
return std::stoi(r.front().at("user_id"));
|
||||
}
|
||||
|
||||
std::optional<UndergroundWorker::HouseConditions> UndergroundWorker::getHouseByUser(int userId){
|
||||
ConnectionGuard g(pool);
|
||||
auto& db=g.get();
|
||||
db.prepare("UG_SELECT_HOUSE_BY_USER",Q_SELECT_HOUSE_BY_USER);
|
||||
const auto r=db.execute("UG_SELECT_HOUSE_BY_USER",{std::to_string(userId)});
|
||||
if(r.empty()) return std::nullopt;
|
||||
HouseConditions h{
|
||||
std::stoi(r.front().at("id")),
|
||||
std::stoi(r.front().at("roof_condition")),
|
||||
std::stoi(r.front().at("floor_condition")),
|
||||
std::stoi(r.front().at("wall_condition")),
|
||||
std::stoi(r.front().at("window_condition"))
|
||||
};
|
||||
return h;
|
||||
}
|
||||
|
||||
void UndergroundWorker::updateHouse(const HouseConditions& h){
|
||||
ConnectionGuard g(pool);
|
||||
auto& db=g.get();
|
||||
db.prepare("UG_UPDATE_HOUSE",Q_UPDATE_HOUSE);
|
||||
db.execute("UG_UPDATE_HOUSE",{
|
||||
std::to_string(h.id),
|
||||
std::to_string(std::clamp(h.roof,0,100)),
|
||||
std::to_string(std::clamp(h.floor,0,100)),
|
||||
std::to_string(std::clamp(h.wall,0,100)),
|
||||
std::to_string(std::clamp(h.windowc,0,100))
|
||||
});
|
||||
}
|
||||
|
||||
nlohmann::json UndergroundWorker::sabotageHouse(int performerId,int victimId,const json& p){
|
||||
int userId=getUserIdForCharacter(victimId);
|
||||
if(userId<0) return {{"status","error"},{"action","sabotage"},{"target","house"},{"message","victim_not_found"},{"performer_id",performerId},{"victim_id",victimId},{"details",p}};
|
||||
auto hopt=getHouseByUser(userId);
|
||||
if(!hopt) return {{"status","error"},{"action","sabotage"},{"target","house"},{"message","house_not_found"},{"performer_id",performerId},{"victim_id",victimId},{"details",p}};
|
||||
auto h=*hopt;
|
||||
|
||||
std::vector<std::string> allow;
|
||||
if(p.contains("conditions") && p["conditions"].is_array())
|
||||
for(const auto& s:p["conditions"]) if(s.is_string()) allow.push_back(s.get<std::string>());
|
||||
|
||||
std::vector<std::pair<std::string,int*>> fields={
|
||||
{"roof_condition",&h.roof},
|
||||
{"floor_condition",&h.floor},
|
||||
{"wall_condition",&h.wall},
|
||||
{"window_condition",&h.windowc}
|
||||
};
|
||||
std::vector<std::pair<std::string,int*>> pool;
|
||||
for(auto& f:fields) if(allow.empty() || std::find(allow.begin(),allow.end(),f.first)!=allow.end()) pool.push_back(f);
|
||||
if(pool.empty()) return {{"status","error"},{"action","sabotage"},{"target","house"},{"message","no_conditions_selected"},{"performer_id",performerId},{"victim_id",victimId},{"details",p}};
|
||||
|
||||
size_t k=static_cast<size_t>(randomInt(1,(int)pool.size()));
|
||||
std::vector<size_t> picks=randomIndices(pool.size(),k);
|
||||
|
||||
json changed=json::array();
|
||||
for(size_t i: picks){
|
||||
int& cur=*pool[i].second;
|
||||
if(cur>0){
|
||||
int red=randomInt(1,cur);
|
||||
cur=std::clamp(cur-red,0,100);
|
||||
}
|
||||
changed.push_back(pool[i].first);
|
||||
}
|
||||
|
||||
updateHouse(h);
|
||||
return {
|
||||
{"status","success"},
|
||||
{"action","sabotage"},
|
||||
{"target","house"},
|
||||
{"performer_id",performerId},
|
||||
{"victim_id",victimId},
|
||||
{"details",p},
|
||||
{"changed_conditions",changed},
|
||||
{"new_conditions",{
|
||||
{"roof_condition",h.roof},
|
||||
{"floor_condition",h.floor},
|
||||
{"wall_condition",h.wall},
|
||||
{"window_condition",h.windowc}
|
||||
}}
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<UndergroundWorker::Row> UndergroundWorker::selectStockByBranch(int branchId){
|
||||
ConnectionGuard g(pool);
|
||||
auto& db=g.get();
|
||||
db.prepare("UG_SELECT_STOCK_BY_BRANCH",Q_SELECT_STOCK_BY_BRANCH);
|
||||
return db.execute("UG_SELECT_STOCK_BY_BRANCH",{std::to_string(branchId)});
|
||||
}
|
||||
|
||||
std::vector<UndergroundWorker::Row> UndergroundWorker::filterByStockTypes(const std::vector<Row>& rows,const std::vector<int>& allowed){
|
||||
if(allowed.empty()) return rows;
|
||||
std::vector<Row> out;
|
||||
out.reserve(rows.size());
|
||||
for(const auto& r:rows){
|
||||
int t=std::stoi(r.at("stock_type_id"));
|
||||
if(std::find(allowed.begin(),allowed.end(),t)!=allowed.end()) out.push_back(r);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void UndergroundWorker::updateStockQty(int id,long long qty){
|
||||
ConnectionGuard g(pool);
|
||||
auto& db=g.get();
|
||||
db.prepare("UG_UPDATE_STOCK_QTY",Q_UPDATE_STOCK_QTY);
|
||||
db.execute("UG_UPDATE_STOCK_QTY",{std::to_string(id),std::to_string(qty)});
|
||||
}
|
||||
|
||||
nlohmann::json UndergroundWorker::sabotageStorage(int performerId,int victimId,const json& p){
|
||||
if(!p.contains("branch_id") || !p["branch_id"].is_number_integer())
|
||||
return {{"status","error"},{"action","sabotage"},{"target","storage"},{"message","branch_id_required"},{"performer_id",performerId},{"victim_id",victimId},{"details",p}};
|
||||
int branchId=p["branch_id"].get<int>();
|
||||
|
||||
std::vector<int> allowed;
|
||||
if(p.contains("stock_type_ids") && p["stock_type_ids"].is_array())
|
||||
for(const auto& v:p["stock_type_ids"]) if(v.is_number_integer()) allowed.push_back(v.get<int>());
|
||||
|
||||
auto rows=filterByStockTypes(selectStockByBranch(branchId),allowed);
|
||||
if(rows.empty()) return {{"status","success"},{"action","sabotage"},{"target","storage"},{"performer_id",performerId},{"victim_id",victimId},{"details",p},{"removed_total",0},{"affected_rows",json::array()}};
|
||||
|
||||
long long total=0;
|
||||
for(const auto& r:rows) total+=std::stoll(r.at("quantity"));
|
||||
if(total<=0) return {{"status","success"},{"action","sabotage"},{"target","storage"},{"performer_id",performerId},{"victim_id",victimId},{"details",p},{"removed_total",0},{"affected_rows",json::array()}};
|
||||
|
||||
long long cap=total/4;
|
||||
if(cap<=0) return {{"status","success"},{"action","sabotage"},{"target","storage"},{"performer_id",performerId},{"victim_id",victimId},{"details",p},{"removed_total",0},{"affected_rows",json::array()}};
|
||||
|
||||
long long to_remove=randomLL(1,cap);
|
||||
std::shuffle(rows.begin(),rows.end(),rng());
|
||||
|
||||
json affected=json::array();
|
||||
for(const auto& r:rows){
|
||||
if(to_remove==0) break;
|
||||
int id=std::stoi(r.at("id"));
|
||||
long long q=std::stoll(r.at("quantity"));
|
||||
if(q<=0) continue;
|
||||
long long take=randomLL(1,std::min(q,to_remove));
|
||||
long long newq=q-take;
|
||||
updateStockQty(id,newq);
|
||||
to_remove-=take;
|
||||
affected.push_back({{"id",id},{"stock_type_id",std::stoi(r.at("stock_type_id"))},{"previous_quantity",q},{"new_quantity",newq},{"removed",take}});
|
||||
}
|
||||
|
||||
long long removed=0;
|
||||
for(const auto& a:affected) removed+=a.at("removed").get<long long>();
|
||||
|
||||
return {
|
||||
{"status","success"},
|
||||
{"action","sabotage"},
|
||||
{"target","storage"},
|
||||
{"performer_id",performerId},
|
||||
{"victim_id",victimId},
|
||||
{"details",p},
|
||||
{"removed_total",removed},
|
||||
{"affected_rows",affected}
|
||||
};
|
||||
}
|
||||
|
||||
nlohmann::json UndergroundWorker::corruptPolitician(int performerId,int victimId,const json& p){
|
||||
return {{"status","success"},{"action","corrupt_politician"},{"performer_id",performerId},{"victim_id",victimId},{"details",p}};
|
||||
}
|
||||
|
||||
nlohmann::json UndergroundWorker::rob(int performerId,int victimId,const json& p){
|
||||
int userId=getUserIdForCharacter(victimId);
|
||||
if(userId<0) return {{"status","error"},{"action","rob"},{"message","victim_not_found"},{"performer_id",performerId},{"victim_id",victimId},{"details",p}};
|
||||
|
||||
ConnectionGuard g(pool);
|
||||
auto& db=g.get();
|
||||
db.prepare("UG_SELECT_FALUKANT_USER",Q_SELECT_FALUKANT_USER);
|
||||
const auto fu=db.execute("UG_SELECT_FALUKANT_USER",{std::to_string(userId)});
|
||||
if(fu.empty()) return {{"status","error"},{"action","rob"},{"message","falukant_user_not_found"},{"performer_id",performerId},{"victim_id",victimId},{"details",p}};
|
||||
|
||||
int falukantUserId=std::stoi(fu.front().at("id"));
|
||||
double money=std::stod(fu.front().at("money"));
|
||||
int defaultBranch=std::stoi(fu.front().at("main_branch_region_id"));
|
||||
|
||||
bool stealGoods = (randomInt(0,1)==1);
|
||||
if(stealGoods){
|
||||
int branchId = p.contains("branch_id") && p["branch_id"].is_number_integer()
|
||||
? p["branch_id"].get<int>()
|
||||
: defaultBranch;
|
||||
|
||||
if(branchId<=0){
|
||||
return {{"status","success"},{"action","rob"},{"mode","goods"},{"performer_id",performerId},{"victim_id",victimId},{"details",p},{"removed_total",0},{"affected_rows",json::array()}};
|
||||
}
|
||||
|
||||
auto rows = selectStockByBranch(branchId);
|
||||
if(rows.empty()){
|
||||
return {{"status","success"},{"action","rob"},{"mode","goods"},{"performer_id",performerId},{"victim_id",victimId},{"details",p},{"removed_total",0},{"affected_rows",json::array()}};
|
||||
}
|
||||
|
||||
long long total=0;
|
||||
for(const auto& r:rows) total+=std::stoll(r.at("quantity"));
|
||||
if(total<=0){
|
||||
return {{"status","success"},{"action","rob"},{"mode","goods"},{"performer_id",performerId},{"victim_id",victimId},{"details",p},{"removed_total",0},{"affected_rows",json::array()}};
|
||||
}
|
||||
|
||||
long long cap = std::max<long long>(1, total/2);
|
||||
long long to_remove = randomLL(1, cap);
|
||||
|
||||
std::shuffle(rows.begin(),rows.end(),rng());
|
||||
json affected = json::array();
|
||||
for(const auto& r:rows){
|
||||
if(to_remove==0) break;
|
||||
int id=std::stoi(r.at("id"));
|
||||
long long q=std::stoll(r.at("quantity"));
|
||||
if(q<=0) continue;
|
||||
long long take=randomLL(1,std::min(q,to_remove));
|
||||
long long newq=q-take;
|
||||
updateStockQty(id,newq);
|
||||
to_remove-=take;
|
||||
affected.push_back({
|
||||
{"id",id},
|
||||
{"stock_type_id",std::stoi(r.at("stock_type_id"))},
|
||||
{"previous_quantity",q},
|
||||
{"new_quantity",newq},
|
||||
{"removed",take}
|
||||
});
|
||||
}
|
||||
|
||||
long long removed=0;
|
||||
for(const auto& a:affected) removed+=a.at("removed").get<long long>();
|
||||
|
||||
return {
|
||||
{"status","success"},
|
||||
{"action","rob"},
|
||||
{"mode","goods"},
|
||||
{"performer_id",performerId},
|
||||
{"victim_id",victimId},
|
||||
{"details",p},
|
||||
{"removed_total",removed},
|
||||
{"affected_rows",affected}
|
||||
};
|
||||
} else {
|
||||
if(money<=0.0){
|
||||
return {{"status","success"},{"action","rob"},{"mode","money"},{"performer_id",performerId},{"victim_id",victimId},{"details",p},{"stolen",0.0},{"balance_before",0.0},{"balance_after",0.0}};
|
||||
}
|
||||
|
||||
double rate = randomDouble(0.0,0.18);
|
||||
double amount = std::floor(money * rate * 100.0 + 0.5) / 100.0;
|
||||
if(amount < 0.01) amount = 0.01;
|
||||
if(amount > money) amount = money;
|
||||
|
||||
json msg = {
|
||||
{"event","money_changed"},
|
||||
{"reason","robbery"},
|
||||
{"delta",-amount},
|
||||
{"performer_id",performerId},
|
||||
{"victim_id",victimId}
|
||||
};
|
||||
changeFalukantUserMoney(falukantUserId, -amount, "robbery", msg);
|
||||
|
||||
double after = std::floor((money - amount) * 100.0 + 0.5)/100.0;
|
||||
|
||||
return {
|
||||
{"status","success"},
|
||||
{"action","rob"},
|
||||
{"mode","money"},
|
||||
{"performer_id",performerId},
|
||||
{"victim_id",victimId},
|
||||
{"details",p},
|
||||
{"stolen",amount},
|
||||
{"rate",rate},
|
||||
{"balance_before",money},
|
||||
{"balance_after",after}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void UndergroundWorker::updateResult(int id,const nlohmann::json& result){
|
||||
ConnectionGuard g(pool);
|
||||
auto& db=g.get();
|
||||
db.prepare("UG_UPDATE_RESULT",Q_UPDATE_RESULT);
|
||||
db.execute("UG_UPDATE_RESULT",{std::to_string(id),result.dump()});
|
||||
}
|
||||
|
||||
double UndergroundWorker::randomDouble(double lo,double hi){
|
||||
std::uniform_real_distribution<double> d(lo,hi);
|
||||
return d(rng());
|
||||
}
|
||||
Reference in New Issue
Block a user