stabilized app
This commit is contained in:
committed by
Torsten (PC)
parent
51fd9fcd13
commit
1451225978
@@ -1,22 +1,33 @@
|
|||||||
cmake_minimum_required(VERSION 3.20)
|
cmake_minimum_required(VERSION 3.20)
|
||||||
project(YourPartDaemon VERSION 1.0 LANGUAGES CXX)
|
project(YourPartDaemon VERSION 1.0 LANGUAGES CXX)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
# C++ Standard and Compiler Settings
|
||||||
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_C_COMPILER "gcc-13")
|
# Explicitly set compiler versions for Tumbleweed
|
||||||
set(CMAKE_CPP_COMPILER "g++-13")
|
set(CMAKE_C_COMPILER gcc-15)
|
||||||
|
set(CMAKE_CXX_COMPILER g++-15)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto=auto")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto=auto")
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -flto")
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -flto")
|
||||||
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
|
|
||||||
|
# Include /usr/local if needed
|
||||||
|
list(APPEND CMAKE_PREFIX_PATH /usr/local)
|
||||||
|
|
||||||
|
# Find libwebsockets via pkg-config
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(LWS REQUIRED libwebsockets)
|
||||||
|
|
||||||
|
# Find other dependencies
|
||||||
find_package(PostgreSQL REQUIRED)
|
find_package(PostgreSQL REQUIRED)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
find_package(nlohmann_json CONFIG REQUIRED)
|
||||||
|
|
||||||
|
# PostgreSQL C++ libpqxx
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(LIBPQXX REQUIRED libpqxx)
|
pkg_check_modules(LIBPQXX REQUIRED libpqxx)
|
||||||
|
|
||||||
include_directories(${LIBPQXX_INCLUDE_DIRS})
|
# Project sources and headers
|
||||||
link_directories(${LIBPQXX_LIBRARY_DIRS})
|
|
||||||
add_definitions(${LIBPQXX_CFLAGS_OTHER})
|
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/config.cpp
|
src/config.cpp
|
||||||
@@ -26,6 +37,12 @@ set(SOURCES
|
|||||||
src/produce_worker.cpp
|
src/produce_worker.cpp
|
||||||
src/message_broker.cpp
|
src/message_broker.cpp
|
||||||
src/websocket_server.cpp
|
src/websocket_server.cpp
|
||||||
|
src/stockagemanager.cpp
|
||||||
|
src/director_worker.cpp
|
||||||
|
src/valuerecalculationworker.cpp
|
||||||
|
src/usercharacterworker.cpp
|
||||||
|
src/houseworker.cpp
|
||||||
|
src/politics_worker.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
@@ -37,13 +54,34 @@ set(HEADERS
|
|||||||
src/produce_worker.h
|
src/produce_worker.h
|
||||||
src/message_broker.h
|
src/message_broker.h
|
||||||
src/websocket_server.h
|
src/websocket_server.h
|
||||||
|
src/stockagemanager.h
|
||||||
|
src/director_worker.h
|
||||||
|
src/valuerecalculationworker.h
|
||||||
|
src/usercharacterworker.h
|
||||||
|
src/houseworker.h
|
||||||
|
src/politics_worker.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Define executable target
|
||||||
add_executable(yourpart-daemon ${SOURCES} ${HEADERS})
|
add_executable(yourpart-daemon ${SOURCES} ${HEADERS})
|
||||||
find_package(nlohmann_json CONFIG REQUIRED)
|
|
||||||
|
|
||||||
target_include_directories(yourpart-daemon PRIVATE ${PostgreSQL_INCLUDE_DIRS})
|
# Include directories
|
||||||
target_link_libraries(yourpart-daemon PRIVATE ${PostgreSQL_LIBRARIES} Threads::Threads z ssl crypto ${CMAKE_SOURCE_DIR}/lib/uSockets.a ${LIBPQXX_LIBRARIES})
|
target_include_directories(yourpart-daemon PRIVATE
|
||||||
|
${PostgreSQL_INCLUDE_DIRS}
|
||||||
|
${LIBPQXX_INCLUDE_DIRS}
|
||||||
|
${LWS_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Link libraries
|
||||||
|
target_link_libraries(yourpart-daemon PRIVATE
|
||||||
|
${PostgreSQL_LIBRARIES}
|
||||||
|
Threads::Threads
|
||||||
|
z ssl crypto
|
||||||
|
${LIBPQXX_LIBRARIES}
|
||||||
|
${LWS_LIBRARIES}
|
||||||
|
nlohmann_json::nlohmann_json
|
||||||
|
)
|
||||||
|
|
||||||
|
# Installation rules
|
||||||
install(TARGETS yourpart-daemon DESTINATION /usr/local/bin)
|
install(TARGETS yourpart-daemon DESTINATION /usr/local/bin)
|
||||||
install(FILES daemon.conf DESTINATION /etc/yourpart/)
|
install(FILES daemon.conf DESTINATION /etc/yourpart/)
|
||||||
|
|||||||
414
CMakeLists.txt.user
Normal file
414
CMakeLists.txt.user
Normal file
@@ -0,0 +1,414 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE QtCreatorProject>
|
||||||
|
<!-- Written by QtCreator 17.0.0, 2025-07-21T14:02:18. -->
|
||||||
|
<qtcreator>
|
||||||
|
<data>
|
||||||
|
<variable>EnvironmentId</variable>
|
||||||
|
<value type="QByteArray">{551ef6b3-a39b-43e2-9ee3-ad56e19ff4f4}</value>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.ActiveTarget</variable>
|
||||||
|
<value type="qlonglong">0</value>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.EditorSettings</variable>
|
||||||
|
<valuemap type="QVariantMap">
|
||||||
|
<value type="bool" key="EditorConfiguration.AutoDetect">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
|
||||||
|
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
|
||||||
|
<value type="QString" key="language">Cpp</value>
|
||||||
|
<valuemap type="QVariantMap" key="value">
|
||||||
|
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
|
||||||
|
</valuemap>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
|
||||||
|
<value type="QString" key="language">QmlJS</value>
|
||||||
|
<valuemap type="QVariantMap" key="value">
|
||||||
|
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
|
||||||
|
</valuemap>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
|
||||||
|
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
|
||||||
|
<value type="int" key="EditorConfiguration.IndentSize">4</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
|
||||||
|
<value type="int" key="EditorConfiguration.LineEndingBehavior">0</value>
|
||||||
|
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
|
||||||
|
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
|
||||||
|
<value type="int" key="EditorConfiguration.PreferAfterWhitespaceComments">0</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
|
||||||
|
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">2</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
|
||||||
|
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
|
||||||
|
<value type="int" key="EditorConfiguration.TabSize">8</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
|
||||||
|
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
|
||||||
|
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
|
||||||
|
</valuemap>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.PluginSettings</variable>
|
||||||
|
<valuemap type="QVariantMap">
|
||||||
|
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
|
||||||
|
<value type="bool" key="AutoTest.Framework.Boost">true</value>
|
||||||
|
<value type="bool" key="AutoTest.Framework.CTest">false</value>
|
||||||
|
<value type="bool" key="AutoTest.Framework.Catch">true</value>
|
||||||
|
<value type="bool" key="AutoTest.Framework.GTest">true</value>
|
||||||
|
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
|
||||||
|
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="bool" key="AutoTest.ApplyFilter">false</value>
|
||||||
|
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
|
||||||
|
<valuelist type="QVariantList" key="AutoTest.PathFilters"/>
|
||||||
|
<value type="int" key="AutoTest.RunAfterBuild">0</value>
|
||||||
|
<value type="bool" key="AutoTest.UseGlobal">true</value>
|
||||||
|
<valuemap type="QVariantMap" key="ClangTools">
|
||||||
|
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
|
||||||
|
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
|
||||||
|
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
|
||||||
|
<value type="int" key="ClangTools.ParallelJobs">8</value>
|
||||||
|
<value type="bool" key="ClangTools.PreferConfigFile">true</value>
|
||||||
|
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
|
||||||
|
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
|
||||||
|
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
|
||||||
|
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
|
||||||
|
</valuemap>
|
||||||
|
</valuemap>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.Target.0</variable>
|
||||||
|
<valuemap type="QVariantMap">
|
||||||
|
<value type="QString" key="DeviceType">Desktop</value>
|
||||||
|
<value type="bool" key="HasPerBcDcs">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Importiertes Kit</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Importiertes Kit</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{78ff90a3-f672-45c2-ad08-343b0923896f}</value>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
||||||
|
<value type="QString" key="CMake.Build.Type">Debug</value>
|
||||||
|
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
|
||||||
|
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
|
||||||
|
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
|
||||||
|
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_BUILD_TYPE:STRING=Release
|
||||||
|
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
|
||||||
|
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
|
||||||
|
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
|
||||||
|
-DCMAKE_GENERATOR:STRING=Unix Makefiles
|
||||||
|
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
|
||||||
|
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
|
||||||
|
-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/torsten/Programs/yourpart-daemon/build/</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||||
|
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
|
||||||
|
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
|
||||||
|
<value type="QString">all</value>
|
||||||
|
</valuelist>
|
||||||
|
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
|
||||||
|
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||||
|
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
|
||||||
|
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
|
||||||
|
<value type="QString">clean</value>
|
||||||
|
</valuelist>
|
||||||
|
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
|
||||||
|
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.1">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||||
|
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
|
||||||
|
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
|
||||||
|
<value type="QString"></value>
|
||||||
|
</valuelist>
|
||||||
|
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
|
||||||
|
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.CMakePackageStep</value>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
|
||||||
|
<value type="QString" key="ApplicationManagerPlugin.Deploy.InstallPackageStep.Arguments">install-package --acknowledge</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Application Manager-Paket installieren</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.InstallPackageStep</value>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedFiles"/>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedHosts"/>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedRemotePaths"/>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedSysroots"/>
|
||||||
|
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedLocalTimes"/>
|
||||||
|
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedRemoteTimes"/>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.Configuration</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">2</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||||
|
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||||
|
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||||
|
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||||
|
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||||
|
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||||
|
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||||
|
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">yourpart-daemon</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeRunConfiguration.</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">yourpart-daemon</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||||
|
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/torsten/Programs/yourpart-daemon/build</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
|
||||||
|
<value type="QString" key="CMake.Build.Type">Debug</value>
|
||||||
|
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
|
||||||
|
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
|
||||||
|
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
|
||||||
|
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_BUILD_TYPE:STRING=Debug
|
||||||
|
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
|
||||||
|
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
|
||||||
|
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
|
||||||
|
-DCMAKE_GENERATOR:STRING=Unix Makefiles
|
||||||
|
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
|
||||||
|
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
|
||||||
|
-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON</value>
|
||||||
|
<value type="QString" key="CMake.Source.Directory">/mnt/share/torsten/Programs/yourpart-daemon</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/torsten/Programs/yourpart-daemon/build</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||||
|
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
|
||||||
|
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
|
||||||
|
<value type="QString">all</value>
|
||||||
|
</valuelist>
|
||||||
|
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
|
||||||
|
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||||
|
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
|
||||||
|
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
|
||||||
|
<value type="QString">clean</value>
|
||||||
|
</valuelist>
|
||||||
|
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
|
||||||
|
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug (importiert)</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">-1</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.1">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||||
|
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
|
||||||
|
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
|
||||||
|
<value type="QString">install</value>
|
||||||
|
</valuelist>
|
||||||
|
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
|
||||||
|
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.CMakePackageStep</value>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
|
||||||
|
<value type="QString" key="ApplicationManagerPlugin.Deploy.InstallPackageStep.Arguments">install-package --acknowledge</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Application Manager-Paket installieren</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.InstallPackageStep</value>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedFiles"/>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedHosts"/>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedRemotePaths"/>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedSysroots"/>
|
||||||
|
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedLocalTimes"/>
|
||||||
|
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedRemoteTimes"/>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.Configuration</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">2</value>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">0</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">2</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.1">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||||
|
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
|
||||||
|
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
|
||||||
|
<value type="QString"></value>
|
||||||
|
</valuelist>
|
||||||
|
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
|
||||||
|
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.CMakePackageStep</value>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
|
||||||
|
<value type="QString" key="ApplicationManagerPlugin.Deploy.InstallPackageStep.Arguments">install-package --acknowledge</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Application Manager-Paket installieren</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.InstallPackageStep</value>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedFiles"/>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedHosts"/>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedRemotePaths"/>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedSysroots"/>
|
||||||
|
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedLocalTimes"/>
|
||||||
|
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedRemoteTimes"/>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.Configuration</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">2</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||||
|
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||||
|
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||||
|
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||||
|
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||||
|
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||||
|
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||||
|
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">yourpart-daemon</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeRunConfiguration.</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">yourpart-daemon</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||||
|
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/torsten/Programs/yourpart-daemon/build</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||||
|
</valuemap>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||||
|
<value type="qlonglong">1</value>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
|
||||||
|
<value type="int">22</value>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>Version</variable>
|
||||||
|
<value type="int">22</value>
|
||||||
|
</data>
|
||||||
|
</qtcreator>
|
||||||
205
CMakeLists.txt.user.d36652f
Normal file
205
CMakeLists.txt.user.d36652f
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE QtCreatorProject>
|
||||||
|
<!-- Written by QtCreator 12.0.2, 2025-07-18T07:45:58. -->
|
||||||
|
<qtcreator>
|
||||||
|
<data>
|
||||||
|
<variable>EnvironmentId</variable>
|
||||||
|
<value type="QByteArray">{d36652ff-969b-426b-a63f-1edd325096c5}</value>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.ActiveTarget</variable>
|
||||||
|
<value type="qlonglong">0</value>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.EditorSettings</variable>
|
||||||
|
<valuemap type="QVariantMap">
|
||||||
|
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
|
||||||
|
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
|
||||||
|
<value type="QString" key="language">Cpp</value>
|
||||||
|
<valuemap type="QVariantMap" key="value">
|
||||||
|
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
|
||||||
|
</valuemap>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
|
||||||
|
<value type="QString" key="language">QmlJS</value>
|
||||||
|
<valuemap type="QVariantMap" key="value">
|
||||||
|
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
|
||||||
|
</valuemap>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
|
||||||
|
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
|
||||||
|
<value type="int" key="EditorConfiguration.IndentSize">4</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
|
||||||
|
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
|
||||||
|
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
|
||||||
|
<value type="int" key="EditorConfiguration.PreferAfterWhitespaceComments">0</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
|
||||||
|
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
|
||||||
|
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
|
||||||
|
<value type="int" key="EditorConfiguration.TabSize">8</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
|
||||||
|
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
|
||||||
|
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
|
||||||
|
</valuemap>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.PluginSettings</variable>
|
||||||
|
<valuemap type="QVariantMap">
|
||||||
|
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
|
||||||
|
<value type="bool" key="AutoTest.Framework.Boost">true</value>
|
||||||
|
<value type="bool" key="AutoTest.Framework.CTest">false</value>
|
||||||
|
<value type="bool" key="AutoTest.Framework.Catch">true</value>
|
||||||
|
<value type="bool" key="AutoTest.Framework.GTest">true</value>
|
||||||
|
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
|
||||||
|
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
|
||||||
|
<value type="int" key="AutoTest.RunAfterBuild">0</value>
|
||||||
|
<value type="bool" key="AutoTest.UseGlobal">true</value>
|
||||||
|
<valuemap type="QVariantMap" key="ClangTools">
|
||||||
|
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
|
||||||
|
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
|
||||||
|
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
|
||||||
|
<value type="int" key="ClangTools.ParallelJobs">8</value>
|
||||||
|
<value type="bool" key="ClangTools.PreferConfigFile">true</value>
|
||||||
|
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
|
||||||
|
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
|
||||||
|
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
|
||||||
|
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="CppEditor.QuickFix">
|
||||||
|
<value type="bool" key="UseGlobalSettings">true</value>
|
||||||
|
</valuemap>
|
||||||
|
</valuemap>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.Target.0</variable>
|
||||||
|
<valuemap type="QVariantMap">
|
||||||
|
<value type="QString" key="DeviceType">Desktop</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Importiertes Kit</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Importiertes Kit</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{3c6cfc13-714d-4db1-bd45-b9794643cc67}</value>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
||||||
|
<value type="QString" key="CMake.Build.Type">Debug</value>
|
||||||
|
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
|
||||||
|
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
|
||||||
|
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
|
||||||
|
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Unix Makefiles
|
||||||
|
-DCMAKE_BUILD_TYPE:STRING=Build
|
||||||
|
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
|
||||||
|
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
|
||||||
|
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
|
||||||
|
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
|
||||||
|
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}</value>
|
||||||
|
<value type="QString" key="CMake.Source.Directory">/home/torsten/Programs/yourpart-daemon</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/torsten/Programs/yourpart-daemon/build</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||||
|
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
|
||||||
|
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
|
||||||
|
<value type="QString">all</value>
|
||||||
|
</valuelist>
|
||||||
|
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
|
||||||
|
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||||
|
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
|
||||||
|
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
|
||||||
|
<value type="QString">clean</value>
|
||||||
|
</valuelist>
|
||||||
|
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
|
||||||
|
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||||
|
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||||
|
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">/usr/bin/valgrind</value>
|
||||||
|
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||||
|
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||||
|
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||||
|
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">yourpart-daemon</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeRunConfiguration.yourpart-daemon</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">yourpart-daemon</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||||
|
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/torsten/Programs/yourpart-daemon/build</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||||
|
</valuemap>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||||
|
<value type="qlonglong">1</value>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
|
||||||
|
<value type="int">22</value>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>Version</variable>
|
||||||
|
<value type="int">22</value>
|
||||||
|
</data>
|
||||||
|
</qtcreator>
|
||||||
@@ -3,15 +3,21 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
CharacterCreationWorker::CharacterCreationWorker(ConnectionPool &pool, MessageBroker &broker)
|
CharacterCreationWorker::CharacterCreationWorker(ConnectionPool &pool, MessageBroker &broker)
|
||||||
: Worker(pool, broker, "CharacterCreationWorker")
|
: Worker(pool, broker, "CharacterCreationWorker"),
|
||||||
, gen(std::random_device{}())
|
gen(std::random_device{}()),
|
||||||
, dist(2, 3)
|
dist(2, 3),
|
||||||
{
|
deathCheckRunning(true),
|
||||||
|
deathThread(&CharacterCreationWorker::monitorCharacterDeaths, this) {
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterCreationWorker::~CharacterCreationWorker() {
|
CharacterCreationWorker::~CharacterCreationWorker() {
|
||||||
|
deathCheckRunning.store(false);
|
||||||
|
if (deathThread.joinable()) {
|
||||||
|
deathThread.join();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterCreationWorker::run() {
|
void CharacterCreationWorker::run() {
|
||||||
@@ -38,22 +44,17 @@ bool CharacterCreationWorker::isTodayCharacterCreated() {
|
|||||||
auto &db = connGuard.get();
|
auto &db = connGuard.get();
|
||||||
setCurrentStep("Execute Query");
|
setCurrentStep("Execute Query");
|
||||||
auto results = db.query(QUERY_IS_PREVIOUS_DAY_CHARACTER_CREATED);
|
auto results = db.query(QUERY_IS_PREVIOUS_DAY_CHARACTER_CREATED);
|
||||||
if (!results.empty()) {
|
return !results.empty();
|
||||||
std::string created_at_str = results[0].at("created_at");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
std::cerr << "[CharacterCreationWorker] Fehler in isTodayCharacterCreated: "
|
std::cerr << "[CharacterCreationWorker] Fehler in isTodayCharacterCreated: " << e.what() << std::endl;
|
||||||
<< e.what() << std::endl;
|
return false;
|
||||||
}
|
}
|
||||||
setCurrentStep("No previous day character found");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterCreationWorker::createCharactersForToday() {
|
void CharacterCreationWorker::createCharactersForToday() {
|
||||||
loadNames();
|
loadNames();
|
||||||
if (first_name_cache.empty() || last_name_cache.empty()) {
|
if (first_name_cache.empty() || last_name_cache.empty()) {
|
||||||
std::cerr << "Fehler: Namen konnten nicht geladen werden." << std::endl;
|
std::cerr << "[CharacterCreationWorker] Fehler: Namen konnten nicht geladen werden." << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +68,7 @@ void CharacterCreationWorker::createCharactersForRegion(int region_id) {
|
|||||||
std::vector<int> nobility_stands = {1, 2, 3};
|
std::vector<int> nobility_stands = {1, 2, 3};
|
||||||
std::vector<std::string> genders = {"male", "female"};
|
std::vector<std::string> genders = {"male", "female"};
|
||||||
for (auto nobility : nobility_stands) {
|
for (auto nobility : nobility_stands) {
|
||||||
for (auto &gender : genders) {
|
for (const auto &gender : genders) {
|
||||||
int num_chars = dist(gen);
|
int num_chars = dist(gen);
|
||||||
for (int i = 0; i < num_chars; ++i) {
|
for (int i = 0; i < num_chars; ++i) {
|
||||||
createCharacter(region_id, gender, nobility);
|
createCharacter(region_id, gender, nobility);
|
||||||
@@ -76,9 +77,7 @@ void CharacterCreationWorker::createCharactersForRegion(int region_id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterCreationWorker::createCharacter(int region_id,
|
void CharacterCreationWorker::createCharacter(int region_id, const std::string &gender, int title_of_nobility) {
|
||||||
const std::string &gender,
|
|
||||||
int title_of_nobility) {
|
|
||||||
int first_name_id = getRandomFromSet(first_name_cache[gender]);
|
int first_name_id = getRandomFromSet(first_name_cache[gender]);
|
||||||
if (first_name_id == -1) {
|
if (first_name_id == -1) {
|
||||||
std::cerr << "Fehler: Kein passender Vorname gefunden." << std::endl;
|
std::cerr << "Fehler: Kein passender Vorname gefunden." << std::endl;
|
||||||
@@ -94,54 +93,152 @@ void CharacterCreationWorker::createCharacter(int region_id,
|
|||||||
auto &db = connGuard.get();
|
auto &db = connGuard.get();
|
||||||
|
|
||||||
db.prepare("insert_character", QUERY_INSERT_CHARACTER);
|
db.prepare("insert_character", QUERY_INSERT_CHARACTER);
|
||||||
db.execute("insert_character", {std::to_string(region_id),
|
db.execute("insert_character", {std::to_string(region_id),
|
||||||
std::to_string(first_name_id),
|
std::to_string(first_name_id),
|
||||||
std::to_string(last_name_id),
|
std::to_string(last_name_id),
|
||||||
gender,
|
gender,
|
||||||
std::to_string(title_of_nobility)});
|
std::to_string(title_of_nobility)});
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
std::cerr << "[CharacterCreationWorker] Fehler in createCharacter: "
|
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;
|
<< 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) });
|
||||||
|
|
||||||
|
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() {
|
std::vector<int> CharacterCreationWorker::getTownRegionIds() {
|
||||||
try {
|
try {
|
||||||
ConnectionGuard connGuard(pool);
|
ConnectionGuard connGuard(pool);
|
||||||
auto &db = connGuard.get();
|
auto &db = connGuard.get();
|
||||||
|
|
||||||
auto rows = db.query(QUERY_GET_TOWN_REGION_IDS);
|
auto rows = db.query(QUERY_GET_TOWN_REGION_IDS);
|
||||||
|
|
||||||
std::vector<int> ids;
|
std::vector<int> ids;
|
||||||
ids.reserve(rows.size());
|
|
||||||
for (const auto &row : rows) {
|
for (const auto &row : rows) {
|
||||||
ids.push_back(std::stoi(row.at("id")));
|
ids.push_back(std::stoi(row.at("id")));
|
||||||
}
|
}
|
||||||
return ids;
|
return ids;
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
std::cerr << "[CharacterCreationWorker] Fehler in getTownRegionIds: "
|
std::cerr << "[CharacterCreationWorker] Fehler in getTownRegionIds: " << e.what() << std::endl;
|
||||||
<< e.what() << std::endl;
|
return {};
|
||||||
}
|
}
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterCreationWorker::loadNames() {
|
void CharacterCreationWorker::loadNames() {
|
||||||
try {
|
try {
|
||||||
ConnectionGuard connGuard(pool);
|
ConnectionGuard connGuard(pool);
|
||||||
auto &db = connGuard.get();
|
auto &db = connGuard.get();
|
||||||
|
|
||||||
auto firstNameRows = db.query(QUERY_LOAD_FIRST_NAMES);
|
auto firstNameRows = db.query(QUERY_LOAD_FIRST_NAMES);
|
||||||
for (const auto &row : firstNameRows) {
|
for (const auto &row : firstNameRows) {
|
||||||
first_name_cache[row.at("gender")].insert(std::stoi(row.at("id")));
|
first_name_cache[row.at("gender")].insert(std::stoi(row.at("id")));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto lastNameRows = db.query(QUERY_LOAD_LAST_NAMES);
|
auto lastNameRows = db.query(QUERY_LOAD_LAST_NAMES);
|
||||||
for (const auto &row : lastNameRows) {
|
for (const auto &row : lastNameRows) {
|
||||||
last_name_cache.insert(std::stoi(row.at("id")));
|
last_name_cache.insert(std::stoi(row.at("id")));
|
||||||
}
|
}
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
std::cerr << "[CharacterCreationWorker] Fehler in loadNames: "
|
std::cerr << "[CharacterCreationWorker] Fehler in loadNames: " << e.what() << std::endl;
|
||||||
<< e.what() << std::endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,13 @@
|
|||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
class CharacterCreationWorker : public Worker {
|
class CharacterCreationWorker : public Worker {
|
||||||
public:
|
public:
|
||||||
CharacterCreationWorker(ConnectionPool &pool, MessageBroker &broker);
|
CharacterCreationWorker(ConnectionPool &pool, MessageBroker &broker);
|
||||||
~CharacterCreationWorker() override;
|
~CharacterCreationWorker() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void run() override;
|
void run() override;
|
||||||
@@ -20,6 +22,8 @@ private:
|
|||||||
std::uniform_int_distribution<int> dist;
|
std::uniform_int_distribution<int> dist;
|
||||||
std::unordered_map<std::string, std::unordered_set<int>> first_name_cache;
|
std::unordered_map<std::string, std::unordered_set<int>> first_name_cache;
|
||||||
std::unordered_set<int> last_name_cache;
|
std::unordered_set<int> last_name_cache;
|
||||||
|
std::atomic<bool> deathCheckRunning{true};
|
||||||
|
std::thread deathThread;
|
||||||
|
|
||||||
bool isTodayCharacterCreated();
|
bool isTodayCharacterCreated();
|
||||||
void createCharactersForToday();
|
void createCharactersForToday();
|
||||||
@@ -28,6 +32,11 @@ private:
|
|||||||
std::vector<int> getTownRegionIds();
|
std::vector<int> getTownRegionIds();
|
||||||
void loadNames();
|
void loadNames();
|
||||||
int getRandomFromSet(const std::unordered_set<int> &name_set);
|
int getRandomFromSet(const std::unordered_set<int> &name_set);
|
||||||
|
void monitorCharacterDeaths();
|
||||||
|
void handleCharacterDeath(int characterId);
|
||||||
|
void notifyUser(int userId, const std::string &eventType);
|
||||||
|
void markCharacterAsDeceased(int characterId);
|
||||||
|
bool calculateDeathProbability(int age);
|
||||||
|
|
||||||
static constexpr const char *QUERY_IS_PREVIOUS_DAY_CHARACTER_CREATED = R"(
|
static constexpr const char *QUERY_IS_PREVIOUS_DAY_CHARACTER_CREATED = R"(
|
||||||
SELECT created_at
|
SELECT created_at
|
||||||
@@ -57,9 +66,97 @@ private:
|
|||||||
|
|
||||||
static constexpr const char *QUERY_INSERT_CHARACTER = R"(
|
static constexpr const char *QUERY_INSERT_CHARACTER = R"(
|
||||||
INSERT INTO falukant_data."character"(
|
INSERT INTO falukant_data."character"(
|
||||||
user_id, region_id, first_name, last_name,
|
user_id, region_id, first_name, last_name,
|
||||||
birthdate, gender, created_at, updated_at, title_of_nobility
|
birthdate, gender, created_at, updated_at, title_of_nobility
|
||||||
)
|
)
|
||||||
VALUES (NULL, $1, $2, $3, NOW(), $4, NOW(), NOW(), $5);
|
VALUES (NULL, $1, $2, $3, NOW(), $4, NOW(), NOW(), $5);
|
||||||
)";
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_GET_ELIGIBLE_NPC_FOR_DEATH = R"(
|
||||||
|
WITH aged AS (
|
||||||
|
SELECT
|
||||||
|
c.id,
|
||||||
|
(current_date - c.birthdate::date) AS age,
|
||||||
|
c.user_id
|
||||||
|
FROM
|
||||||
|
falukant_data."character" c
|
||||||
|
WHERE
|
||||||
|
c.user_id IS NULL
|
||||||
|
AND (current_date - c.birthdate::date) > 60
|
||||||
|
),
|
||||||
|
always_sel AS (
|
||||||
|
-- Immer mitnehmen: alle über 85 Tage
|
||||||
|
SELECT *
|
||||||
|
FROM aged
|
||||||
|
WHERE age > 85
|
||||||
|
),
|
||||||
|
random_sel AS (
|
||||||
|
-- Zufallsstichprobe: alle zwischen 61 und 85 Tagen, hier beispielhaft auf 10 limitiert
|
||||||
|
SELECT *
|
||||||
|
FROM aged
|
||||||
|
WHERE age <= 85
|
||||||
|
ORDER BY random()
|
||||||
|
LIMIT 10 -- <-- hier die gewünschte Anzahl anpassen
|
||||||
|
)
|
||||||
|
-- Zusammenführen der beiden Mengen
|
||||||
|
SELECT *
|
||||||
|
FROM always_sel
|
||||||
|
UNION ALL
|
||||||
|
SELECT *
|
||||||
|
FROM random_sel;
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_DELETE_DIRECTOR = R"(
|
||||||
|
DELETE FROM falukant_data.director
|
||||||
|
WHERE director_character_id = $1
|
||||||
|
RETURNING employer_user_id;
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_DELETE_RELATIONSHIP = R"(
|
||||||
|
WITH deleted AS (
|
||||||
|
DELETE FROM falukant_data.relationship
|
||||||
|
WHERE character1_id = $1
|
||||||
|
OR character2_id = $1
|
||||||
|
RETURNING
|
||||||
|
CASE
|
||||||
|
WHEN character1_id = $1 THEN character2_id
|
||||||
|
ELSE character1_id
|
||||||
|
END AS related_character_id,
|
||||||
|
relationship_type_id
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
c.user_id AS related_user_id
|
||||||
|
FROM deleted d
|
||||||
|
JOIN falukant_data."character" c
|
||||||
|
ON c.id = d.related_character_id;
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_DELETE_CHILD_RELATION = R"(
|
||||||
|
WITH deleted AS (
|
||||||
|
DELETE FROM falukant_data.child_relation
|
||||||
|
WHERE child_character_id = $1
|
||||||
|
RETURNING
|
||||||
|
father_character_id,
|
||||||
|
mother_character_id
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
cf.user_id AS father_user_id,
|
||||||
|
cm.user_id AS mother_user_id
|
||||||
|
FROM deleted d
|
||||||
|
JOIN falukant_data."character" cf
|
||||||
|
ON cf.id = d.father_character_id
|
||||||
|
JOIN falukant_data."character" cm
|
||||||
|
ON cm.id = d.mother_character_id;
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_INSERT_NOTIFICATION = R"(
|
||||||
|
INSERT INTO falukant_log.notification (user_id, tr, shown, created_at, updated_at)
|
||||||
|
VALUES ($1, 'director_death', false, NOW(), NOW());
|
||||||
|
)";
|
||||||
|
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_MARK_CHARACTER_DECEASED = R"(
|
||||||
|
DELETE FROM falukant_data."character"
|
||||||
|
WHERE id = $1;
|
||||||
|
)";
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
#include "database.h"
|
#include "database.h"
|
||||||
|
#include <pqxx/pqxx>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
Database::Database(const std::string &conninfo)
|
Database::Database(const std::string &conninfo)
|
||||||
{
|
{
|
||||||
@@ -9,7 +14,9 @@ Database::Database(const std::string &conninfo)
|
|||||||
}
|
}
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
std::cerr << "[Database] Fehler beim Verbinden: " << e.what() << std::endl;
|
std::cerr << "[Database] Fehler beim Verbinden: " << e.what() << std::endl;
|
||||||
throw;
|
|
||||||
|
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,32 +55,16 @@ void Database::prepare(const std::string &stmtName, const std::string &sql)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <pqxx/pqxx>
|
Database::FieldList Database::execute(const std::string &stmtName, const std::vector<std::string> ¶ms) {
|
||||||
#include <vector>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <string>
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
std::vector<std::unordered_map<std::string, std::string>>
|
|
||||||
Database::execute(const std::string &stmtName, const std::vector<std::string> ¶ms)
|
|
||||||
{
|
|
||||||
std::vector<std::unordered_map<std::string, std::string>> rows;
|
std::vector<std::unordered_map<std::string, std::string>> rows;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pqxx::work txn(*connection_);
|
pqxx::work txn(*connection_);
|
||||||
pqxx::prepare::invocation inv = txn.prepared(stmtName);
|
pqxx::prepare::invocation inv = txn.prepared(stmtName);
|
||||||
|
|
||||||
// Parameter einzeln hinzufügen
|
|
||||||
for (auto &p : params) {
|
for (auto &p : params) {
|
||||||
inv(p);
|
inv(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ausführung
|
|
||||||
pqxx::result r = inv.exec();
|
pqxx::result r = inv.exec();
|
||||||
txn.commit();
|
txn.commit();
|
||||||
|
|
||||||
// Ergebnis verarbeiten
|
|
||||||
for (const auto &row : r) {
|
for (const auto &row : r) {
|
||||||
std::unordered_map<std::string, std::string> mapRow;
|
std::unordered_map<std::string, std::string> mapRow;
|
||||||
for (pqxx::row::size_type i = 0; i < row.size(); ++i) {
|
for (pqxx::row::size_type i = 0; i < row.size(); ++i) {
|
||||||
@@ -91,6 +82,12 @@ Database::execute(const std::string &stmtName, const std::vector<std::string> &p
|
|||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Database::remove(const std::string &stmtName) {
|
||||||
|
pqxx::work txn(*connection_);
|
||||||
|
txn.conn().unprepare(stmtName);
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
bool Database::isValid() const {
|
bool Database::isValid() const {
|
||||||
try {
|
try {
|
||||||
if (!connection_ || !connection_->is_open()) {
|
if (!connection_ || !connection_->is_open()) {
|
||||||
|
|||||||
@@ -2,24 +2,25 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <sstream>
|
|
||||||
#include <pgsql/libpq-fe.h>
|
#include <pgsql/libpq-fe.h>
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <pqxx/pqxx>
|
#include <pqxx/pqxx>
|
||||||
|
|
||||||
class Database
|
class Database {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
Database(const std::string &conninfo);
|
Database(const std::string &conninfo);
|
||||||
|
|
||||||
|
typedef std::unordered_map<std::string, std::string> FieldMap;
|
||||||
|
typedef std::vector<FieldMap> FieldList;
|
||||||
|
|
||||||
std::vector<std::map<std::string, std::string>> query(const std::string &sql);
|
std::vector<std::map<std::string, std::string>> query(const std::string &sql);
|
||||||
void prepare(const std::string &stmtName, const std::string &sql);
|
void prepare(const std::string &stmtName, const std::string &sql);
|
||||||
std::vector<std::unordered_map<std::string,std::string>> execute(
|
FieldList execute(
|
||||||
const std::string &stmtName,
|
const std::string &stmtName,
|
||||||
const std::vector<std::string> ¶ms = {}
|
const std::vector<std::string> ¶ms = {}
|
||||||
);
|
);
|
||||||
|
void remove(const std::string &stmtName);
|
||||||
bool isOpen() const { return connection_ && connection_->is_open(); }
|
bool isOpen() const { return connection_ && connection_->is_open(); }
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
|
||||||
|
|||||||
192
src/director_worker.cpp
Normal file
192
src/director_worker.cpp
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
#include "director_worker.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
DirectorWorker::DirectorWorker(ConnectionPool &pool, MessageBroker &broker)
|
||||||
|
: Worker(pool, broker, "DirectorWorker") {
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectorWorker::~DirectorWorker() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirectorWorker::run() {
|
||||||
|
auto lastExecutionTime = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
while (runningWorker) {
|
||||||
|
signalActivity();
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - lastExecutionTime).count();
|
||||||
|
if (elapsed >= 60) {
|
||||||
|
try {
|
||||||
|
performTask();
|
||||||
|
paySalary();
|
||||||
|
calculateSatisfaction();
|
||||||
|
lastExecutionTime = now;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
std::cerr << "[DirectorWorker] Fehler beim Ausführen der Aufgabe: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirectorWorker::performTask() {
|
||||||
|
try {
|
||||||
|
setCurrentStep("Get Database Connection");
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
setCurrentStep("Get director actions");
|
||||||
|
db.prepare("QUERY_GET_DIRECTORS", QUERY_GET_DIRECTORS);
|
||||||
|
const auto directors = db.execute("QUERY_GET_DIRECTORS");
|
||||||
|
for (const auto &director: directors) {
|
||||||
|
if (director.at("may_produce") == "t") {
|
||||||
|
startProductions(director);
|
||||||
|
}
|
||||||
|
if (director.at("may_start_transport") == "t") {
|
||||||
|
startTransports(director);
|
||||||
|
}
|
||||||
|
if (director.at("may_sell") == "t") {
|
||||||
|
startSellings(director);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
std::cerr << "[DirectorWorker] Fehler bei der Datenbankoperation: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirectorWorker::startProductions(std::unordered_map<std::string, std::string> director) {
|
||||||
|
auto parseIntOrZero = [&](const std::string &s){
|
||||||
|
if (s.empty() || s == "null") return 0;
|
||||||
|
try {
|
||||||
|
return std::stoi(s);
|
||||||
|
} catch(...) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setCurrentStep("Get Database Connection - Production");
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
|
||||||
|
setCurrentStep("Get to produce");
|
||||||
|
db.prepare("get_to_produce", QUERY_GET_BEST_PRODUCTION);
|
||||||
|
const auto productions = db.execute("get_to_produce", { director.at("id") });
|
||||||
|
if (productions.empty()) return;
|
||||||
|
const auto &production = productions.at(0);
|
||||||
|
|
||||||
|
int runningProductions = parseIntOrZero(production.at("running_productions"));
|
||||||
|
if (runningProductions >= 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentStep("Add production to DB");
|
||||||
|
int availableStock = parseIntOrZero(production.at("stock_size"));
|
||||||
|
int usedStock = parseIntOrZero(production.at("used_in_stock"));
|
||||||
|
int freeCapacity = availableStock - usedStock - runningProductions;
|
||||||
|
|
||||||
|
int certificate = parseIntOrZero(production.at("certificate"));
|
||||||
|
int onePieceCost = certificate * 6;
|
||||||
|
int money = parseIntOrZero(production.at("money"));
|
||||||
|
int maxMoneyProduction = onePieceCost > 0 ? money / onePieceCost : 0;
|
||||||
|
|
||||||
|
int toProduce = std::min(std::min(freeCapacity, maxMoneyProduction), 300);
|
||||||
|
if (toProduce < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int falukantUserId = parseIntOrZero(production.at("falukant_user_id"));
|
||||||
|
int productionCost = toProduce * onePieceCost;
|
||||||
|
|
||||||
|
nlohmann::json msg1 = { { "event", "falukantUpdateStatus" } };
|
||||||
|
setCurrentStep("Update money");
|
||||||
|
changeFalukantUserMoney(falukantUserId, -productionCost, "director starts production", msg1);
|
||||||
|
|
||||||
|
setCurrentStep("Insert production");
|
||||||
|
db.prepare("insert_production", QUERY_INSERT_PRODUCTION);
|
||||||
|
|
||||||
|
int remaining = toProduce;
|
||||||
|
while (remaining > 0) {
|
||||||
|
int batch = std::min(100, remaining);
|
||||||
|
db.execute("insert_production", {
|
||||||
|
production.at("branch_id"),
|
||||||
|
production.at("product_id"),
|
||||||
|
std::to_string(batch)
|
||||||
|
});
|
||||||
|
remaining -= batch;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json msg2 = {
|
||||||
|
{ "event", "production_started" },
|
||||||
|
{ "branch_id", production.at("branch_id") }
|
||||||
|
};
|
||||||
|
sendMessageToFalukantUsers(falukantUserId, msg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirectorWorker::startTransports(std::unordered_map<std::string, std::string>) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirectorWorker::startSellings(std::unordered_map<std::string, std::string> director) {
|
||||||
|
setCurrentStep("Get Database Connection - Production");
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
setCurrentStep("Get to sell");
|
||||||
|
db.prepare("get_to_sell", QUERY_GET_INVENTORY);
|
||||||
|
const auto inventory = db.execute("get_to_sell", { director.at("id") });
|
||||||
|
for (const auto &item: inventory) {
|
||||||
|
const auto inventoryId = std::stoi(item.at("id"));
|
||||||
|
const auto productId = std::stoi(item.at("product_id"));
|
||||||
|
const auto quantity = std::stoi(item.at("quantity"));
|
||||||
|
const auto quality = std::stoi(item.at("quality"));
|
||||||
|
const auto maxSellPrice = std::stod(item.at("sell_cost"));
|
||||||
|
auto falukantUserId = std::stoi(item.at("user_id"));
|
||||||
|
const auto regionId = std::stoi(item.at("region_id"));
|
||||||
|
if (quantity > 0) {
|
||||||
|
const auto minPrice = maxSellPrice * 0.6;
|
||||||
|
const auto pieceSellPrice = minPrice + (double)(maxSellPrice - minPrice) * (quality / 100.0);
|
||||||
|
const auto sellPrice = pieceSellPrice * quantity;
|
||||||
|
const nlohmann::json changeMessage = {
|
||||||
|
{ "productId", productId },
|
||||||
|
{ "event", "falukantUpdateStatus" }
|
||||||
|
};
|
||||||
|
changeFalukantUserMoney(falukantUserId, sellPrice, "sell products", changeMessage);
|
||||||
|
db.prepare("QUERY_ADD_SELL_LOG", QUERY_ADD_SELL_LOG);
|
||||||
|
db.execute("QUERY_ADD_SELL_LOG", { std::to_string(regionId), std::to_string(productId), std::to_string(quantity),
|
||||||
|
std::to_string(falukantUserId) });
|
||||||
|
}
|
||||||
|
db.prepare("remove_inventory", QUERY_REMOVE_INVENTORY);
|
||||||
|
db.execute("remove_inventory", { std::to_string(inventoryId) });
|
||||||
|
nlohmann::json message = {
|
||||||
|
{ "event", "selled_items" },
|
||||||
|
{ "branch_id", item.at("branch_id") },
|
||||||
|
};
|
||||||
|
sendMessageToFalukantUsers(falukantUserId, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirectorWorker::paySalary() {
|
||||||
|
setCurrentStep("salary - load to pay");
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
db.prepare("QUERY_GET_SALARY_TO_PAY", QUERY_GET_SALARY_TO_PAY);
|
||||||
|
const auto &salariesToPay = db.execute("QUERY_GET_SALARY_TO_PAY");
|
||||||
|
nlohmann::json message = {
|
||||||
|
{ "event", "falukantUpdateStatus" }
|
||||||
|
};
|
||||||
|
for (auto const &item: salariesToPay) {
|
||||||
|
changeFalukantUserMoney(std::stoi(item.at("employer_user_id")), -std::stoi(item.at("income")), "director payed out", message);
|
||||||
|
db.prepare("QUERY_SET_SALARY_PAYED", QUERY_SET_SALARY_PAYED);
|
||||||
|
db.execute("QUERY_SET_SALARY_PAYED", { std::to_string(std::stoi(item.at("id"))) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirectorWorker::calculateSatisfaction() {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
db.prepare("QUERY_UPDATE_SATISFACTION", QUERY_UPDATE_SATISFACTION);
|
||||||
|
const auto &changedDirectors = db.execute("QUERY_UPDATE_SATISFACTION");
|
||||||
|
nlohmann::json message = {
|
||||||
|
{ "event", "directorchanged" }
|
||||||
|
};
|
||||||
|
for (auto const &director: changedDirectors) {
|
||||||
|
sendMessageToFalukantUsers(std::stoi(director.at("employer_user_id")), message);
|
||||||
|
}
|
||||||
|
}
|
||||||
155
src/director_worker.h
Normal file
155
src/director_worker.h
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
#ifndef DIRECTOR_WORKER_H
|
||||||
|
#define DIRECTOR_WORKER_H
|
||||||
|
|
||||||
|
#include "worker.h"
|
||||||
|
|
||||||
|
class DirectorWorker : public Worker {
|
||||||
|
public:
|
||||||
|
explicit DirectorWorker(ConnectionPool &pool, MessageBroker &broker);
|
||||||
|
~DirectorWorker() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void performTask();
|
||||||
|
void startProductions(std::unordered_map<std::string, std::string> director);
|
||||||
|
void startTransports(std::unordered_map<std::string, std::string>);
|
||||||
|
void startSellings(std::unordered_map<std::string, std::string>);
|
||||||
|
void paySalary();
|
||||||
|
void calculateSatisfaction();
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_GET_DIRECTORS = R"(
|
||||||
|
select d.may_produce, d.may_sell, d.may_start_transport, b.id branch_id, fu.id falukantUserId, d.id
|
||||||
|
from falukant_data.director d
|
||||||
|
join falukant_data.falukant_user fu
|
||||||
|
on fu.id = d.employer_user_id
|
||||||
|
join falukant_data."character" c
|
||||||
|
on c.id = d.director_character_id
|
||||||
|
join falukant_data.branch b
|
||||||
|
on b.region_id = c.region_id
|
||||||
|
and b.falukant_user_id = fu.id
|
||||||
|
where current_time between '08:00:00' and '17:00:00'
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_GET_BEST_PRODUCTION = R"(
|
||||||
|
select fdu."id" falukant_user_id, fdu."money", fdu."certificate", ftp."id" product_id, ftp.label_tr,(select sum("quantity")
|
||||||
|
from "falukant_data"."stock" fds
|
||||||
|
where fds."branch_id" = fdb."id") stock_size, coalesce((select sum(coalesce(fdi."quantity", 0))
|
||||||
|
from "falukant_data"."stock" fds
|
||||||
|
join "falukant_data"."inventory" fdi
|
||||||
|
on fdi."stock_id" = fds."id"
|
||||||
|
where fds."branch_id" = fdb."id"), 0) used_in_stock,
|
||||||
|
(ftp."sell_cost" * (fdtpw."worth_percent" + (fdk_character."knowledge" * 2 + fdk_director."knowledge") / 3) / 100 - 6 * ftp.category) / (300.0 * ftp. production_time) worth,
|
||||||
|
fdb."id" branch_id,
|
||||||
|
(select count("id") from "falukant_data"."production" where "branch_id" = fdb."id") running_productions,
|
||||||
|
coalesce((select sum(coalesce(fdp.quantity, 0)) quantity from
|
||||||
|
falukant_data.production fdp where fdp.branch_id = fdb.id), 0) running_productions
|
||||||
|
from "falukant_data"."director" fdd
|
||||||
|
join "falukant_data".character fdc
|
||||||
|
on fdc.id = fdd.director_character_id
|
||||||
|
join "falukant_data"."falukant_user" fdu
|
||||||
|
on fdd."employer_user_id" = fdu."id"
|
||||||
|
join "falukant_data"."character" user_character
|
||||||
|
on user_character."user_id" = fdu."id"
|
||||||
|
join "falukant_data"."branch" fdb
|
||||||
|
on fdb."falukant_user_id" = fdu."id"
|
||||||
|
and fdb."region_id" = fdc."region_id"
|
||||||
|
join "falukant_data"."town_product_worth" fdtpw
|
||||||
|
on fdtpw."region_id" = fdb."region_id"
|
||||||
|
join "falukant_data"."knowledge" fdk_character
|
||||||
|
on
|
||||||
|
fdk_character."product_id" = fdtpw."product_id"
|
||||||
|
and fdk_character."character_id" = user_character."id"
|
||||||
|
and fdk_character."product_id" = fdtpw."product_id"
|
||||||
|
join "falukant_data"."knowledge" fdk_director
|
||||||
|
on
|
||||||
|
fdk_director."product_id" = fdtpw."product_id"
|
||||||
|
and fdk_director."character_id" = fdd."director_character_id"
|
||||||
|
and fdk_director."product_id" = fdtpw."product_id"
|
||||||
|
join "falukant_type"."product" ftp
|
||||||
|
on
|
||||||
|
ftp."id" = fdtpw."product_id"
|
||||||
|
and ftp.category <= fdu.certificate
|
||||||
|
where fdd."id" = $1
|
||||||
|
order by worth desc
|
||||||
|
limit 1;
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_INSERT_PRODUCTION = R"(
|
||||||
|
insert into "falukant_data"."production" ("branch_id", "product_id", "quantity")
|
||||||
|
values ($1, $2, $3)
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_GET_INVENTORY = R"(
|
||||||
|
select i.id, i.product_id, i.quantity, i.quality, p.sell_cost, fu.id user_id, b.region_id, b.id branch_id
|
||||||
|
from falukant_data.inventory i
|
||||||
|
join falukant_data.stock s
|
||||||
|
on s.id = i.stock_id
|
||||||
|
join falukant_data.branch b
|
||||||
|
on b.id = s.branch_id
|
||||||
|
join falukant_data.falukant_user fu
|
||||||
|
on fu.id = b.falukant_user_id
|
||||||
|
join falukant_data.director d
|
||||||
|
on d.employer_user_id = fu.id
|
||||||
|
join falukant_type.product p
|
||||||
|
on p.id = i.product_id
|
||||||
|
where d.id = $1
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_REMOVE_INVENTORY = R"(
|
||||||
|
delete from falukant_data.inventory
|
||||||
|
where id = $1
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_ADD_SELL_LOG = R"(
|
||||||
|
INSERT INTO falukant_log.sell ("region_id", "product_id", "quantity", "seller_id")
|
||||||
|
values ($1, $2, $3, $4)
|
||||||
|
ON CONFLICT ("region_id", "product_id", "seller_id")
|
||||||
|
DO UPDATE
|
||||||
|
SET "quantity" = falukant_log.sell."quantity" + EXCLUDED.quantity
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_GET_SALARY_TO_PAY = R"(
|
||||||
|
select d.id, d.employer_user_id, d.income
|
||||||
|
from falukant_data.director d
|
||||||
|
where date(d.last_salary_payout) < date(now())
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_SET_SALARY_PAYED = R"(
|
||||||
|
update falukant_data.director
|
||||||
|
set last_salary_payout = NOW()
|
||||||
|
where id = $1
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_UPDATE_SATISFACTION = R"(
|
||||||
|
WITH new_sats AS (
|
||||||
|
SELECT
|
||||||
|
d.id,
|
||||||
|
ROUND(
|
||||||
|
d.income::numeric
|
||||||
|
/
|
||||||
|
(
|
||||||
|
c.title_of_nobility
|
||||||
|
* POWER(1.231, AVG(k.knowledge) / 1.5)
|
||||||
|
)
|
||||||
|
* 100
|
||||||
|
) AS new_satisfaction
|
||||||
|
FROM falukant_data.director d
|
||||||
|
JOIN falukant_data.knowledge k
|
||||||
|
ON d.director_character_id = k.character_id
|
||||||
|
JOIN falukant_data.character c
|
||||||
|
ON c.id = d.director_character_id
|
||||||
|
GROUP BY d.id, c.title_of_nobility, d.income
|
||||||
|
)
|
||||||
|
UPDATE falukant_data.director dir
|
||||||
|
SET satisfaction = ns.new_satisfaction
|
||||||
|
FROM new_sats ns
|
||||||
|
WHERE dir.id = ns.id
|
||||||
|
-- Nur updaten, wenn sich der Wert tatsächlich ändert:
|
||||||
|
AND dir.satisfaction IS DISTINCT FROM ns.new_satisfaction
|
||||||
|
RETURNING dir.employer_user_id;
|
||||||
|
)";
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DIRECTOR_WORKER_H
|
||||||
65
src/houseworker.cpp
Normal file
65
src/houseworker.cpp
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#include "houseworker.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
HouseWorker::HouseWorker(ConnectionPool &pool, MessageBroker &broker):
|
||||||
|
Worker(pool, broker, "HouseWorker") {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
HouseWorker::~HouseWorker() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void HouseWorker::run() {
|
||||||
|
auto lastExecutionTime = std::chrono::steady_clock::now();
|
||||||
|
auto lastHouseStateChange = std::chrono::system_clock::now();
|
||||||
|
while (runningWorker) {
|
||||||
|
signalActivity();
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - lastExecutionTime).count();
|
||||||
|
if (elapsed >= 3600) {
|
||||||
|
performTask();
|
||||||
|
}
|
||||||
|
auto nowSystem = std::chrono::system_clock::now();
|
||||||
|
auto lastDay = floor<std::chrono::days>(lastHouseStateChange);
|
||||||
|
auto today = floor<std::chrono::days>(nowSystem);
|
||||||
|
if (lastDay < today) {
|
||||||
|
performHouseStateChange();
|
||||||
|
lastHouseStateChange = nowSystem;
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HouseWorker::performTask() {
|
||||||
|
try {
|
||||||
|
setCurrentStep("Get Database Connection");
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
setCurrentStep("Get new houses data");
|
||||||
|
db.prepare("QUERY_GET_NEW_HOUSE_DATA", QUERY_GET_NEW_HOUSE_DATA);
|
||||||
|
const auto newHouses = db.execute("QUERY_GET_NEW_HOUSE_DATA");
|
||||||
|
for (const auto &newHouse: newHouses) {
|
||||||
|
db.prepare("QUERY_ADD_NEW_BUYABLE_HOUSE", QUERY_ADD_NEW_BUYABLE_HOUSE);
|
||||||
|
db.execute("QUERY_ADD_NEW_BUYABLE_HOUSE", { newHouse.at("house_id") });
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
std::cerr << "[HouseWorker] Fehler bei der Datenbankoperation: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HouseWorker::performHouseStateChange() {
|
||||||
|
try {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
db.remove("QUERY_UPDATE_BUYABLE_HOUSE_STATE");
|
||||||
|
db.remove("QUERY_UPDATE_USER_HOUSE_STATE");
|
||||||
|
db.prepare("QUERY_UPDATE_BUYABLE_HOUSE_STATE", QUERY_UPDATE_BUYABLE_HOUSE_STATE);
|
||||||
|
db.prepare("QUERY_UPDATE_USER_HOUSE_STATE", QUERY_UPDATE_USER_HOUSE_STATE);
|
||||||
|
db.execute("QUERY_UPDATE_BUYABLE_HOUSE_STATE");
|
||||||
|
db.execute("QUERY_UPDATE_USER_HOUSE_STATE");
|
||||||
|
} catch(const std::exception &e) {
|
||||||
|
std::cerr << "[HouseWorker] Fehler bei der Datenbankoperation: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
54
src/houseworker.h
Normal file
54
src/houseworker.h
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#ifndef HOUSEWORKER_H
|
||||||
|
#define HOUSEWORKER_H
|
||||||
|
|
||||||
|
#include "worker.h"
|
||||||
|
|
||||||
|
class HouseWorker : public Worker {
|
||||||
|
public:
|
||||||
|
HouseWorker(ConnectionPool &pool, MessageBroker &broker);
|
||||||
|
~HouseWorker() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void performTask();
|
||||||
|
void performHouseStateChange();
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_GET_NEW_HOUSE_DATA = R"(
|
||||||
|
SELECT
|
||||||
|
h.id AS house_id
|
||||||
|
FROM
|
||||||
|
falukant_type.house AS h
|
||||||
|
WHERE
|
||||||
|
random() < 0.0001
|
||||||
|
and "label_tr" != 'under_bridge';
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_ADD_NEW_BUYABLE_HOUSE = R"(
|
||||||
|
insert into falukant_data.buyable_house (house_type_id) values ($1);
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_UPDATE_BUYABLE_HOUSE_STATE = R"(
|
||||||
|
update falukant_data.buyable_house
|
||||||
|
set roof_condition = round(roof_condition - random() * (3 + 0 * id)),
|
||||||
|
floor_condition = round(floor_condition - random() * (3 + 0 * id)),
|
||||||
|
wall_condition = round(wall_condition - random() * (3 + 0 * id)),
|
||||||
|
window_condition = round(wall_condition - random() * (3 + 0 * id))
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_UPDATE_USER_HOUSE_STATE = R"(
|
||||||
|
update falukant_data.user_house
|
||||||
|
set roof_condition = round(roof_condition - random() * (3 + 0 * id)),
|
||||||
|
floor_condition = round(floor_condition - random() * (3 + 0 * id)),
|
||||||
|
wall_condition = round(wall_condition - random() * (3 + 0 * id)),
|
||||||
|
window_condition = round(window_condition - random() * (3 + 0 * id))
|
||||||
|
where house_type_id not in (
|
||||||
|
select id
|
||||||
|
from falukant_type.house h
|
||||||
|
where h.label_tr = 'under_bridge'
|
||||||
|
)
|
||||||
|
)";
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // HOUSEWORKER_H
|
||||||
45
src/main.cpp
45
src/main.cpp
@@ -1,13 +1,21 @@
|
|||||||
#include "character_creation_worker.h"
|
#include "character_creation_worker.h"
|
||||||
#include "produce_worker.h"
|
#include "produce_worker.h"
|
||||||
|
#include "stockagemanager.h"
|
||||||
|
#include "director_worker.h"
|
||||||
|
#include "valuerecalculationworker.h"
|
||||||
#include "connection_pool.h"
|
#include "connection_pool.h"
|
||||||
#include "websocket_server.h"
|
#include "websocket_server.h"
|
||||||
#include "message_broker.h"
|
#include "message_broker.h"
|
||||||
|
#include "usercharacterworker.h"
|
||||||
|
#include "houseworker.h"
|
||||||
|
#include "politics_worker.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
std::atomic<bool> keepRunning(true);
|
std::atomic<bool> keepRunning(true);
|
||||||
|
|
||||||
@@ -25,39 +33,44 @@ int main() {
|
|||||||
try {
|
try {
|
||||||
Config config("/etc/yourpart/daemon.conf");
|
Config config("/etc/yourpart/daemon.conf");
|
||||||
ConnectionPool pool(
|
ConnectionPool pool(
|
||||||
config.get("DB_HOST"),
|
config.get("DB_HOST"),
|
||||||
config.get("DB_PORT"),
|
config.get("DB_PORT"),
|
||||||
config.get("DB_NAME"),
|
config.get("DB_NAME"),
|
||||||
config.get("DB_USER"),
|
config.get("DB_USER"),
|
||||||
config.get("DB_PASSWORD"),
|
config.get("DB_PASSWORD"),
|
||||||
10
|
10
|
||||||
);
|
);
|
||||||
|
|
||||||
int websocketPort = std::stoi(config.get("WEBSOCKET_PORT"));
|
int websocketPort = std::stoi(config.get("WEBSOCKET_PORT"));
|
||||||
MessageBroker broker;
|
MessageBroker broker;
|
||||||
WebSocketServer websocketServer(websocketPort, pool, broker);
|
WebSocketServer websocketServer(websocketPort, pool, broker);
|
||||||
CharacterCreationWorker creationWorker(pool, broker);
|
std::vector<std::unique_ptr<Worker>> workers;
|
||||||
ProduceWorker produceWorker(pool, broker);
|
workers.push_back(std::make_unique<CharacterCreationWorker>(pool, broker));
|
||||||
|
workers.push_back(std::make_unique<ProduceWorker>(pool, broker));
|
||||||
|
workers.push_back(std::make_unique<StockageManager>(pool, broker));
|
||||||
|
workers.push_back(std::make_unique<DirectorWorker>(pool, broker));
|
||||||
|
workers.push_back(std::make_unique<ValueRecalculationWorker>(pool, broker));
|
||||||
|
workers.push_back(std::make_unique<UserCharacterWorker>(pool, broker));
|
||||||
|
workers.push_back(std::make_unique<HouseWorker>(pool, broker));
|
||||||
|
workers.push_back(std::make_unique<PoliticsWorker>(pool, broker));
|
||||||
|
websocketServer.setWorkers(workers);
|
||||||
|
|
||||||
broker.start();
|
broker.start();
|
||||||
websocketServer.run();
|
websocketServer.run();
|
||||||
creationWorker.startWorkerThread();
|
for (auto &worker : workers) {
|
||||||
produceWorker.startWorkerThread();
|
worker->startWorkerThread();
|
||||||
creationWorker.enableWatchdog();
|
worker->enableWatchdog();
|
||||||
produceWorker.enableWatchdog();
|
}
|
||||||
|
|
||||||
while (keepRunning) {
|
while (keepRunning) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||||
}
|
}
|
||||||
|
for (auto &worker : workers) {
|
||||||
creationWorker.stopWorkerThread();
|
worker->stopWorkerThread();
|
||||||
produceWorker.stopWorkerThread();
|
}
|
||||||
websocketServer.stop();
|
websocketServer.stop();
|
||||||
broker.stop();
|
broker.stop();
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
std::cerr << "Fehler: " << e.what() << std::endl;
|
std::cerr << "Fehler: " << e.what() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
224
src/politics_worker.cpp
Normal file
224
src/politics_worker.cpp
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
// File: politics_worker.cpp
|
||||||
|
|
||||||
|
#include "politics_worker.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
PoliticsWorker::PoliticsWorker(ConnectionPool &pool, MessageBroker &broker)
|
||||||
|
: Worker(pool, broker, "PoliticsWorker")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PoliticsWorker::~PoliticsWorker()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void PoliticsWorker::run() {
|
||||||
|
auto lastExecutionDate = std::chrono::system_clock::time_point{};
|
||||||
|
while (runningWorker) {
|
||||||
|
signalActivity();
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
auto todayFloor = std::chrono::floor<std::chrono::days>(now);
|
||||||
|
auto targetTime = todayFloor + std::chrono::hours(3); // 03:00 Uhr
|
||||||
|
if (now >= targetTime && lastExecutionDate < todayFloor) {
|
||||||
|
signalActivity();
|
||||||
|
performDailyPoliticsTask();
|
||||||
|
lastExecutionDate = todayFloor;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 5 && runningWorker.load(); ++i) {
|
||||||
|
signalActivity();
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PoliticsWorker::performDailyPoliticsTask() {
|
||||||
|
try {
|
||||||
|
// … (Schritte für Notifications und evaluatePoliticalPositions) …
|
||||||
|
|
||||||
|
// 3) Elections anlegen und **je 2 × posts_to_fill** Kandidaten hinzufügen
|
||||||
|
{
|
||||||
|
setCurrentStep("Schedule Elections and Insert Candidates");
|
||||||
|
|
||||||
|
// 3a) Neue Elections erzeugen (liefert jetzt auch posts_to_fill)
|
||||||
|
auto elections = scheduleElections();
|
||||||
|
|
||||||
|
if (!elections.empty()) {
|
||||||
|
for (auto const & tpl : elections) {
|
||||||
|
int electionId = std::get<0>(tpl);
|
||||||
|
int regionId = std::get<1>(tpl);
|
||||||
|
int postsToFill = std::get<2>(tpl);
|
||||||
|
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
|
||||||
|
db.prepare("INSERT_CANDIDATES", QUERY_INSERT_CANDIDATES);
|
||||||
|
// $1 = electionId, $2 = regionId, $3 = postsToFill
|
||||||
|
db.execute("INSERT_CANDIDATES", {
|
||||||
|
std::to_string(electionId),
|
||||||
|
std::to_string(regionId),
|
||||||
|
std::to_string(postsToFill)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// … nach scheduleElections() & Kandidaten …
|
||||||
|
{
|
||||||
|
setCurrentStep("Process Elections After 3 Days");
|
||||||
|
auto newOffices = processElections();
|
||||||
|
for (auto const &tup : newOffices) {
|
||||||
|
notifyOfficeFilled({tup});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (std::exception const & e) {
|
||||||
|
std::cerr << "[PoliticsWorker] Fehler bei performDailyPoliticsTask: " << e.what() << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PoliticsWorker::evaluatePoliticalPositions(
|
||||||
|
std::unordered_map<int,int>& requiredPerRegion,
|
||||||
|
std::unordered_map<int,int>& occupiedPerRegion
|
||||||
|
) {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
|
||||||
|
signalActivity();
|
||||||
|
db.prepare("COUNT_OFFICES_PER_REGION", QUERY_COUNT_OFFICES_PER_REGION);
|
||||||
|
signalActivity();
|
||||||
|
const auto result = db.execute("COUNT_OFFICES_PER_REGION");
|
||||||
|
signalActivity();
|
||||||
|
|
||||||
|
for (const auto &row : result) {
|
||||||
|
int regionId = std::stoi(row.at("region_id"));
|
||||||
|
int reqCount = std::stoi(row.at("required_count"));
|
||||||
|
int occCount = std::stoi(row.at("occupied_count"));
|
||||||
|
|
||||||
|
requiredPerRegion[regionId] = reqCount;
|
||||||
|
occupiedPerRegion[regionId] = occCount;
|
||||||
|
|
||||||
|
signalActivity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// politics_worker.cpp (Auszug)
|
||||||
|
|
||||||
|
std::vector<std::tuple<int,int,int>> PoliticsWorker::scheduleElections() {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
signalActivity();
|
||||||
|
db.prepare("SELECT_NEEDED_ELECTIONS", QUERY_SELECT_NEEDED_ELECTIONS);
|
||||||
|
signalActivity();
|
||||||
|
auto result = db.execute("SELECT_NEEDED_ELECTIONS");
|
||||||
|
signalActivity();
|
||||||
|
std::vector<std::tuple<int,int,int>> created;
|
||||||
|
created.reserve(result.size());
|
||||||
|
for (auto const & row : result) {
|
||||||
|
int electionId = std::stoi(row.at("election_id"));
|
||||||
|
int regionId = std::stoi(row.at("region_id"));
|
||||||
|
int postsToFill = std::stoi(row.at("posts_to_fill"));
|
||||||
|
created.emplace_back(electionId, regionId, postsToFill);
|
||||||
|
signalActivity();
|
||||||
|
}
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::tuple<int,int,int,int>> PoliticsWorker::processExpiredOfficesAndFill() {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
|
||||||
|
signalActivity();
|
||||||
|
db.prepare("PROCESS_EXPIRED_AND_FILL", QUERY_PROCESS_EXPIRED_AND_FILL);
|
||||||
|
signalActivity();
|
||||||
|
const auto result = db.execute("PROCESS_EXPIRED_AND_FILL");
|
||||||
|
signalActivity();
|
||||||
|
|
||||||
|
std::vector<std::tuple<int,int,int,int>> created;
|
||||||
|
for (const auto &row : result) {
|
||||||
|
int officeId = std::stoi(row.at("office_id"));
|
||||||
|
int officeTypeId = std::stoi(row.at("office_type_id"));
|
||||||
|
int characterId = std::stoi(row.at("character_id"));
|
||||||
|
int regionId = std::stoi(row.at("region_id"));
|
||||||
|
created.emplace_back(officeId, officeTypeId, characterId, regionId);
|
||||||
|
signalActivity();
|
||||||
|
}
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> PoliticsWorker::getUserIdsInCitiesOfRegions(const std::vector<int>& regionIds) {
|
||||||
|
if (regionIds.empty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
|
||||||
|
std::vector<int> userIds;
|
||||||
|
for (int rid : regionIds) {
|
||||||
|
signalActivity();
|
||||||
|
db.prepare("GET_USERS_IN_CITIES", QUERY_USERS_IN_CITIES_OF_REGIONS);
|
||||||
|
signalActivity();
|
||||||
|
const auto rows = db.execute("GET_USERS_IN_CITIES", { std::to_string(rid) });
|
||||||
|
signalActivity();
|
||||||
|
|
||||||
|
for (const auto &row : rows) {
|
||||||
|
int uid = std::stoi(row.at("user_id"));
|
||||||
|
userIds.push_back(uid);
|
||||||
|
signalActivity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return userIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PoliticsWorker::notifyOfficeExpirations() {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
|
||||||
|
signalActivity();
|
||||||
|
db.prepare("NOTIFY_OFFICE_EXPIRATION", QUERY_NOTIFY_OFFICE_EXPIRATION);
|
||||||
|
signalActivity();
|
||||||
|
db.execute("NOTIFY_OFFICE_EXPIRATION");
|
||||||
|
signalActivity();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PoliticsWorker::notifyElectionCreated(const std::vector<std::pair<int,int>>& elections) {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
|
||||||
|
db.prepare("NOTIFY_ELECTION_CREATED", QUERY_NOTIFY_ELECTION_CREATED);
|
||||||
|
for (const auto &pr : elections) {
|
||||||
|
signalActivity();
|
||||||
|
db.execute("NOTIFY_ELECTION_CREATED", { std::to_string(pr.first) });
|
||||||
|
signalActivity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PoliticsWorker::notifyOfficeFilled(const std::vector<std::tuple<int,int,int,int>>& newOffices) {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
|
||||||
|
db.prepare("NOTIFY_OFFICE_FILLED", QUERY_NOTIFY_OFFICE_FILLED);
|
||||||
|
for (const auto &tup : newOffices) {
|
||||||
|
int characterId = std::get<2>(tup);
|
||||||
|
signalActivity();
|
||||||
|
db.execute("NOTIFY_OFFICE_FILLED", { std::to_string(characterId) });
|
||||||
|
signalActivity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::tuple<int,int,int,int>> PoliticsWorker::processElections() {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
db.prepare("PROCESS_ELECTIONS", QUERY_PROCESS_ELECTIONS);
|
||||||
|
auto result = db.execute("PROCESS_ELECTIONS", {});
|
||||||
|
std::vector<std::tuple<int,int,int,int>> created;
|
||||||
|
for (auto const &row : result) {
|
||||||
|
int officeId = std::stoi(row.at("office_id"));
|
||||||
|
int officeTypeId = std::stoi(row.at("office_type_id"));
|
||||||
|
int characterId = std::stoi(row.at("character_id"));
|
||||||
|
int regionId = std::stoi(row.at("region_id"));
|
||||||
|
created.emplace_back(officeId, officeTypeId, characterId, regionId);
|
||||||
|
}
|
||||||
|
return created;
|
||||||
|
}
|
||||||
460
src/politics_worker.h
Normal file
460
src/politics_worker.h
Normal file
@@ -0,0 +1,460 @@
|
|||||||
|
// File: politics_worker.h
|
||||||
|
|
||||||
|
#ifndef POLITICS_WORKER_H
|
||||||
|
#define POLITICS_WORKER_H
|
||||||
|
|
||||||
|
#include "worker.h"
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
class PoliticsWorker : public Worker {
|
||||||
|
public:
|
||||||
|
PoliticsWorker(ConnectionPool &pool, MessageBroker &broker);
|
||||||
|
~PoliticsWorker() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void performDailyPoliticsTask();
|
||||||
|
|
||||||
|
void evaluatePoliticalPositions(
|
||||||
|
std::unordered_map<int,int>& requiredPerRegion,
|
||||||
|
std::unordered_map<int,int>& occupiedPerRegion
|
||||||
|
);
|
||||||
|
|
||||||
|
std::vector<std::tuple<int,int,int>> scheduleElections();
|
||||||
|
std::vector<std::tuple<int,int,int,int>> processExpiredOfficesAndFill();
|
||||||
|
std::vector<int> getUserIdsInCitiesOfRegions(const std::vector<int>& regionIds);
|
||||||
|
|
||||||
|
void notifyOfficeExpirations();
|
||||||
|
void notifyElectionCreated(const std::vector<std::pair<int,int>>& elections);
|
||||||
|
void notifyOfficeFilled(const std::vector<std::tuple<int,int,int,int>>& newOffices);
|
||||||
|
std::vector<std::tuple<int, int, int, int> > processElections();
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// QUERY: Zähle pro Region, wie viele Sitze vorgesehen vs. besetzt sind
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
static constexpr const char* QUERY_COUNT_OFFICES_PER_REGION = R"(
|
||||||
|
WITH
|
||||||
|
seats_per_region AS (
|
||||||
|
SELECT
|
||||||
|
pot.id AS office_type_id,
|
||||||
|
rt.id AS region_id,
|
||||||
|
pot.seats_per_region AS seats_total
|
||||||
|
FROM
|
||||||
|
falukant_type.political_office_type AS pot
|
||||||
|
JOIN
|
||||||
|
falukant_type.region AS rt
|
||||||
|
ON pot.region_type = rt.label_tr
|
||||||
|
),
|
||||||
|
occupied AS (
|
||||||
|
SELECT
|
||||||
|
po.office_type_id,
|
||||||
|
po.region_id,
|
||||||
|
COUNT(*) AS occupied_count
|
||||||
|
FROM
|
||||||
|
falukant_data.political_office AS po
|
||||||
|
GROUP BY
|
||||||
|
po.office_type_id, po.region_id
|
||||||
|
),
|
||||||
|
combined AS (
|
||||||
|
SELECT
|
||||||
|
spr.region_id,
|
||||||
|
spr.seats_total AS required_count,
|
||||||
|
COALESCE(o.occupied_count, 0) AS occupied_count
|
||||||
|
FROM
|
||||||
|
seats_per_region AS spr
|
||||||
|
LEFT JOIN
|
||||||
|
occupied AS o
|
||||||
|
ON spr.office_type_id = o.office_type_id
|
||||||
|
AND spr.region_id = o.region_id
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
region_id,
|
||||||
|
SUM(required_count) AS required_count,
|
||||||
|
SUM(occupied_count) AS occupied_count
|
||||||
|
FROM combined
|
||||||
|
GROUP BY region_id;
|
||||||
|
)";
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// STEP 1: Erzeuge nur diejenigen Wahlen, bei denen noch keine Election
|
||||||
|
// für denselben Termin (NOW()+2 Tage) existiert.
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
static constexpr const char* QUERY_SELECT_NEEDED_ELECTIONS = R"(
|
||||||
|
WITH
|
||||||
|
-- 1) Definiere das heutige Datum einmal als Referenz
|
||||||
|
target_date AS (
|
||||||
|
SELECT NOW()::date AS election_date
|
||||||
|
),
|
||||||
|
|
||||||
|
-- 2) Lösche nur diejenigen Ämter, deren Ablaufdatum heute erreicht ist,
|
||||||
|
-- und merke deren (office_type_id, region_id)
|
||||||
|
expired_today AS (
|
||||||
|
DELETE FROM falukant_data.political_office AS po
|
||||||
|
USING falukant_type.political_office_type AS pot
|
||||||
|
WHERE po.office_type_id = pot.id
|
||||||
|
AND (po.created_at + (pot.term_length * INTERVAL '1 day'))::date = (SELECT election_date FROM target_date)
|
||||||
|
RETURNING
|
||||||
|
pot.id AS office_type_id,
|
||||||
|
po.region_id AS region_id
|
||||||
|
),
|
||||||
|
|
||||||
|
-- 3) Gruppiere nach Typ+Region und zähle, wie viele Sitze heute frei geworden sind
|
||||||
|
gaps_per_region AS (
|
||||||
|
SELECT
|
||||||
|
office_type_id,
|
||||||
|
region_id,
|
||||||
|
COUNT(*) AS gaps
|
||||||
|
FROM expired_today
|
||||||
|
GROUP BY office_type_id, region_id
|
||||||
|
),
|
||||||
|
|
||||||
|
-- 4) Filtere nur diejenigen Typ+Region‐Kombinationen, für die noch **keine** Election
|
||||||
|
-- mit genau demselben Datum angelegt wurde
|
||||||
|
to_schedule AS (
|
||||||
|
SELECT
|
||||||
|
g.office_type_id,
|
||||||
|
g.region_id,
|
||||||
|
g.gaps,
|
||||||
|
td.election_date
|
||||||
|
FROM
|
||||||
|
gaps_per_region AS g
|
||||||
|
CROSS JOIN
|
||||||
|
target_date AS td
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM falukant_data.election AS e
|
||||||
|
WHERE e.office_type_id = g.office_type_id
|
||||||
|
AND e.region_id = g.region_id
|
||||||
|
AND e."date"::date = td.election_date
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
-- 5) Lege für jede so gefilterte Kombination genau eine Election an
|
||||||
|
new_elections AS (
|
||||||
|
INSERT INTO falukant_data.election
|
||||||
|
(office_type_id, "date", posts_to_fill, created_at, updated_at, region_id)
|
||||||
|
SELECT
|
||||||
|
ts.office_type_id,
|
||||||
|
ts.election_date AS "date",
|
||||||
|
ts.gaps AS posts_to_fill,
|
||||||
|
NOW() AS created_at,
|
||||||
|
NOW() AS updated_at,
|
||||||
|
ts.region_id
|
||||||
|
FROM
|
||||||
|
to_schedule AS ts
|
||||||
|
RETURNING
|
||||||
|
id AS election_id,
|
||||||
|
region_id,
|
||||||
|
posts_to_fill
|
||||||
|
)
|
||||||
|
|
||||||
|
-- 6) Gib alle neu angelegten Wahlen zurück
|
||||||
|
SELECT
|
||||||
|
ne.election_id,
|
||||||
|
ne.region_id,
|
||||||
|
ne.posts_to_fill
|
||||||
|
FROM
|
||||||
|
new_elections AS ne
|
||||||
|
ORDER BY
|
||||||
|
ne.region_id,
|
||||||
|
ne.election_id;
|
||||||
|
)";
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// 2) Fügt für eine gegebene Election genau LIMIT = ($3 * 2) Kandidaten ein:
|
||||||
|
// $1 = election_id, $2 = region_id, $3 = Anzahl der Sitze (posts_to_fill)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static constexpr const char* QUERY_INSERT_CANDIDATES = R"(
|
||||||
|
INSERT INTO falukant_data.candidate
|
||||||
|
(election_id, character_id, created_at, updated_at)
|
||||||
|
SELECT
|
||||||
|
$1 AS election_id,
|
||||||
|
sub.id AS character_id,
|
||||||
|
NOW() AS created_at,
|
||||||
|
NOW() AS updated_at
|
||||||
|
FROM (
|
||||||
|
WITH RECURSIVE region_tree AS (
|
||||||
|
SELECT r.id
|
||||||
|
FROM falukant_data.region AS r
|
||||||
|
WHERE r.id = $2
|
||||||
|
UNION ALL
|
||||||
|
SELECT r2.id
|
||||||
|
FROM falukant_data.region AS r2
|
||||||
|
JOIN region_tree AS rt
|
||||||
|
ON r2.parent_id = rt.id
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
ch.id
|
||||||
|
FROM
|
||||||
|
falukant_data."character" AS ch
|
||||||
|
JOIN
|
||||||
|
region_tree AS rt2
|
||||||
|
ON ch.region_id = rt2.id
|
||||||
|
WHERE
|
||||||
|
ch.user_id IS NULL
|
||||||
|
AND ch.birthdate <= NOW() - INTERVAL '21 days'
|
||||||
|
AND ch.title_of_nobility IN (
|
||||||
|
SELECT id
|
||||||
|
FROM falukant_type.title
|
||||||
|
WHERE label_tr != 'noncivil'
|
||||||
|
)
|
||||||
|
ORDER BY RANDOM()
|
||||||
|
LIMIT ($3 * 2)
|
||||||
|
) AS sub(id);
|
||||||
|
)";
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// STEP 2: Füge eine einzelne neue Election ein und liefere die neue election_id
|
||||||
|
// $1 = office_type_id
|
||||||
|
// $2 = gaps (posts_to_fill)
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
static constexpr const char* QUERY_INSERT_ELECTION = R"(
|
||||||
|
INSERT INTO falukant_data.election
|
||||||
|
(office_type_id, "date", posts_to_fill, created_at, updated_at)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
$1,
|
||||||
|
NOW() + INTERVAL '2 days',
|
||||||
|
$2,
|
||||||
|
NOW(),
|
||||||
|
NOW()
|
||||||
|
)
|
||||||
|
RETURNING id;
|
||||||
|
)";
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// QUERY: Process Expired Offices & Refill (Winner + Random)
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
static constexpr const char* QUERY_PROCESS_EXPIRED_AND_FILL = R"(
|
||||||
|
WITH
|
||||||
|
expired_offices AS (
|
||||||
|
DELETE FROM falukant_data.political_office AS po
|
||||||
|
USING falukant_type.political_office_type AS pot
|
||||||
|
WHERE po.office_type_id = pot.id
|
||||||
|
AND (po.created_at + (pot.term_length * INTERVAL '1 day')) <= NOW()
|
||||||
|
RETURNING
|
||||||
|
pot.id AS office_type_id,
|
||||||
|
po.region_id AS region_id
|
||||||
|
),
|
||||||
|
distinct_types AS (
|
||||||
|
SELECT DISTINCT
|
||||||
|
office_type_id,
|
||||||
|
region_id
|
||||||
|
FROM expired_offices
|
||||||
|
),
|
||||||
|
votes_per_candidate AS (
|
||||||
|
SELECT
|
||||||
|
dt.office_type_id,
|
||||||
|
dt.region_id,
|
||||||
|
c.character_id,
|
||||||
|
COUNT(v.id) AS vote_count
|
||||||
|
FROM distinct_types AS dt
|
||||||
|
JOIN falukant_data.election AS e
|
||||||
|
ON e.office_type_id = dt.office_type_id
|
||||||
|
JOIN falukant_data.vote AS v
|
||||||
|
ON v.election_id = e.id
|
||||||
|
JOIN falukant_data.candidate AS c
|
||||||
|
ON c.election_id = e.id
|
||||||
|
AND c.id = v.candidate_id
|
||||||
|
WHERE e."date" >= (NOW() - INTERVAL '30 days')
|
||||||
|
GROUP BY
|
||||||
|
dt.office_type_id,
|
||||||
|
dt.region_id,
|
||||||
|
c.character_id
|
||||||
|
),
|
||||||
|
ranked_winners AS (
|
||||||
|
SELECT
|
||||||
|
vpc.office_type_id,
|
||||||
|
vpc.region_id,
|
||||||
|
vpc.character_id,
|
||||||
|
ROW_NUMBER() OVER (
|
||||||
|
PARTITION BY vpc.office_type_id, vpc.region_id
|
||||||
|
ORDER BY vpc.vote_count DESC
|
||||||
|
) AS rn
|
||||||
|
FROM votes_per_candidate AS vpc
|
||||||
|
),
|
||||||
|
selected_winners AS (
|
||||||
|
SELECT
|
||||||
|
rw.office_type_id,
|
||||||
|
rw.region_id,
|
||||||
|
rw.character_id
|
||||||
|
FROM ranked_winners AS rw
|
||||||
|
JOIN falukant_type.political_office_type AS pot
|
||||||
|
ON pot.id = rw.office_type_id
|
||||||
|
WHERE rw.rn <= pot.seats_per_region
|
||||||
|
),
|
||||||
|
insert_winners AS (
|
||||||
|
INSERT INTO falukant_data.political_office
|
||||||
|
(office_type_id, character_id, created_at, updated_at, region_id)
|
||||||
|
SELECT
|
||||||
|
sw.office_type_id,
|
||||||
|
sw.character_id,
|
||||||
|
NOW() AS created_at,
|
||||||
|
NOW() AS updated_at,
|
||||||
|
sw.region_id
|
||||||
|
FROM selected_winners AS sw
|
||||||
|
RETURNING
|
||||||
|
id AS new_office_id,
|
||||||
|
office_type_id,
|
||||||
|
character_id,
|
||||||
|
region_id
|
||||||
|
),
|
||||||
|
count_inserted AS (
|
||||||
|
SELECT
|
||||||
|
office_type_id,
|
||||||
|
region_id,
|
||||||
|
COUNT(*) AS inserted_count
|
||||||
|
FROM insert_winners
|
||||||
|
GROUP BY office_type_id, region_id
|
||||||
|
),
|
||||||
|
needed_to_fill AS (
|
||||||
|
SELECT
|
||||||
|
dt.office_type_id,
|
||||||
|
dt.region_id,
|
||||||
|
(pot.seats_per_region - COALESCE(ci.inserted_count, 0)) AS gaps
|
||||||
|
FROM distinct_types AS dt
|
||||||
|
JOIN falukant_type.political_office_type AS pot
|
||||||
|
ON pot.id = dt.office_type_id
|
||||||
|
LEFT JOIN count_inserted AS ci
|
||||||
|
ON ci.office_type_id = dt.office_type_id
|
||||||
|
AND ci.region_id = dt.region_id
|
||||||
|
WHERE (pot.seats_per_region - COALESCE(ci.inserted_count, 0)) > 0
|
||||||
|
),
|
||||||
|
random_candidates AS (
|
||||||
|
SELECT
|
||||||
|
rtf.office_type_id,
|
||||||
|
rtf.region_id,
|
||||||
|
ch.id AS character_id,
|
||||||
|
ROW_NUMBER() OVER (
|
||||||
|
PARTITION BY rtf.office_type_id, rtf.region_id
|
||||||
|
ORDER BY RANDOM()
|
||||||
|
) AS rn
|
||||||
|
FROM needed_to_fill AS rtf
|
||||||
|
JOIN falukant_data."character" AS ch
|
||||||
|
ON ch.region_id = rtf.region_id
|
||||||
|
AND ch.user_id IS NULL
|
||||||
|
AND ch.birthdate <= NOW() - INTERVAL '21 days'
|
||||||
|
AND ch.title_of_nobility IN (
|
||||||
|
SELECT id FROM falukant_type.title WHERE label_tr != 'noncivil'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM falukant_data.political_office AS po2
|
||||||
|
JOIN falukant_type.political_office_type AS pot2
|
||||||
|
ON pot2.id = po2.office_type_id
|
||||||
|
WHERE po2.character_id = ch.id
|
||||||
|
AND (po2.created_at + (pot2.term_length * INTERVAL '1 day')) > NOW() + INTERVAL '2 days'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
insert_random AS (
|
||||||
|
INSERT INTO falukant_data.political_office
|
||||||
|
(office_type_id, character_id, created_at, updated_at, region_id)
|
||||||
|
SELECT
|
||||||
|
rc.office_type_id,
|
||||||
|
rc.character_id,
|
||||||
|
NOW() AS created_at,
|
||||||
|
NOW() AS updated_at,
|
||||||
|
rc.region_id
|
||||||
|
FROM random_candidates AS rc
|
||||||
|
JOIN needed_to_fill AS rtf
|
||||||
|
ON rtf.office_type_id = rc.office_type_id
|
||||||
|
AND rtf.region_id = rc.region_id
|
||||||
|
WHERE rc.rn <= rtf.gaps
|
||||||
|
RETURNING
|
||||||
|
id AS new_office_id,
|
||||||
|
office_type_id,
|
||||||
|
character_id,
|
||||||
|
region_id
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
new_office_id AS office_id,
|
||||||
|
office_type_id,
|
||||||
|
character_id,
|
||||||
|
region_id
|
||||||
|
FROM insert_winners
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
new_office_id AS office_id,
|
||||||
|
office_type_id,
|
||||||
|
character_id,
|
||||||
|
region_id
|
||||||
|
FROM insert_random;
|
||||||
|
)";
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// QUERY: Hole User-IDs in allen Cities untergeordneter Regionen:
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
static constexpr const char* QUERY_USERS_IN_CITIES_OF_REGIONS = R"(
|
||||||
|
WITH RECURSIVE region_tree AS (
|
||||||
|
SELECT id
|
||||||
|
FROM falukant_data.region
|
||||||
|
WHERE id = $1
|
||||||
|
UNION ALL
|
||||||
|
SELECT r2.id
|
||||||
|
FROM falukant_data.region AS r2
|
||||||
|
JOIN region_tree AS rt
|
||||||
|
ON r2.parent_id = rt.id
|
||||||
|
)
|
||||||
|
SELECT DISTINCT
|
||||||
|
ch.user_id
|
||||||
|
FROM
|
||||||
|
falukant_data."character" AS ch
|
||||||
|
JOIN
|
||||||
|
region_tree AS rt2
|
||||||
|
ON ch.region_id = rt2.id
|
||||||
|
WHERE
|
||||||
|
ch.user_id IS NOT NULL;
|
||||||
|
)";
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// QUERY: Benachrichtige User, deren Amt in 2 Tagen abläuft
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
static constexpr const char* QUERY_NOTIFY_OFFICE_EXPIRATION = R"(
|
||||||
|
INSERT INTO falukant_log.notification
|
||||||
|
(user_id, tr, created_at, updated_at)
|
||||||
|
SELECT
|
||||||
|
po.character_id,
|
||||||
|
'notify_office_expiring',
|
||||||
|
NOW(), NOW()
|
||||||
|
FROM
|
||||||
|
falukant_data.political_office AS po
|
||||||
|
JOIN
|
||||||
|
falukant_type.political_office_type AS pot
|
||||||
|
ON po.office_type_id = pot.id
|
||||||
|
WHERE
|
||||||
|
(po.created_at + (pot.term_length * INTERVAL '1 day'))
|
||||||
|
BETWEEN (NOW() + INTERVAL '2 days')
|
||||||
|
AND (NOW() + INTERVAL '2 days' + INTERVAL '1 second');
|
||||||
|
)";
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// QUERY: Benachrichtige User, wenn Election angelegt wurde
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
static constexpr const char* QUERY_NOTIFY_ELECTION_CREATED = R"(
|
||||||
|
INSERT INTO falukant_log.notification
|
||||||
|
(user_id, tr, created_at, updated_at)
|
||||||
|
VALUES
|
||||||
|
($1, 'notify_election_created', NOW(), NOW());
|
||||||
|
)";
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// QUERY: Benachrichtige User, wenn Amt neu besetzt wurde
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
static constexpr const char* QUERY_NOTIFY_OFFICE_FILLED = R"(
|
||||||
|
INSERT INTO falukant_log.notification
|
||||||
|
(user_id, tr, created_at, updated_at)
|
||||||
|
VALUES
|
||||||
|
($1, 'notify_office_filled', NOW(), NOW());
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char* QUERY_PROCESS_ELECTIONS = R"(
|
||||||
|
SELECT office_id, office_type_id, character_id, region_id
|
||||||
|
FROM falukant_data.process_elections();
|
||||||
|
)";
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // POLITICS_WORKER_H
|
||||||
@@ -1,14 +1,12 @@
|
|||||||
#include "produce_worker.h"
|
#include "produce_worker.h"
|
||||||
#include "connection_guard.h" // Include for ConnectionGuard
|
#include "connection_guard.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
ProduceWorker::ProduceWorker(ConnectionPool &pool, MessageBroker &broker)
|
ProduceWorker::ProduceWorker(ConnectionPool &pool, MessageBroker &broker)
|
||||||
: Worker(pool, broker, "ProduceWorker")
|
: Worker(pool, broker, "ProduceWorker") {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ProduceWorker::~ProduceWorker() {
|
ProduceWorker::~ProduceWorker() {
|
||||||
}
|
}
|
||||||
@@ -43,10 +41,8 @@ void ProduceWorker::processProductions() {
|
|||||||
setCurrentStep("Get Database Connection");
|
setCurrentStep("Get Database Connection");
|
||||||
ConnectionGuard connGuard(pool);
|
ConnectionGuard connGuard(pool);
|
||||||
auto &db = connGuard.get();
|
auto &db = connGuard.get();
|
||||||
|
|
||||||
setCurrentStep("Fetch Finished Productions");
|
setCurrentStep("Fetch Finished Productions");
|
||||||
auto finishedProductions = getFinishedProductions(db);
|
auto finishedProductions = getFinishedProductions(db);
|
||||||
|
|
||||||
setCurrentStep("Process Finished Productions");
|
setCurrentStep("Process Finished Productions");
|
||||||
for (const auto &production : finishedProductions) {
|
for (const auto &production : finishedProductions) {
|
||||||
if (production.find("branch_id") == production.end() ||
|
if (production.find("branch_id") == production.end() ||
|
||||||
@@ -56,16 +52,20 @@ void ProduceWorker::processProductions() {
|
|||||||
production.find("user_id") == production.end()) {
|
production.find("user_id") == production.end()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int branchId = std::stoi(production.at("branch_id"));
|
int branchId = std::stoi(production.at("branch_id"));
|
||||||
int productId = std::stoi(production.at("product_id"));
|
int productId = std::stoi(production.at("product_id"));
|
||||||
int quantity = std::stoi(production.at("quantity"));
|
int quantity = std::stoi(production.at("quantity"));
|
||||||
int quality = std::stoi(production.at("quality"));
|
int quality = std::stoi(production.at("quality"));
|
||||||
int userId = std::stoi(production.at("user_id"));
|
int userId = std::stoi(production.at("user_id"));
|
||||||
|
int regionId = std::stoi(production.at("region_id"));
|
||||||
if (addToInventory(db, branchId, productId, quantity, quality, userId)) {
|
addToInventory(db, branchId, productId, quantity, quality, userId);
|
||||||
}
|
|
||||||
deleteProduction(db, production.at("production_id"));
|
deleteProduction(db, production.at("production_id"));
|
||||||
|
addProductionToLog(regionId, userId, productId, quantity);
|
||||||
|
const nlohmann::json message = {
|
||||||
|
{"event", "production_ready"},
|
||||||
|
{"branch_id", std::to_string(branchId) }
|
||||||
|
};
|
||||||
|
sendMessageToFalukantUsers(userId, message);
|
||||||
}
|
}
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
std::cerr << "[ProduceWorker] Fehler in processProductions: " << e.what() << std::endl;
|
std::cerr << "[ProduceWorker] Fehler in processProductions: " << e.what() << std::endl;
|
||||||
@@ -83,13 +83,12 @@ std::vector<std::unordered_map<std::string, std::string>> ProduceWorker::getFini
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProduceWorker::addToInventory(Database &db,
|
bool ProduceWorker::addToInventory(Database &db,
|
||||||
int branchId,
|
int branchId,
|
||||||
int productId,
|
int productId,
|
||||||
int quantity,
|
int quantity,
|
||||||
int quality,
|
int quality,
|
||||||
int userId)
|
int userId) {
|
||||||
{
|
|
||||||
try {
|
try {
|
||||||
db.prepare("get_stocks", QUERY_GET_AVAILABLE_STOCKS);
|
db.prepare("get_stocks", QUERY_GET_AVAILABLE_STOCKS);
|
||||||
auto stocks = db.execute("get_stocks", {std::to_string(branchId)});
|
auto stocks = db.execute("get_stocks", {std::to_string(branchId)});
|
||||||
@@ -99,22 +98,29 @@ bool ProduceWorker::addToInventory(Database &db,
|
|||||||
int totalCapacity = std::stoi(stock.at("total_capacity"));
|
int totalCapacity = std::stoi(stock.at("total_capacity"));
|
||||||
int filledCapacity = std::stoi(stock.at("filled"));
|
int filledCapacity = std::stoi(stock.at("filled"));
|
||||||
int freeCapacity = totalCapacity - filledCapacity;
|
int freeCapacity = totalCapacity - filledCapacity;
|
||||||
|
|
||||||
if (freeCapacity <= 0) {
|
if (freeCapacity <= 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int toStore = std::min(remainingQuantity, freeCapacity);
|
int toStore = std::min(remainingQuantity, freeCapacity);
|
||||||
if (!storeInStock(db, stockId, productId, toStore, quality)) {
|
if (!storeInStock(db, stockId, productId, toStore, quality)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
remainingQuantity -= toStore;
|
remainingQuantity -= toStore;
|
||||||
sendProductionReadyEvent(userId, productId, quantity, quality, branchId);
|
|
||||||
if (remainingQuantity <= 0) {
|
if (remainingQuantity <= 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (remainingQuantity == 0);
|
if (remainingQuantity == 0) {
|
||||||
|
sendProductionReadyEvent(userId, productId, quantity, quality, branchId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
db.prepare("QUERY_ADD_OVERPRODUCTION_NOTIFICATION", QUERY_ADD_OVERPRODUCTION_NOTIFICATION);
|
||||||
|
nlohmann::json notification = {
|
||||||
|
{"tr", "production.overproduction"},
|
||||||
|
{"value", remainingQuantity}
|
||||||
|
};
|
||||||
|
db.execute("QUERY_ADD_OVERPRODUCTION_NOTIFICATION", {std::to_string(userId), notification.dump()});
|
||||||
|
return true;
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
std::cerr << "[ProduceWorker] Fehler in addToInventory: " << e.what() << std::endl;
|
std::cerr << "[ProduceWorker] Fehler in addToInventory: " << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
@@ -125,8 +131,7 @@ bool ProduceWorker::storeInStock(Database &db,
|
|||||||
int stockId,
|
int stockId,
|
||||||
int productId,
|
int productId,
|
||||||
int quantity,
|
int quantity,
|
||||||
int quality)
|
int quality) {
|
||||||
{
|
|
||||||
try {
|
try {
|
||||||
db.prepare("insert_inventory", QUERY_INSERT_INVENTORY);
|
db.prepare("insert_inventory", QUERY_INSERT_INVENTORY);
|
||||||
db.execute("insert_inventory", {std::to_string(stockId),
|
db.execute("insert_inventory", {std::to_string(stockId),
|
||||||
@@ -170,3 +175,15 @@ void ProduceWorker::sendProductionReadyEvent(int userId,
|
|||||||
<< e.what() << std::endl;
|
<< e.what() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProduceWorker::addProductionToLog(int regionId, int userId, int productId, int quantity) {
|
||||||
|
try {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
db.prepare("QUERY_INSERT_UPDATE_PRODUCTION_LOG", QUERY_INSERT_UPDATE_PRODUCTION_LOG);
|
||||||
|
db.execute("QUERY_INSERT_UPDATE_PRODUCTION_LOG", { std::to_string(regionId), std::to_string(productId),
|
||||||
|
std::to_string(productId), std::to_string(userId) });
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,38 +2,35 @@
|
|||||||
|
|
||||||
#include "worker.h"
|
#include "worker.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
class ProduceWorker : public Worker {
|
||||||
class ProduceWorker : public Worker
|
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
explicit ProduceWorker(ConnectionPool &pool, MessageBroker &broker);
|
explicit ProduceWorker(ConnectionPool &pool, MessageBroker &broker);
|
||||||
~ProduceWorker() override;
|
~ProduceWorker() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void run() override; // überschreibt Worker::run()
|
void run() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Fachlogik
|
|
||||||
void processProductions();
|
void processProductions();
|
||||||
std::vector<std::unordered_map<std::string, std::string>> getFinishedProductions(Database &db);
|
std::vector<std::unordered_map<std::string, std::string>> getFinishedProductions(Database &db);
|
||||||
bool addToInventory(Database &db, int branchId, int productId, int quantity, int quality, int userId);
|
bool addToInventory(Database &db, int branchId, int productId, int quantity, int quality, int userId);
|
||||||
bool storeInStock(Database &db, int stockId, int productId, int quantity, int quality);
|
bool storeInStock(Database &db, int stockId, int productId, int quantity, int quality);
|
||||||
void deleteProduction(Database &db, const std::string &productionId);
|
void deleteProduction(Database &db, const std::string &productionId);
|
||||||
void sendProductionReadyEvent(int userId, int productId, int quantity, int quality, int branchId);
|
void sendProductionReadyEvent(int userId, int productId, int quantity, int quality, int branchId);
|
||||||
|
void addProductionToLog(int regionId, int userId, int productId, int quantity);
|
||||||
|
|
||||||
static constexpr const char *QUERY_GET_FINISHED_PRODUCTIONS = R"(
|
static constexpr const char *QUERY_GET_FINISHED_PRODUCTIONS = R"(
|
||||||
SELECT
|
SELECT DISTINCT
|
||||||
p.id AS production_id,
|
p.id AS production_id,
|
||||||
p.branch_id,
|
p.branch_id,
|
||||||
p.product_id,
|
p.product_id,
|
||||||
p.quantity,
|
p.quantity,
|
||||||
p.start_timestamp,
|
p.start_timestamp,
|
||||||
pr.production_time,
|
pr.production_time,
|
||||||
k.character_id,
|
k.character_id,
|
||||||
k.knowledge AS quality,
|
case when k2.id is not null then (k.knowledge * 2 + k2.knowledge) / 3 else k.knowledge end AS quality,
|
||||||
br.region_id,
|
br.region_id,
|
||||||
br.falukant_user_id user_id
|
br.falukant_user_id user_id
|
||||||
FROM falukant_data.production p
|
FROM falukant_data.production p
|
||||||
@@ -42,6 +39,8 @@ private:
|
|||||||
JOIN falukant_data.character c ON c.user_id = br.falukant_user_id
|
JOIN falukant_data.character c ON c.user_id = br.falukant_user_id
|
||||||
JOIN falukant_data.knowledge k ON p.product_id = k.product_id AND k.character_id = c.id
|
JOIN falukant_data.knowledge k ON p.product_id = k.product_id AND k.character_id = c.id
|
||||||
JOIN falukant_data.stock s ON s.branch_id = br.id
|
JOIN falukant_data.stock s ON s.branch_id = br.id
|
||||||
|
LEFT JOIN falukant_data.director d on d.employer_user_id = c.user_id
|
||||||
|
LEFT JOIN falukant_data.knowledge k2 on k2.character_id = d.director_character_id and k2.product_id = p.product_id
|
||||||
WHERE p.start_timestamp + interval '1 minute' * pr.production_time <= NOW()
|
WHERE p.start_timestamp + interval '1 minute' * pr.production_time <= NOW()
|
||||||
ORDER BY p.start_timestamp;
|
ORDER BY p.start_timestamp;
|
||||||
)";
|
)";
|
||||||
@@ -67,4 +66,24 @@ private:
|
|||||||
INSERT INTO falukant_data.inventory (stock_id, product_id, quantity, quality, produced_at)
|
INSERT INTO falukant_data.inventory (stock_id, product_id, quantity, quality, produced_at)
|
||||||
VALUES ($1, $2, $3, $4, NOW());
|
VALUES ($1, $2, $3, $4, NOW());
|
||||||
)";
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_INSERT_UPDATE_PRODUCTION_LOG = R"(
|
||||||
|
INSERT INTO falukant_log.production (
|
||||||
|
region_id,
|
||||||
|
product_id,
|
||||||
|
quantity,
|
||||||
|
producer_id,
|
||||||
|
production_date
|
||||||
|
)
|
||||||
|
VALUES ($1, $2, $3, $4, CURRENT_DATE)
|
||||||
|
ON CONFLICT (producer_id, product_id, region_id, production_date)
|
||||||
|
DO UPDATE
|
||||||
|
SET quantity = falukant_log.production.quantity + EXCLUDED.quantity;
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_ADD_OVERPRODUCTION_NOTIFICATION = R"(
|
||||||
|
INSERT INTO falukant_log.notification
|
||||||
|
(user_id, tr, shown, created_at, updated_at)
|
||||||
|
VALUES($1, $2, false, now(), now());
|
||||||
|
)";
|
||||||
};
|
};
|
||||||
|
|||||||
98
src/stockagemanager.cpp
Normal file
98
src/stockagemanager.cpp
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#include "stockagemanager.h"
|
||||||
|
#include "connection_guard.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <random>
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
StockageManager::StockageManager(ConnectionPool &pool, MessageBroker &broker)
|
||||||
|
: Worker(pool, broker, "StockageManager") {}
|
||||||
|
|
||||||
|
StockageManager::~StockageManager() {
|
||||||
|
addStocksRunning = false;
|
||||||
|
if (addStocksThread.joinable()) addStocksThread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StockageManager::run() {
|
||||||
|
addStocksThread = std::thread([this]() { addLocalStocks(); });
|
||||||
|
while (runningWorker) {
|
||||||
|
setCurrentStep("Main loop: Running...");
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
signalActivity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StockageManager::addLocalStocks() {
|
||||||
|
auto lastExecutionTime = std::chrono::steady_clock::now();
|
||||||
|
std::uniform_real_distribution<> dist(0.0, 1.0);
|
||||||
|
while (addStocksRunning) {
|
||||||
|
signalActivity();
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - lastExecutionTime).count();
|
||||||
|
if (elapsed >= 60) {
|
||||||
|
try {
|
||||||
|
setCurrentStep("Add Local Stocks: Fetch Town IDs");
|
||||||
|
auto townIds = getTownIds();
|
||||||
|
for (const auto &townId : townIds) {
|
||||||
|
std::mt19937 gen(std::random_device{}());
|
||||||
|
double chance = round(dist(gen) * 2160);
|
||||||
|
if (chance <= 1) {
|
||||||
|
addStockForTown(townId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
std::cerr << "[StockageManager] Fehler in addLocalStocks: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
lastExecutionTime = now;
|
||||||
|
}
|
||||||
|
cleanupBuyableSotck();
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<int> StockageManager::getTownIds() {
|
||||||
|
try {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
db.prepare("get_towns", QUERY_GET_TOWNS);
|
||||||
|
const auto towns = db.execute("get_towns");
|
||||||
|
std::vector<int> townIds;
|
||||||
|
for (const auto &town: towns) {
|
||||||
|
auto id = town.at("id");
|
||||||
|
townIds.push_back(std::stoi(id));
|
||||||
|
}
|
||||||
|
return townIds;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
std::cerr << "[ProduceWorker] Fehler beim Abrufen abgeschlossener Produktionen: "
|
||||||
|
<< e.what() << std::endl;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void StockageManager::addStockForTown(int townId) {
|
||||||
|
try {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
db.prepare("add_stock", QUERY_INSERT_STOCK);
|
||||||
|
db.execute("add_stock", {std::to_string(townId)});
|
||||||
|
nlohmann::json message = {
|
||||||
|
{"event", "stock_change"},
|
||||||
|
{"branch", std::to_string(townId) }
|
||||||
|
};
|
||||||
|
sendMessageToRegionUsers(townId, message);
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
std::cerr << "[StockageManager] Fehler in addStockForTown: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StockageManager::cleanupBuyableSotck() {
|
||||||
|
try {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
db.prepare("cleanup_stock", QUERY_CLEANUP_STOCK);
|
||||||
|
db.execute("cleanup_stock", {});
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
std::cerr << "[StockageManager] Fehler bei stock cleanup: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/stockagemanager.h
Normal file
59
src/stockagemanager.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "worker.h"
|
||||||
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
class StockageManager : public Worker {
|
||||||
|
public:
|
||||||
|
explicit StockageManager(ConnectionPool &pool, MessageBroker &broker);
|
||||||
|
~StockageManager() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void addLocalStocks();
|
||||||
|
std::vector<int> getTownIds();
|
||||||
|
void addStockForTown(int townId);
|
||||||
|
|
||||||
|
std::atomic<bool> addStocksRunning{true};
|
||||||
|
std::thread addStocksThread;
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_GET_TOWNS = R"(
|
||||||
|
SELECT fdr.id
|
||||||
|
from falukant_data.region fdr
|
||||||
|
join falukant_type.region ftr
|
||||||
|
on ftr.id = fdr.region_type_id
|
||||||
|
where ftr.label_tr = 'city'
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_INSERT_STOCK = R"(
|
||||||
|
INSERT INTO falukant_data.buyable_stock (region_id, stock_type_id, quantity)
|
||||||
|
SELECT
|
||||||
|
$1 AS region_id,
|
||||||
|
s.id AS stock_type_id,
|
||||||
|
GREATEST(1, ROUND(RANDOM() * 5 * COUNT(br.id))) AS quantity
|
||||||
|
FROM
|
||||||
|
falukant_data.branch AS br
|
||||||
|
CROSS JOIN
|
||||||
|
falukant_type.stock AS s
|
||||||
|
WHERE
|
||||||
|
br.region_id = $1
|
||||||
|
GROUP BY
|
||||||
|
s.id
|
||||||
|
ORDER BY
|
||||||
|
RANDOM()
|
||||||
|
LIMIT
|
||||||
|
GREATEST(
|
||||||
|
ROUND(RANDOM() * (SELECT COUNT(id) FROM falukant_type.stock)),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_CLEANUP_STOCK = R"(
|
||||||
|
delete from falukant_data.buyable_stock
|
||||||
|
where quantity <= 0
|
||||||
|
)";
|
||||||
|
void cleanupBuyableSotck();
|
||||||
|
};
|
||||||
361
src/usercharacterworker.cpp
Normal file
361
src/usercharacterworker.cpp
Normal file
@@ -0,0 +1,361 @@
|
|||||||
|
#include "usercharacterworker.h"
|
||||||
|
#include "connection_guard.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
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();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
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 (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();
|
||||||
|
|
||||||
|
db.prepare("delete_character", "DELETE FROM falukant_data.character WHERE id = $1");
|
||||||
|
db.execute("delete_character", { std::to_string(characterId) });
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (auto const &row : rows) {
|
||||||
|
int fatherCid = std::stoi(row.at("father_cid"));
|
||||||
|
int motherCid = std::stoi(row.at("mother_cid"));
|
||||||
|
int fatherUid = std::stoi(row.at("father_uid"));
|
||||||
|
int motherUid = std::stoi(row.at("mother_uid"));
|
||||||
|
int titleOfNobility = std::stoi(row.at("title_of_nobility"));
|
||||||
|
int lastName = std::stoi(row.at("last_name"));
|
||||||
|
int regionId = std::stoi(row.at("region_id"));
|
||||||
|
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
|
||||||
|
});
|
||||||
|
int childCid = std::stoi(resChild.front().at("child_cid"));
|
||||||
|
db.prepare("insert_relation", QUERY_INSERT_CHILD_RELATION);
|
||||||
|
auto resRel = db.execute("insert_relation", {
|
||||||
|
std::to_string(fatherCid),
|
||||||
|
std::to_string(motherCid),
|
||||||
|
std::to_string(childCid)
|
||||||
|
});
|
||||||
|
const nlohmann::json message = {{"event", "children_update"}};
|
||||||
|
sendMessageToFalukantUsers(fatherUid, message);
|
||||||
|
sendMessageToFalukantUsers(motherUid, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
374
src/usercharacterworker.h
Normal file
374
src/usercharacterworker.h
Normal file
@@ -0,0 +1,374 @@
|
|||||||
|
#ifndef USERCHARACTERWORKER_H
|
||||||
|
#define USERCHARACTERWORKER_H
|
||||||
|
|
||||||
|
#include "worker.h"
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
class UserCharacterWorker : public Worker {
|
||||||
|
public:
|
||||||
|
UserCharacterWorker(ConnectionPool &pool, MessageBroker &broker);
|
||||||
|
~UserCharacterWorker() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Character {
|
||||||
|
int id;
|
||||||
|
int age;
|
||||||
|
int health;
|
||||||
|
};
|
||||||
|
|
||||||
|
void processCharacterEvents();
|
||||||
|
void updateCharacterHealth(Character& character);
|
||||||
|
void updateCharactersMood();
|
||||||
|
int calculateHealthChange(int age);
|
||||||
|
void handleCharacterDeath(int characterId);
|
||||||
|
void recalculateKnowledge();
|
||||||
|
void processPregnancies();
|
||||||
|
void handleCredits();
|
||||||
|
void setHeir(int characterId);
|
||||||
|
int getFalukantUserId(int characterId);
|
||||||
|
int getHeirFromChildren(int deceasedCharacterId);
|
||||||
|
int getRandomHeir(int deceasedCharacterId);
|
||||||
|
void setNewCharacter(int falukantUserId, int heirCharacterId);
|
||||||
|
void setNewMoney(int falukantUserId, double newAmount);
|
||||||
|
double getHouseValue(int falukantUserId);
|
||||||
|
double getSettlementValue(int falukantUserId);
|
||||||
|
double getInventoryValue(int falukantUserId);
|
||||||
|
double getCreditDebt(int falukantUserId);
|
||||||
|
double getCurrentMoney(int falukantUserId);
|
||||||
|
double calculateNewMoney(int falukantUserId, bool hasHeir);
|
||||||
|
int getChildCount(int deceasedUserId);
|
||||||
|
|
||||||
|
std::random_device rd;
|
||||||
|
std::mt19937 gen;
|
||||||
|
std::uniform_real_distribution<> dist;
|
||||||
|
bool didRunToday { false };
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_GET_USERS_TO_UPDATE = R"(
|
||||||
|
SELECT "id", CURRENT_DATE - birthdate::date AS age, "health"
|
||||||
|
FROM "falukant_data"."character"
|
||||||
|
WHERE "user_id" IS NOT NULL;
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_UPDATE_CHARACTERS_HEALTH = R"(
|
||||||
|
UPDATE "falukant_data"."character"
|
||||||
|
SET health = $1
|
||||||
|
WHERE id = $2
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_UPDATE_GET_ITEMS_TO_UPDATE = R"(
|
||||||
|
SELECT id, product_id, producer_id, quantity
|
||||||
|
FROM falukant_log.production p
|
||||||
|
WHERE p.production_timestamp::date < current_date
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_UPDATE_GET_CHARACTER_IDS = R"(
|
||||||
|
select fu.id user_id, c.id character_id, c2.id director_id
|
||||||
|
from falukant_data.falukant_user fu
|
||||||
|
join falukant_data."character" c
|
||||||
|
on c.user_id = fu.id
|
||||||
|
left join falukant_data.director d
|
||||||
|
on d.employer_user_id = fu.id
|
||||||
|
left join falukant_data."character" c2
|
||||||
|
on c2.id = d.director_character_id
|
||||||
|
where fu.id = $1
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_UPDATE_KNOWLEDGE = R"(
|
||||||
|
update falukant_data.knowledge
|
||||||
|
set knowledge = least(knowledge + $3, 100)
|
||||||
|
where character_id = $1
|
||||||
|
and product_id = $2
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_DELETE_LOG_ENTRY = R"(
|
||||||
|
delete from falukant_log.production
|
||||||
|
where id = $1
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr char const* QUERY_GET_PREGNANCY_CANDIDATES = R"(
|
||||||
|
SELECT
|
||||||
|
r.character1_id AS father_cid,
|
||||||
|
r.character2_id AS mother_cid,
|
||||||
|
c1.title_of_nobility,
|
||||||
|
c1.last_name,
|
||||||
|
c1.region_id,
|
||||||
|
fu1.id AS father_uid,
|
||||||
|
fu2.id AS mother_uid,
|
||||||
|
-- Durchschnittsalter in Tagen
|
||||||
|
((NOW()::date - c1.birthdate::date)
|
||||||
|
+ (NOW()::date - c2.birthdate::date)) / 2 AS avg_age_days,
|
||||||
|
-- Angepasste Schwangerschaftswahrscheinlichkeit in Prozent
|
||||||
|
100.0 /
|
||||||
|
(1
|
||||||
|
+ EXP(
|
||||||
|
0.0647 * (
|
||||||
|
((NOW()::date - c1.birthdate::date)
|
||||||
|
+ (NOW()::date - c2.birthdate::date)) / 2
|
||||||
|
)
|
||||||
|
- 0.0591
|
||||||
|
)
|
||||||
|
) AS prob_pct
|
||||||
|
FROM falukant_data.relationship r
|
||||||
|
JOIN falukant_type.relationship r2
|
||||||
|
ON r2.id = r.relationship_type_id
|
||||||
|
AND r2.tr = 'married'
|
||||||
|
JOIN falukant_data."character" c1
|
||||||
|
ON c1.id = r.character1_id
|
||||||
|
JOIN falukant_data."character" c2
|
||||||
|
ON c2.id = r.character2_id
|
||||||
|
LEFT JOIN falukant_data.falukant_user fu1
|
||||||
|
ON fu1.id = c1.user_id
|
||||||
|
LEFT JOIN falukant_data.falukant_user fu2
|
||||||
|
ON fu2.id = c2.user_id
|
||||||
|
WHERE random()*100 < (
|
||||||
|
100.0 /
|
||||||
|
(1
|
||||||
|
+ EXP(
|
||||||
|
0.11166347 * (
|
||||||
|
((NOW()::date - c1.birthdate::date)
|
||||||
|
+ (NOW()::date - c2.birthdate::date)) / 2
|
||||||
|
)
|
||||||
|
- 2.638267
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr char const* QUERY_INSERT_CHILD = R"(
|
||||||
|
INSERT INTO falukant_data."character" (
|
||||||
|
user_id,
|
||||||
|
region_id,
|
||||||
|
first_name,
|
||||||
|
last_name,
|
||||||
|
birthdate,
|
||||||
|
gender,
|
||||||
|
title_of_nobility,
|
||||||
|
mood_id,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
) VALUES (
|
||||||
|
NULL,
|
||||||
|
$1::int, -- region_id
|
||||||
|
/* zufälliger Vorname passend zum Gender */
|
||||||
|
(
|
||||||
|
SELECT id
|
||||||
|
FROM falukant_predefine.firstname
|
||||||
|
WHERE gender = $2
|
||||||
|
ORDER BY RANDOM()
|
||||||
|
LIMIT 1
|
||||||
|
),
|
||||||
|
$3::int, -- last_name (Eltern-Nachname)
|
||||||
|
NOW(),
|
||||||
|
$2::varchar, -- gender
|
||||||
|
$4::int, -- title_of_nobility
|
||||||
|
/* zufällige Stimmung */
|
||||||
|
(
|
||||||
|
SELECT id
|
||||||
|
FROM falukant_type.mood
|
||||||
|
ORDER BY RANDOM()
|
||||||
|
LIMIT 1
|
||||||
|
),
|
||||||
|
NOW(),
|
||||||
|
NOW()
|
||||||
|
)
|
||||||
|
RETURNING id AS child_cid
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr char const* QUERY_INSERT_CHILD_RELATION = R"(
|
||||||
|
-- QUERY_INSERT_CHILD_RELATION
|
||||||
|
INSERT INTO falukant_data.child_relation (
|
||||||
|
father_character_id,
|
||||||
|
mother_character_id,
|
||||||
|
child_character_id,
|
||||||
|
name_set,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
$1::int, -- father_cid
|
||||||
|
$2::int, -- mother_cid
|
||||||
|
$3::int, -- child_cid
|
||||||
|
false,
|
||||||
|
NOW(), NOW()
|
||||||
|
)
|
||||||
|
RETURNING
|
||||||
|
father_character_id,
|
||||||
|
-- Vater-User
|
||||||
|
(SELECT user_id FROM falukant_data."character" WHERE id = father_character_id) AS father_user_id,
|
||||||
|
mother_character_id,
|
||||||
|
-- Mutter-User
|
||||||
|
(SELECT user_id FROM falukant_data."character" WHERE id = mother_character_id) AS mother_user_id,
|
||||||
|
child_character_id,
|
||||||
|
-- Kind-User
|
||||||
|
(SELECT user_id FROM falukant_data."character" WHERE id = child_character_id) AS child_user_id;
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr char const* QUERY_AUTOBATISM = R"(
|
||||||
|
update falukant_data.child_relation
|
||||||
|
set name_set = true
|
||||||
|
where id in (
|
||||||
|
select cr.id
|
||||||
|
from falukant_data.child_relation cr
|
||||||
|
join falukant_data."character" c
|
||||||
|
on c.id = cr.child_character_id
|
||||||
|
where cr.name_set = false
|
||||||
|
and c.birthdate < current_date - interval '5 days'
|
||||||
|
)
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr char const* QUERY_UPDATE_MOOD = R"(
|
||||||
|
UPDATE falukant_data."character" AS c
|
||||||
|
SET mood_id = falukant_data.get_random_mood_id()
|
||||||
|
WHERE c.health > 0;
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr char const* QUERY_GET_OPEN_CREDITS = R"(
|
||||||
|
select c.id credit_id, c.amount, c.remaining_amount, c.interest_rate, fu.id user_id, fu."money", c2.id character_id, dp.created_at debitor_prism_start,
|
||||||
|
dp.created_at::date < current_date prism_started_previously
|
||||||
|
from falukant_data.credit c
|
||||||
|
join falukant_data.falukant_user fu
|
||||||
|
on fu.id = c.id
|
||||||
|
join falukant_data."character" c2
|
||||||
|
on c2.user_id = c.falukant_user_id
|
||||||
|
left join falukant_data.debtors_prism dp
|
||||||
|
on dp.character_id = c2.id
|
||||||
|
where c.remaining_amount > 0
|
||||||
|
and c.updated_at::date < current_date
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr char const* QUERY_UPDATE_CREDIT = R"(
|
||||||
|
update falukant_data.credit c
|
||||||
|
set remaining_amount = $1
|
||||||
|
where falukant_user_id = $2
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr char const* QUERY_CLEANUP_CREDITS = R"(
|
||||||
|
delete from falukant_data.credit
|
||||||
|
where remaining_amount >= 0.01
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr char const* QUERY_ADD_CHARACTER_TO_DEBTORS_PRISM = R"(
|
||||||
|
insert into falukant_data.debtors_prism (character_id) values ($1)
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char* QUERY_GET_HEIR = R"(
|
||||||
|
SELECT child_character_id
|
||||||
|
FROM falukant_data.child_relation
|
||||||
|
WHERE father_character_id = $1
|
||||||
|
OR mother_character_id = $1
|
||||||
|
ORDER BY (is_heir IS TRUE) DESC,
|
||||||
|
updated_at DESC
|
||||||
|
LIMIT 1
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char* QUERY_RANDOM_HEIR = R"(
|
||||||
|
WITH chosen AS (
|
||||||
|
SELECT
|
||||||
|
cr.id AS relation_id,
|
||||||
|
cr.child_character_id
|
||||||
|
FROM
|
||||||
|
falukant_data.child_relation AS cr
|
||||||
|
JOIN
|
||||||
|
falukant_data."character" AS ch
|
||||||
|
ON ch.id = cr.child_character_id
|
||||||
|
WHERE
|
||||||
|
(cr.father_character_id = $1 OR cr.mother_character_id = $1)
|
||||||
|
-- gleicher Wohnort wie der Verstorbene
|
||||||
|
AND ch.region_id = (
|
||||||
|
SELECT region_id
|
||||||
|
FROM falukant_data."character"
|
||||||
|
WHERE id = $1
|
||||||
|
)
|
||||||
|
-- nicht älter als 10 Tage
|
||||||
|
AND ch.birthdate >= NOW() - INTERVAL '10 days'
|
||||||
|
-- Titel "noncivil"
|
||||||
|
AND ch.title_of_nobility = (
|
||||||
|
SELECT id
|
||||||
|
FROM falukant_type.title
|
||||||
|
WHERE label_tr = 'noncivil'
|
||||||
|
)
|
||||||
|
ORDER BY RANDOM()
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
UPDATE
|
||||||
|
falukant_data.child_relation AS cr2
|
||||||
|
SET
|
||||||
|
is_heir = true,
|
||||||
|
updated_at = NOW()
|
||||||
|
FROM
|
||||||
|
chosen
|
||||||
|
WHERE
|
||||||
|
cr2.id = chosen.relation_id
|
||||||
|
RETURNING
|
||||||
|
chosen.child_character_id
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char* QUERY_SET_CHARACTER_USER = R"(
|
||||||
|
UPDATE falukant_data."character"
|
||||||
|
SET user_id = $1,
|
||||||
|
updated_at = NOW()
|
||||||
|
WHERE id = $2
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char* QUERY_UPDATE_USER_MONEY = R"(
|
||||||
|
UPDATE falukant_data.falukant_user
|
||||||
|
SET money = $1,
|
||||||
|
updated_at = NOW()
|
||||||
|
WHERE user_id = $2
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char* QUERY_GET_FALUKANT_USER_ID = R"(
|
||||||
|
SELECT user_id
|
||||||
|
FROM falukant_data."character"
|
||||||
|
WHERE id = $1
|
||||||
|
LIMIT 1
|
||||||
|
)";
|
||||||
|
|
||||||
|
// Sub‐Queries
|
||||||
|
static constexpr const char* QUERY_GET_CURRENT_MONEY = R"(
|
||||||
|
SELECT COALESCE(money,0) AS sum
|
||||||
|
FROM falukant_data.falukant_user
|
||||||
|
WHERE user_id = $1
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char* QUERY_HOUSE_VALUE = R"(
|
||||||
|
SELECT COALESCE(SUM(h.cost),0) AS sum
|
||||||
|
FROM falukant_data.user_house AS uh
|
||||||
|
JOIN falukant_type.house AS h ON uh.house_type_id = h.id
|
||||||
|
WHERE uh.user_id = $1
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char* QUERY_SETTLEMENT_VALUE = R"(
|
||||||
|
SELECT COALESCE(SUM(b.base_cost),0) AS sum
|
||||||
|
FROM falukant_data.branch AS br
|
||||||
|
JOIN falukant_type.branch AS b ON br.branch_type_id = b.id
|
||||||
|
WHERE br.falukant_user_id = $1
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char* QUERY_INVENTORY_VALUE = R"(
|
||||||
|
SELECT COALESCE(SUM(i.quantity * p.sell_cost),0) AS sum
|
||||||
|
FROM falukant_data.inventory AS i
|
||||||
|
JOIN falukant_type.product AS p ON i.product_id = p.id
|
||||||
|
JOIN falukant_data.branch AS br ON i.stock_id = br.id
|
||||||
|
WHERE br.falukant_user_id = $1
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char* QUERY_CREDIT_DEBT = R"(
|
||||||
|
SELECT COALESCE(SUM(remaining_amount),0) AS sum
|
||||||
|
FROM falukant_data.credit
|
||||||
|
WHERE falukant_user_id = $1
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char* QUERY_COUNT_CHILDREN = R"(
|
||||||
|
SELECT COUNT(*) AS cnt
|
||||||
|
FROM falukant_data.child_relation
|
||||||
|
WHERE father_character_id = $1
|
||||||
|
OR mother_character_id = $1
|
||||||
|
)";
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // USERCHARACTERWORKER_H
|
||||||
168
src/valuerecalculationworker.cpp
Normal file
168
src/valuerecalculationworker.cpp
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
#include "valuerecalculationworker.h"
|
||||||
|
|
||||||
|
ValueRecalculationWorker::ValueRecalculationWorker(ConnectionPool &pool, MessageBroker &broker)
|
||||||
|
: Worker(pool, broker, "ValueRecalculationWorker"),
|
||||||
|
activities{
|
||||||
|
{"productKnowledge", Activity(std::chrono::system_clock::from_time_t(0),
|
||||||
|
[this]() { calculateProductKnowledge(); },
|
||||||
|
std::chrono::hours(0))}, // 00:00 Uhr
|
||||||
|
|
||||||
|
{"regionalSellPrice", Activity(std::chrono::system_clock::from_time_t(0),
|
||||||
|
[this]() { calculateRegionalSellPrice(); },
|
||||||
|
std::chrono::hours(12) + std::chrono::minutes(0))} // 12:00 Uhr
|
||||||
|
}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueRecalculationWorker::~ValueRecalculationWorker() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueRecalculationWorker::run() {
|
||||||
|
while (runningWorker) {
|
||||||
|
setCurrentStep("Check if activity has to run");
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
for (auto &[key, activity] : activities) {
|
||||||
|
if (shouldRunToday(activity)) {
|
||||||
|
activity.lastRun = now;
|
||||||
|
activity.callMethod();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setCurrentStep("CalculateMarriages");
|
||||||
|
calculateMarriages();
|
||||||
|
calculateStudying();
|
||||||
|
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 ValueRecalculationWorker::shouldRunToday(const Activity& activity) {
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
auto todayScheduledTime = getNextScheduledTime(activity.scheduledTime);
|
||||||
|
return now >= todayScheduledTime && activity.lastRun < todayScheduledTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::chrono::system_clock::time_point ValueRecalculationWorker::getNextScheduledTime(std::chrono::system_clock::duration scheduledDuration) {
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
std::time_t now_c = std::chrono::system_clock::to_time_t(now);
|
||||||
|
std::tm now_tm = *std::localtime(&now_c);
|
||||||
|
now_tm.tm_hour = std::chrono::duration_cast<std::chrono::hours>(scheduledDuration).count();
|
||||||
|
now_tm.tm_min = std::chrono::duration_cast<std::chrono::minutes>(scheduledDuration).count() % 60;
|
||||||
|
now_tm.tm_sec = 0;
|
||||||
|
return std::chrono::system_clock::from_time_t(std::mktime(&now_tm));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueRecalculationWorker::calculateProductKnowledge() {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
db.prepare("QUERY_UPDATE_PRODUCT_KNOWLEDGE_USER", QUERY_UPDATE_PRODUCT_KNOWLEDGE_USER);
|
||||||
|
db.execute("QUERY_UPDATE_PRODUCT_KNOWLEDGE_USER");
|
||||||
|
db.prepare("QUERY_GET_PRODUCERS_LAST_DAY", QUERY_GET_PRODUCERS_LAST_DAY);
|
||||||
|
const auto &usersToInform = db.execute("QUERY_GET_PRODUCERS_LAST_DAY");
|
||||||
|
const nlohmann::json message = {
|
||||||
|
{ "event", "price_update" }
|
||||||
|
};
|
||||||
|
for (const auto &user: usersToInform) {
|
||||||
|
const auto userId = std::stoi(user.at("producer_id"));
|
||||||
|
sendMessageToFalukantUsers(userId, message);
|
||||||
|
}
|
||||||
|
db.prepare("QUERY_DELETE_OLD_PRODUCTIONS", QUERY_DELETE_OLD_PRODUCTIONS);
|
||||||
|
db.execute("QUERY_DELETE_OLD_PRODUCTIONS");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueRecalculationWorker::calculateRegionalSellPrice() {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
db.prepare("QUERY_UPDATE_REGION_SELL_PRICE", QUERY_UPDATE_REGION_SELL_PRICE);
|
||||||
|
db.execute("QUERY_UPDATE_REGION_SELL_PRICE");
|
||||||
|
db.prepare("QUERY_GET_SELL_REGIONS", QUERY_GET_SELL_REGIONS);
|
||||||
|
const auto ®ionsWithSells = db.execute("QUERY_GET_SELL_REGIONS");
|
||||||
|
const nlohmann::json message = {
|
||||||
|
{ "event", "price_update" }
|
||||||
|
};
|
||||||
|
for (const auto ®ion: regionsWithSells) {
|
||||||
|
const auto regionId = std::stoi(region.at("region_id"));
|
||||||
|
sendMessageToRegionUsers(regionId, message);
|
||||||
|
}
|
||||||
|
db.prepare("QUERY_DELETE_REGION_SELL_PRICE", QUERY_DELETE_REGION_SELL_PRICE);
|
||||||
|
db.execute("QUERY_DELETE_REGION_SELL_PRICE");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueRecalculationWorker::calculateMarriages() {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
db.prepare("QUERY_SET_MARRIAGES_BY_PARTY", QUERY_SET_MARRIAGES_BY_PARTY);
|
||||||
|
const auto &usersFromUpdatedRelationships = db.execute("QUERY_SET_MARRIAGES_BY_PARTY");
|
||||||
|
const nlohmann::json message = {
|
||||||
|
{ "event", "relationship_changed" }
|
||||||
|
};
|
||||||
|
for (const auto &userFromUpdatedRelationships: usersFromUpdatedRelationships) {
|
||||||
|
if (userFromUpdatedRelationships.at("character1_user") != "") {
|
||||||
|
const auto user1Id = std::stoi(userFromUpdatedRelationships.at("character1_user"));
|
||||||
|
sendMessageToRegionUsers(user1Id, message);
|
||||||
|
}
|
||||||
|
if (userFromUpdatedRelationships.at("character2_user") != "") {
|
||||||
|
const auto user2Id = std::stoi(userFromUpdatedRelationships.at("character2_user"));
|
||||||
|
sendMessageToRegionUsers(user2Id, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueRecalculationWorker::calculateStudying() {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
db.prepare("QUERY_GET_STUDYINGS_TO_EXECUTE", QUERY_GET_STUDYINGS_TO_EXECUTE);
|
||||||
|
db.prepare("QUERY_SET_LEARNING_DONE", QUERY_SET_LEARNING_DONE);
|
||||||
|
const auto studies = db.execute("QUERY_GET_STUDYINGS_TO_EXECUTE");
|
||||||
|
for (const auto &study: studies) {
|
||||||
|
if (study.at("tr") == "self") {
|
||||||
|
calculateStudyingSelf(study);
|
||||||
|
} else if (study.at("tr") == "children" || study.at("tr") == "director") {
|
||||||
|
caclulateStudyingForAssociatedCharacter(study);
|
||||||
|
}
|
||||||
|
db.execute("QUERY_SET_LEARNING_DONE", {study.at("id")});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueRecalculationWorker::calculateStudyingSelf(Database::FieldMap entry) {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
db.prepare("QUERY_GET_OWN_CHARACTER_ID", QUERY_GET_OWN_CHARACTER_ID);
|
||||||
|
const auto ownCharacterIdResult = db.execute("QUERY_GET_OWN_CHARACTER_ID", { entry.at("associated_falukant_user_id") });
|
||||||
|
if (ownCharacterIdResult.size() > 0) {
|
||||||
|
auto characterId = std::stoi(ownCharacterIdResult.at(0).at("id"));
|
||||||
|
auto learnAll = entry.at("learn_all_products") == "t" || entry.at("product_id") == "";
|
||||||
|
int productId = learnAll ? 0 : std::stoi(entry.at("product_id"));
|
||||||
|
calculateStudyingCharacter(characterId, learnAll, productId, std::stoi(entry.at("learning_recipient_id")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueRecalculationWorker::caclulateStudyingForAssociatedCharacter(Database::FieldMap entry) {
|
||||||
|
auto characterId = std::stoi(entry.at("associated_learning_character_id"));
|
||||||
|
auto learnAll = entry.at("learn_all_products") == "t" || entry.at("product_id") == "";
|
||||||
|
int productId = learnAll ? 0 : std::stoi(entry.at("product_id"));
|
||||||
|
calculateStudyingCharacter(characterId, learnAll, productId, std::stoi(entry.at("learning_recipient_id")));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueRecalculationWorker::calculateStudyingCharacter(int characterId, bool all, int productId, int falukantUserId) {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
if (all) {
|
||||||
|
db.prepare("QUERY_INCREASE_ALL_PRODUCTS_KNOWLEDGE", QUERY_INCREASE_ALL_PRODUCTS_KNOWLEDGE);
|
||||||
|
db.execute("QUERY_INCREASE_ALL_PRODUCTS_KNOWLEDGE", { "1", std::to_string(characterId) });
|
||||||
|
} else {
|
||||||
|
db.prepare("QUERY_INCREASE_ONE_PRODUCT_KNOWLEDGE", QUERY_INCREASE_ONE_PRODUCT_KNOWLEDGE);
|
||||||
|
db.execute("QUERY_INCREASE_ONE_PRODUCT_KNOWLEDGE", { "5", std::to_string(characterId), std::to_string(productId) });
|
||||||
|
}
|
||||||
|
const nlohmann::json message = {
|
||||||
|
{ "event", "knowledge_updated" }
|
||||||
|
};
|
||||||
|
sendMessageToFalukantUsers(falukantUserId, message);
|
||||||
|
}
|
||||||
|
|
||||||
175
src/valuerecalculationworker.h
Normal file
175
src/valuerecalculationworker.h
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
#ifndef VALUERECALCULATIONWORKER_H
|
||||||
|
#define VALUERECALCULATIONWORKER_H
|
||||||
|
|
||||||
|
#include "worker.h"
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <functional>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
class ValueRecalculationWorker : public Worker {
|
||||||
|
public:
|
||||||
|
ValueRecalculationWorker(ConnectionPool &pool, MessageBroker &broker);
|
||||||
|
~ValueRecalculationWorker() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Activity {
|
||||||
|
std::chrono::system_clock::time_point lastRun;
|
||||||
|
std::function<void()> callMethod;
|
||||||
|
std::chrono::system_clock::duration scheduledTime;
|
||||||
|
|
||||||
|
Activity(std::chrono::system_clock::time_point lr, std::function<void()> cm, std::chrono::system_clock::duration st)
|
||||||
|
: lastRun(lr), callMethod(std::move(cm)), scheduledTime(st) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<std::string, Activity> activities;
|
||||||
|
|
||||||
|
void calculateProductKnowledge();
|
||||||
|
void calculateRegionalSellPrice();
|
||||||
|
void calculateMarriages();
|
||||||
|
void calculateStudying();
|
||||||
|
void calculateStudyingSelf(Database::FieldMap entry);
|
||||||
|
void caclulateStudyingForAssociatedCharacter(Database::FieldMap entry);
|
||||||
|
void calculateStudyingCharacter(int characterId, bool all, int productId, int falukantUserId);
|
||||||
|
|
||||||
|
bool shouldRunToday(const Activity& activity);
|
||||||
|
std::chrono::system_clock::time_point getNextScheduledTime(std::chrono::system_clock::duration scheduledDuration);
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_UPDATE_PRODUCT_KNOWLEDGE_USER = R"(
|
||||||
|
UPDATE falukant_data.knowledge k
|
||||||
|
SET knowledge = LEAST(100, k.knowledge + 1)
|
||||||
|
FROM falukant_data."character" c
|
||||||
|
JOIN falukant_log.production p
|
||||||
|
ON DATE(p.production_timestamp) = CURRENT_DATE - INTERVAL '1 day'
|
||||||
|
WHERE c.id = k.character_id
|
||||||
|
AND c.user_id = 18
|
||||||
|
AND k.product_id = 10
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_DELETE_OLD_PRODUCTIONS = R"(
|
||||||
|
delete from falukant_log.production flp
|
||||||
|
where date(flp.production_timestamp) < CURRENT_DATE
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_GET_PRODUCERS_LAST_DAY = R"(
|
||||||
|
select p."producer_id"
|
||||||
|
from falukant_log.production p
|
||||||
|
where date(p."production_timestamp") = CURRENT_DATE - interval '1 day'
|
||||||
|
group by producer_id
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_UPDATE_REGION_SELL_PRICE = R"(
|
||||||
|
UPDATE falukant_data.town_product_worth tpw
|
||||||
|
SET worth_percent =
|
||||||
|
GREATEST(
|
||||||
|
0,
|
||||||
|
LEAST(
|
||||||
|
CASE
|
||||||
|
WHEN s.quantity > avg_sells THEN tpw.worth_percent - 1
|
||||||
|
WHEN s.quantity < avg_sells THEN tpw.worth_percent + 1
|
||||||
|
ELSE tpw.worth_percent
|
||||||
|
END,
|
||||||
|
100
|
||||||
|
)
|
||||||
|
)
|
||||||
|
FROM (
|
||||||
|
SELECT region_id, product_id, quantity,
|
||||||
|
(SELECT AVG(quantity)
|
||||||
|
FROM falukant_log.sell avs
|
||||||
|
WHERE avs.product_id = s.product_id) AS avg_sells
|
||||||
|
FROM falukant_log.sell s
|
||||||
|
WHERE DATE(s.sell_timestamp) = CURRENT_DATE - INTERVAL '1 day'
|
||||||
|
) s
|
||||||
|
WHERE tpw.region_id = s.region_id
|
||||||
|
AND tpw.product_id = s.product_id
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_DELETE_REGION_SELL_PRICE = R"(
|
||||||
|
delete from falukant_log.sell s
|
||||||
|
where date(s.sell_timestamp) < CURRENT_DATE
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_GET_SELL_REGIONS = R"(
|
||||||
|
select s."region_id"
|
||||||
|
from falukant_log.sell s
|
||||||
|
where date(s."sell_timestamp") = CURRENT_DATE - interval '1 day'
|
||||||
|
group by "region_id"
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char * QUERY_SET_MARRIAGES_BY_PARTY = R"(
|
||||||
|
WITH updated_relations AS (
|
||||||
|
UPDATE falukant_data.relationship AS rel
|
||||||
|
SET relationship_type_id = (
|
||||||
|
SELECT id
|
||||||
|
FROM falukant_type.relationship AS rt
|
||||||
|
WHERE rt.tr = 'married'
|
||||||
|
)
|
||||||
|
WHERE rel.id IN (
|
||||||
|
SELECT rel2.id
|
||||||
|
FROM falukant_data.party AS p
|
||||||
|
JOIN falukant_type.party AS pt
|
||||||
|
ON pt.id = p.party_type_id
|
||||||
|
AND pt.tr = 'wedding'
|
||||||
|
JOIN falukant_data.falukant_user AS fu
|
||||||
|
ON fu.id = p.falukant_user_id
|
||||||
|
JOIN falukant_data."character" AS c
|
||||||
|
ON c.user_id = fu.id
|
||||||
|
JOIN falukant_data.relationship AS rel2
|
||||||
|
ON rel2.character1_id = c.id
|
||||||
|
OR rel2.character2_id = c.id
|
||||||
|
JOIN falukant_type.relationship AS rt2
|
||||||
|
ON rt2.id = rel2.relationship_type_id
|
||||||
|
AND rt2.tr = 'engaged'
|
||||||
|
WHERE p.created_at <= NOW() - INTERVAL '1 day'
|
||||||
|
)
|
||||||
|
RETURNING character1_id, character2_id
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
c1.user_id AS character1_user,
|
||||||
|
c2.user_id AS character2_user
|
||||||
|
FROM updated_relations AS ur
|
||||||
|
JOIN falukant_data."character" AS c1
|
||||||
|
ON c1.id = ur.character1_id
|
||||||
|
JOIN falukant_data."character" AS c2
|
||||||
|
ON c2.id = ur.character2_id;
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char * QUERY_GET_STUDYINGS_TO_EXECUTE = R"(
|
||||||
|
select l.id, l.associated_falukant_user_id, l.associated_learning_character_id, l.learn_all_products, l.learning_recipient_id, l.product_id,
|
||||||
|
lr.tr
|
||||||
|
from falukant_data.learning l
|
||||||
|
join falukant_type.learn_recipient lr
|
||||||
|
on lr.id = l.learning_recipient_id
|
||||||
|
where l.learning_is_executed = false
|
||||||
|
and l.created_at + interval '1 day' < now();
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char * QUERY_GET_OWN_CHARACTER_ID = R"(
|
||||||
|
select id
|
||||||
|
from falukant_data."character" c
|
||||||
|
where c.user_id = $1
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_INCREASE_ONE_PRODUCT_KNOWLEDGE = R"(
|
||||||
|
update falukant_data.knowledge k
|
||||||
|
set knowledge = LEAST(100, k.knowledge + $1)
|
||||||
|
where k.character_id = $2
|
||||||
|
and k.product_id = $3
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_INCREASE_ALL_PRODUCTS_KNOWLEDGE = R"(
|
||||||
|
update falukant_data.knowledge k
|
||||||
|
set knowledge = LEAST(100, k.knowledge + $1)
|
||||||
|
where k.character_id = $2
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_SET_LEARNING_DONE = R"(
|
||||||
|
update falukant_data.learning
|
||||||
|
set learning_is_executed = true
|
||||||
|
where id = $1
|
||||||
|
)";
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VALUERECALCULATIONWORKER_H
|
||||||
@@ -1,149 +1,154 @@
|
|||||||
#include "websocket_server.h"
|
#include "websocket_server.h"
|
||||||
#include "connection_guard.h"
|
#include "connection_guard.h"
|
||||||
|
#include "worker.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
#include <thread>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <queue>
|
#include <cstring>
|
||||||
#include <condition_variable>
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
// Protocols array definition
|
||||||
|
struct lws_protocols WebSocketServer::protocols[] = {
|
||||||
|
{
|
||||||
|
"yourpart-protocol",
|
||||||
|
WebSocketServer::wsCallback,
|
||||||
|
sizeof(WebSocketUserData),
|
||||||
|
4096
|
||||||
|
},
|
||||||
|
{ nullptr, nullptr, 0, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
WebSocketServer::WebSocketServer(int port, ConnectionPool &pool, MessageBroker &broker)
|
WebSocketServer::WebSocketServer(int port, ConnectionPool &pool, MessageBroker &broker)
|
||||||
: port(port), pool(pool), broker(broker) {}
|
: port(port), pool(pool), broker(broker) {}
|
||||||
|
|
||||||
|
WebSocketServer::~WebSocketServer() {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
void WebSocketServer::run() {
|
void WebSocketServer::run() {
|
||||||
running = true;
|
running = true;
|
||||||
broker.subscribe([this](const std::string &message) {
|
broker.subscribe([this](const std::string &msg) {
|
||||||
std::lock_guard<std::mutex> lock(queueMutex);
|
{
|
||||||
messageQueue.push(message);
|
std::lock_guard<std::mutex> lock(queueMutex);
|
||||||
|
messageQueue.push(msg);
|
||||||
|
}
|
||||||
queueCV.notify_one();
|
queueCV.notify_one();
|
||||||
});
|
});
|
||||||
serverThread = std::thread([this]() { startServer(); });
|
serverThread = std::thread([this](){ startServer(); });
|
||||||
messageProcessingThread = std::thread([this]() { processMessageQueue(); });
|
messageThread = std::thread([this](){ processMessageQueue(); });
|
||||||
pingThread = std::thread([this]() { pingClients(); });
|
pingThread = std::thread([this](){ pingClients(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketServer::stop() {
|
void WebSocketServer::stop() {
|
||||||
running = false;
|
running = false;
|
||||||
|
if (context) lws_cancel_service(context);
|
||||||
if (serverThread.joinable()) serverThread.join();
|
if (serverThread.joinable()) serverThread.join();
|
||||||
if (messageProcessingThread.joinable()) messageProcessingThread.join();
|
if (messageThread.joinable()) messageThread.join();
|
||||||
if (pingThread.joinable()) pingThread.join();
|
if (pingThread.joinable()) pingThread.join();
|
||||||
|
if (context) {
|
||||||
|
lws_context_destroy(context);
|
||||||
|
context = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketServer::startServer() {
|
void WebSocketServer::startServer() {
|
||||||
uWS::App()
|
struct lws_context_creation_info info;
|
||||||
.ws<WebSocketUserData>("/*", {
|
memset(&info, 0, sizeof(info));
|
||||||
.open = [this](uWS::WebSocket<false, true, WebSocketUserData> *ws) {
|
info.port = port;
|
||||||
ws->getUserData()->pongReceived = true;
|
info.protocols = protocols;
|
||||||
},
|
context = lws_create_context(&info);
|
||||||
.message = [this](uWS::WebSocket<false, true, WebSocketUserData> *ws, std::string_view message, uWS::OpCode opCode) {
|
if (!context) {
|
||||||
handleWebSocketMessage(ws, message, opCode);
|
throw std::runtime_error("Failed to create LWS context");
|
||||||
},
|
}
|
||||||
.close = [this](uWS::WebSocket<false, true, WebSocketUserData> *ws, int /*code*/, std::string_view /*message*/) {
|
|
||||||
handleWebSocketClose(ws);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.listen(port, [this](auto *token) {
|
|
||||||
if (token) {
|
|
||||||
std::cout << "WebSocket-Server läuft auf Port " << port << std::endl;
|
|
||||||
} else {
|
|
||||||
std::cerr << "WebSocket-Server konnte nicht gestartet werden!\n";
|
|
||||||
running = false;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketServer::pingClients() {
|
|
||||||
while (running) {
|
while (running) {
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(30));
|
lws_service(context, 50);
|
||||||
|
|
||||||
std::unique_lock lock(connectionsMutex);
|
|
||||||
for (auto &[userId, ws] : connections) {
|
|
||||||
if (!ws->getUserData()->pongReceived) {
|
|
||||||
ws->close();
|
|
||||||
} else {
|
|
||||||
ws->getUserData()->pongReceived = false;
|
|
||||||
ws->send("ping", uWS::OpCode::TEXT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketServer::processMessageQueue() {
|
void WebSocketServer::processMessageQueue() {
|
||||||
while (running) {
|
while (running) {
|
||||||
std::unique_lock lock(queueMutex);
|
std::unique_lock<std::mutex> lock(queueMutex);
|
||||||
queueCV.wait(lock, [this]() { return !messageQueue.empty() || !running; });
|
queueCV.wait(lock, [this](){ return !messageQueue.empty() || !running; });
|
||||||
while (!messageQueue.empty()) {
|
while (!messageQueue.empty()) {
|
||||||
std::string message = std::move(messageQueue.front());
|
std::string msg = std::move(messageQueue.front());
|
||||||
messageQueue.pop();
|
messageQueue.pop();
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
handleBrokerMessage(message);
|
handleBrokerMessage(msg);
|
||||||
lock.lock();
|
lock.lock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebSocketServer::pingClients() {
|
||||||
|
while (running) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(30));
|
||||||
|
lws_callback_on_writable_all_protocol(context, &protocols[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebSocketServer::wsCallback(struct lws *wsi,
|
||||||
|
enum lws_callback_reasons reason,
|
||||||
|
void *user, void *in, size_t len) {
|
||||||
|
auto *ud = reinterpret_cast<WebSocketUserData*>(user);
|
||||||
|
switch (reason) {
|
||||||
|
case LWS_CALLBACK_ESTABLISHED:
|
||||||
|
ud->pongReceived = true;
|
||||||
|
break;
|
||||||
|
case LWS_CALLBACK_RECEIVE: {
|
||||||
|
std::string msg(reinterpret_cast<char*>(in), len);
|
||||||
|
// Here you would dispatch the received message to handleBrokerMessage or handleWebSocketMessage
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CALLBACK_SERVER_WRITEABLE: {
|
||||||
|
unsigned char buf[LWS_PRE + 4];
|
||||||
|
memcpy(buf + LWS_PRE, "ping", 4);
|
||||||
|
lws_write(wsi, buf + LWS_PRE, 4, LWS_WRITE_TEXT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWS_CALLBACK_CLOSED:
|
||||||
|
// Remove closed connection if stored
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void WebSocketServer::handleBrokerMessage(const std::string &message) {
|
void WebSocketServer::handleBrokerMessage(const std::string &message) {
|
||||||
try {
|
try {
|
||||||
json parsedMessage = json::parse(message);
|
json parsed = json::parse(message);
|
||||||
if (parsedMessage.contains("user_id")) {
|
if (parsed.contains("user_id")) {
|
||||||
int falukantUserId = parsedMessage["user_id"];
|
int fid = parsed["user_id"].get<int>();
|
||||||
std::shared_lock lock(connectionsMutex);
|
auto userId = getUserIdFromFalukantUserId(fid);
|
||||||
auto userId = getUserIdFromFalukantUserId(falukantUserId);
|
std::shared_lock<std::shared_mutex> lock(connectionsMutex);
|
||||||
auto it = connections.find(userId);
|
auto it = connections.find(userId);
|
||||||
if (it != connections.end()) {
|
if (it != connections.end()) {
|
||||||
it->second->send(message, uWS::OpCode::TEXT);
|
lws_callback_on_writable(it->second);
|
||||||
std::cout << "[WebSocketServer] Nachricht an User-ID: " << userId << " gesendet.\n";
|
|
||||||
} else {
|
|
||||||
std::cerr << "[WebSocketServer] Keine Verbindung für User-ID: " << userId << "\n";
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
std::cerr << "[WebSocketServer] Ungültige Nachricht: " << message << "\n";
|
|
||||||
}
|
}
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
std::cerr << "[WebSocketServer] Fehler beim Verarbeiten der Nachricht: " << e.what() << "\n";
|
std::cerr << "Error processing broker message: " << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketServer::handleWebSocketMessage(uWS::WebSocket<false, true, WebSocketUserData> *ws, std::string_view message, uWS::OpCode opCode) {
|
std::string WebSocketServer::getUserIdFromFalukantUserId(int userId) {
|
||||||
if (message == "pong") {
|
|
||||||
ws->getUserData()->pongReceived = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
json parsedMessage = json::parse(message);
|
|
||||||
if (parsedMessage.contains("event") && parsedMessage["event"] == "setUserId") {
|
|
||||||
std::string userId = parsedMessage["data"]["userId"];
|
|
||||||
std::unique_lock lock(connectionsMutex);
|
|
||||||
connections[userId] = ws;
|
|
||||||
ws->getUserData()->userId = userId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketServer::handleWebSocketClose(uWS::WebSocket<false, true, WebSocketUserData> *ws) {
|
|
||||||
std::unique_lock lock(connectionsMutex);
|
|
||||||
auto userId = ws->getUserData()->userId;
|
|
||||||
if (!userId.empty()) {
|
|
||||||
connections.erase(userId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string WebSocketServer::getUserIdFromFalukantUserId(int & userId) {
|
|
||||||
ConnectionGuard guard(pool);
|
ConnectionGuard guard(pool);
|
||||||
auto &db = guard.get();
|
auto &db = guard.get();
|
||||||
std::string query = R"(
|
std::string sql = R"(
|
||||||
SELECT u.hashed_id
|
SELECT u.hashed_id
|
||||||
FROM community.user u
|
FROM community.user u
|
||||||
JOIN falukant_data.falukant_user fu ON u.id = fu.user_id
|
JOIN falukant_data.falukant_user fu ON u.id = fu.user_id
|
||||||
WHERE fu.id = $1
|
WHERE fu.id = $1
|
||||||
)";
|
)";
|
||||||
db.prepare("get_user_id", query);
|
db.prepare("get_user_id", sql);
|
||||||
auto users = db.execute("get_user_id", {std::to_string(userId)});
|
auto res = db.execute("get_user_id", {std::to_string(userId)});
|
||||||
if (!users.empty()) {
|
return (!res.empty()) ? res[0]["hashed_id"] : std::string();
|
||||||
return users[0]["hashed_id"];
|
}
|
||||||
} else {
|
|
||||||
return "";
|
void WebSocketServer::setWorkers(const std::vector<std::unique_ptr<Worker>> &workerList) {
|
||||||
|
workers.clear();
|
||||||
|
workers.reserve(workerList.size());
|
||||||
|
for (const auto &wptr : workerList) {
|
||||||
|
workers.push_back(wptr.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +1,66 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "message_broker.h"
|
#include <libwebsockets.h>
|
||||||
|
#include "connection_guard.h"
|
||||||
#include "connection_pool.h"
|
#include "connection_pool.h"
|
||||||
#include <uWebSockets/App.h>
|
#include "message_broker.h"
|
||||||
#include <unordered_map>
|
#include <nlohmann/json.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
struct WebSocketUserData {
|
struct WebSocketUserData {
|
||||||
std::string userId;
|
std::string userId;
|
||||||
bool pongReceived = true;
|
bool pongReceived = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Worker; // forward
|
||||||
|
|
||||||
class WebSocketServer {
|
class WebSocketServer {
|
||||||
public:
|
public:
|
||||||
WebSocketServer(int port, ConnectionPool &pool, MessageBroker &broker);
|
WebSocketServer(int port, ConnectionPool &pool, MessageBroker &broker);
|
||||||
|
~WebSocketServer();
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
void stop();
|
void stop();
|
||||||
|
void setWorkers(const std::vector<std::unique_ptr<Worker>> &workerList);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void startServer();
|
void startServer();
|
||||||
void processMessageQueue();
|
void processMessageQueue();
|
||||||
void pingClients();
|
void pingClients();
|
||||||
void handleBrokerMessage(const std::string &message);
|
void handleBrokerMessage(const std::string &message);
|
||||||
void handleWebSocketMessage(uWS::WebSocket<false, true, WebSocketUserData> *ws, std::string_view message, uWS::OpCode opCode);
|
std::string getUserIdFromFalukantUserId(int falukantUserId);
|
||||||
void handleWebSocketClose(uWS::WebSocket<false, true, WebSocketUserData> *ws);
|
|
||||||
std::string getUserIdFromFalukantUserId(int &userId);
|
static int wsCallback(struct lws *wsi,
|
||||||
|
enum lws_callback_reasons reason,
|
||||||
|
void *user, void *in, size_t len);
|
||||||
|
|
||||||
int port;
|
int port;
|
||||||
ConnectionPool &pool;
|
ConnectionPool &pool;
|
||||||
MessageBroker &broker;
|
MessageBroker &broker;
|
||||||
|
|
||||||
std::atomic<bool> running{false};
|
std::atomic<bool> running{false};
|
||||||
|
struct lws_context *context = nullptr;
|
||||||
std::thread serverThread;
|
std::thread serverThread;
|
||||||
std::thread messageProcessingThread;
|
std::thread messageThread;
|
||||||
std::thread pingThread;
|
std::thread pingThread;
|
||||||
|
|
||||||
std::unordered_map<std::string, uWS::WebSocket<false, true, WebSocketUserData> *> connections;
|
|
||||||
std::shared_mutex connectionsMutex;
|
|
||||||
|
|
||||||
std::queue<std::string> messageQueue;
|
|
||||||
std::mutex queueMutex;
|
std::mutex queueMutex;
|
||||||
std::condition_variable queueCV;
|
std::condition_variable queueCV;
|
||||||
|
std::queue<std::string> messageQueue;
|
||||||
|
|
||||||
|
std::shared_mutex connectionsMutex;
|
||||||
|
std::unordered_map<std::string, struct lws*> connections;
|
||||||
|
|
||||||
|
std::vector<Worker*> workers;
|
||||||
|
|
||||||
|
static struct lws_protocols protocols[];
|
||||||
};
|
};
|
||||||
|
|||||||
62
src/worker.h
62
src/worker.h
@@ -3,14 +3,14 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <condition_variable>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <functional>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
#include "connection_pool.h"
|
#include "connection_pool.h"
|
||||||
#include "message_broker.h"
|
#include "message_broker.h"
|
||||||
#include "database.h"
|
#include "database.h"
|
||||||
|
#include "connection_guard.h"
|
||||||
|
|
||||||
class Worker {
|
class Worker {
|
||||||
public:
|
public:
|
||||||
@@ -64,6 +64,11 @@ public:
|
|||||||
return currentStep;
|
return currentStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string getStatus() {
|
||||||
|
std::lock_guard<std::mutex> lock(stepMutex);
|
||||||
|
return "{\"worker\":\"" + workerName + "\", \"currentStep\":\"" + currentStep + "\"}";
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void run() = 0;
|
virtual void run() = 0;
|
||||||
|
|
||||||
@@ -101,6 +106,42 @@ protected:
|
|||||||
currentStep = step;
|
currentStep = step;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sendMessageToRegionUsers(const int ®ionId, nlohmann::json message) {
|
||||||
|
ConnectionGuard guard(pool);
|
||||||
|
auto &db = guard.get();
|
||||||
|
db.prepare("QUERY_GET_REGION_USERS", QUERY_GET_REGION_USERS);
|
||||||
|
auto users = db.execute("QUERY_GET_REGION_USERS", {std::to_string(regionId)});
|
||||||
|
for (const auto &user: users) {
|
||||||
|
message["user_id"] = user.at("user_id");
|
||||||
|
broker.publish(message.dump());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendMessageToFalukantUsers(const int &falukantUserId, nlohmann::json message) {
|
||||||
|
message["user_id"] = falukantUserId;
|
||||||
|
broker.publish(message.dump());
|
||||||
|
}
|
||||||
|
|
||||||
|
void changeFalukantUserMoney(int falukantUserId, double moneyChange, std::string action, nlohmann::json message) {
|
||||||
|
try {
|
||||||
|
ConnectionGuard connGuard(pool);
|
||||||
|
auto &db = connGuard.get();
|
||||||
|
db.prepare("QUERY_UPDATE_MONEY", QUERY_UPDATE_MONEY);
|
||||||
|
db.execute("QUERY_UPDATE_MONEY", {
|
||||||
|
std::to_string(falukantUserId),
|
||||||
|
std::to_string(moneyChange),
|
||||||
|
action
|
||||||
|
});
|
||||||
|
sendMessageToFalukantUsers(falukantUserId, message);
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
std::cerr << "[" << workerName << "] Fehler in changeFalukantUserMoney: " << e.what() << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t getLastActivity() {
|
||||||
|
return lastActivity;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ConnectionPool &pool;
|
ConnectionPool &pool;
|
||||||
MessageBroker &broker;
|
MessageBroker &broker;
|
||||||
@@ -114,4 +155,21 @@ protected:
|
|||||||
std::chrono::seconds watchdogInterval{10};
|
std::chrono::seconds watchdogInterval{10};
|
||||||
std::mutex stepMutex;
|
std::mutex stepMutex;
|
||||||
std::string currentStep;
|
std::string currentStep;
|
||||||
|
time_t lastActivity;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr const char *QUERY_GET_REGION_USERS = R"(
|
||||||
|
select c.user_id
|
||||||
|
from falukant_data."character" c
|
||||||
|
where c.region_id = $1
|
||||||
|
and c.user_id is not null;
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr const char *QUERY_UPDATE_MONEY = R"(
|
||||||
|
SELECT falukant_data.update_money(
|
||||||
|
$1,
|
||||||
|
$2,
|
||||||
|
$3
|
||||||
|
);
|
||||||
|
)";
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user