Skip to content

Commit 5e6cb2c

Browse files
committed
Database interaction is done
1 parent 5643aa7 commit 5e6cb2c

16 files changed

+461
-51
lines changed

sprint4/problems/leave_game/solution/CMakeLists.txt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@ set(CXX_STANDARD_REQUIRED ON)
77
include(${CMAKE_BINARY_DIR}/conanbuildinfo_multi.cmake)
88
conan_basic_setup(TARGETS)
99

10+
# Указываем путь к заголовкам
11+
# include_directories(/usr/include/pqxx)
12+
13+
# # Ищем библиотеку
14+
# find_library(PQXX_LIB pqxx REQUIRED HINTS /usr/lib/aarch64-linux-gnu)
15+
16+
find_package(PkgConfig REQUIRED)
17+
pkg_check_modules(PQXX REQUIRED libpqxx)
18+
19+
# Добавляем найденные пути к заголовкам
20+
include_directories(${PQXX_INCLUDE_DIRS})
21+
link_directories(${PQXX_LIBRARY_DIRS})
22+
23+
# Добавляем библиотеку к линковке
1024
# get_property(importTargets DIRECTORY "${CMAKE_SOURCE_DIR}" PROPERTY IMPORTED_TARGETS)
1125
# message(STATUS "${importTargets}")
1226

@@ -52,6 +66,12 @@ add_executable(game_server
5266
src/collision_detector.cpp
5367
src/model_serialization.h
5468
src/model_serialization.cpp
69+
src/database_manager.h
70+
src/database_manager.cpp
5571
)
5672

57-
target_link_libraries(game_server CONAN_PKG::boost)
73+
target_link_libraries(game_server
74+
CONAN_PKG::boost
75+
# ${PQXX_LIB}
76+
${PQXX_LIBRARIES}
77+
)

sprint4/problems/leave_game/solution/server_state.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

sprint4/problems/leave_game/solution/src/application.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
#include "application.h"
2+
#include "database_manager.h"
23
#include "handlers.h"
34
#include "json_loader.h"
45
#include "model.h"
56
#include "model_serialization.h"
67
#include "player.h"
78
#include "type_declarations.h"
9+
#include "log.h"
10+
11+
#include <boost/log/trivial.hpp>
12+
#include <boost/exception/all.hpp>
13+
814
#include <boost/json/object.hpp>
915
#include <iomanip>
16+
#include <exception>
17+
#include <unordered_set>
1018

1119
namespace app {
1220
char to_uppercase(unsigned char c) {
@@ -195,8 +203,9 @@ namespace app {
195203
}
196204
}
197205

198-
Application::Application(model::Game& game)
199-
: game_(game) {
206+
Application::Application(model::Game& game, db::ConnectionPool& pool)
207+
: game_(game)
208+
, connection_pool_(pool) {
200209
}
201210

202211
const std::vector<std::string>
@@ -225,6 +234,8 @@ Application::Application(model::Game& game)
225234
players_.GetPlayerByToken(token);
226235
if (!player) return boost::json::serialize(players_json);
227236

237+
// for (const auto& )
238+
228239
for (const auto& [dog_name, dog_id] : players_.GetPlayerNamesToId()) {
229240
players_json[std::to_string(dog_id)] = boost::json::object{
230241
{"name", dog_name}
@@ -277,7 +288,8 @@ Application::Application(model::Game& game)
277288
}
278289

279290
void Application::Tick(milliseconds delta_time) const {
280-
game_.GetEngine().Tick(delta_time);
291+
std::unordered_set<std::shared_ptr<model::Dog>>
292+
afk_players = game_.GetEngine().Tick(delta_time);
281293

282294
if (listener_) {
283295
listener_->OnTick(delta_time);
@@ -324,9 +336,9 @@ namespace serialization {
324336
}
325337

326338
std::filesystem::rename(temp_file, state_file_);
327-
std::cout << "Game state saved to " << state_file_ << std::endl;
339+
std::cout << "Game state saved to file: " << state_file_ << std::endl;
328340
} catch (const std::exception& e) {
329-
std::cerr << "Error saving game state: " << e.what() << std::endl;
341+
throw std::runtime_error(e.what());
330342
}
331343
}
332344

@@ -354,7 +366,7 @@ namespace serialization {
354366

355367
LoadGameDataFromFile(std::move(g_s));
356368

357-
std::cout << "Game state restored from " << state_file_ << std::endl;
369+
std::cout << "Game state restored from file: "s << state_file_ << std::endl;
358370
} catch (const std::exception& e) {
359371
std::cerr << "Error loading game state: " << e.what() << std::endl;
360372
exit(EXIT_FAILURE);

sprint4/problems/leave_game/solution/src/application.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "infrastructure.h"
77
#include "player.h"
88
#include "model.h"
9+
#include "database_manager.h"
910

1011
#include <chrono>
1112
#include <cstdint>
@@ -142,13 +143,18 @@ namespace app {
142143
model::Dog::Id id;
143144
};
144145

145-
explicit Application(model::Game& game);
146+
explicit Application(model::Game& game,
147+
db::ConnectionPool& pool);
146148

147149
void SetApplicationListener(ApplicationListener& listener);
148150

149151
const std::string GetSerializedPlayersList(const Token& token) const;
150152
const std::string GetSerializedGameState(const Token& token) const;
151153

154+
std::string GetGameRecords() const {
155+
return game_.GetGameRecords();
156+
}
157+
152158
model::MapService& GetGameMapService() {
153159
return game_.GetMapService();
154160
}
@@ -206,6 +212,8 @@ namespace app {
206212

207213
Token FindTokenByPlayer(std::shared_ptr<player::Player> player);
208214

215+
db::ConnectionPool& connection_pool_;
216+
209217
model::Game& game_;
210218
Players players_;
211219
ApplicationListener* listener_ = nullptr;
@@ -322,7 +330,6 @@ namespace app_serialization {
322330

323331
template <typename Archive>
324332
void serialize(Archive& ar, [[maybe_unused]] const unsigned version) {
325-
std::cout << "Serializing players, count: " << players_.size() << std::endl;
326333
ar & players_;
327334
ar & dog_ids_;
328335
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#include "database_manager.h"
2+
3+
namespace db {
4+
db::ConnectionPool CreateConnectionPool(unsigned num_threads, const std::string& db_url) {
5+
return db::ConnectionPool{
6+
num_threads, [db_url] {
7+
return std::make_shared<pqxx::connection>(db_url);
8+
}
9+
};
10+
}
11+
12+
void InitDatabase(db::ConnectionPool& conn_pool) {
13+
auto connection = conn_pool.GetConnection();
14+
15+
pqxx::work work{ *connection };
16+
17+
work.exec(R"(
18+
CREATE TABLE IF NOT EXISTS retired_players (
19+
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
20+
name VARCHAR(100) NOT NULL,
21+
score INTEGER,
22+
play_time_ms INTEGER
23+
);
24+
)");
25+
26+
work.exec(R"(
27+
CREATE INDEX IF NOT EXISTS retired_players_idx
28+
ON retired_players USING btree (score DESC, play_time_ms, name);
29+
)");
30+
31+
work.commit();
32+
}
33+
34+
void RegisterPrepQueries(ConnectionPool::ConnectionWrapper conn_wrap) {
35+
conn_wrap->prepare(
36+
"insert_retire",
37+
"INSERT INTO retired_players (name, score, play_time_ms) VALUES ($1, $2, $3)"
38+
);
39+
}
40+
41+
template <typename ConnectionFactory>
42+
ConnectionPool::ConnectionPool(size_t capacity,
43+
ConnectionFactory&& connection_factory) {
44+
pool_.reserve(capacity);
45+
for (size_t i = 0; i < capacity; ++i) {
46+
pool_.emplace_back(connection_factory());
47+
RegisterPrepQueries(GetConnection());
48+
}
49+
}
50+
51+
ConnectionPool::ConnectionWrapper ConnectionPool::GetConnection() {
52+
std::unique_lock lock{mutex_};
53+
// Блокируем текущий поток и ждём, пока cond_var_ не получит
54+
// уведомление и не освободится хотя бы одно соединение
55+
cond_var_.wait(lock, [this] {
56+
return used_connections_ < pool_.size();
57+
});
58+
// После выхода из цикла ожидания мьютекс остаётся захваченным
59+
60+
return {std::move(pool_[used_connections_++]), *this};
61+
}
62+
63+
void ConnectionPool::ReturnConnection(ConnectionPtr&& conn) {
64+
// Возвращаем соединение обратно в пул
65+
{
66+
std::lock_guard lock{mutex_};
67+
assert(used_connections_ != 0);
68+
pool_[--used_connections_] = std::move(conn);
69+
}
70+
// Уведомляем один из ожидающих потоков об изменении состояния пула
71+
cond_var_.notify_one();
72+
}
73+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#pragma once
2+
#include <memory>
3+
4+
#include <cassert>
5+
#include <condition_variable>
6+
#include <mutex>
7+
8+
#include <pqxx/pqxx>
9+
10+
namespace db {
11+
// using pqxx::operator"" _zv;
12+
13+
class ConnectionPool;
14+
class ConnectionWrapper;
15+
16+
ConnectionPool CreateConnectionPool(unsigned num_threads, const std::string& db_url);
17+
18+
void InitDatabase(db::ConnectionPool& conn_pool);
19+
20+
void RegisterPrepQueries(ConnectionWrapper connection);
21+
22+
class ConnectionPool {
23+
using PoolType = ConnectionPool;
24+
using ConnectionPtr = std::shared_ptr<pqxx::connection>;
25+
26+
public:
27+
class ConnectionWrapper {
28+
public:
29+
ConnectionWrapper(std::shared_ptr<pqxx::connection>&& conn, PoolType& pool) noexcept
30+
: conn_{std::move(conn)}
31+
, pool_{&pool} {
32+
}
33+
34+
ConnectionWrapper(const ConnectionWrapper&) = delete;
35+
ConnectionWrapper& operator=(const ConnectionWrapper&) = delete;
36+
37+
ConnectionWrapper(ConnectionWrapper&&) = default;
38+
ConnectionWrapper& operator=(ConnectionWrapper&&) = default;
39+
40+
pqxx::connection& operator*() const& noexcept {
41+
return *conn_;
42+
}
43+
pqxx::connection& operator*() const&& = delete;
44+
45+
pqxx::connection* operator->() const& noexcept {
46+
return conn_.get();
47+
}
48+
49+
~ConnectionWrapper() {
50+
if (conn_) {
51+
pool_->ReturnConnection(std::move(conn_));
52+
}
53+
}
54+
55+
private:
56+
std::shared_ptr<pqxx::connection> conn_;
57+
PoolType* pool_;
58+
};
59+
60+
// ConnectionFactory is a functional object returning std::shared_ptr<pqxx::connection>
61+
template <typename ConnectionFactory>
62+
ConnectionPool(size_t capacity, ConnectionFactory&& connection_factory);
63+
64+
ConnectionWrapper GetConnection() ;
65+
66+
private:
67+
void ReturnConnection(ConnectionPtr&& conn) ;
68+
69+
std::mutex mutex_;
70+
std::condition_variable cond_var_;
71+
std::vector<ConnectionPtr> pool_;
72+
size_t used_connections_ = 0;
73+
};
74+
} //namespace db

sprint4/problems/leave_game/solution/src/http_server.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ namespace http_server {
5555
if (ec) {
5656
return ReportError(ec, "read"sv);
5757
}
58+
5859
HandleRequest(std::move(request_));
5960
}
6061

sprint4/problems/leave_game/solution/src/log.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include <string_view>
1010
#include <boost/log/utility/manipulators/add_value.hpp>
1111
#include <boost/json.hpp>
12+
#include <boost/exception/all.hpp>
13+
1214

1315
namespace logging = boost::log;
1416

sprint4/problems/leave_game/solution/src/main.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "request_handler.h"
1010
#include "ticker.h"
1111
#include "extra_data.h"
12+
#include "database_manager.h"
1213

1314
#include <boost/asio/io_context.hpp>
1415
#include <chrono>
@@ -37,7 +38,6 @@ void RunWorkers(unsigned n, const Fn& fn) {
3738
}
3839
fn();
3940
}
40-
4141
} // namespace
4242

4343
int main(int argc, const char* argv[]) {
@@ -64,11 +64,18 @@ int main(int argc, const char* argv[]) {
6464

6565
std::chrono::milliseconds tick_time = std::chrono::milliseconds(static_cast<int>(arg.period));
6666

67+
std::string db_url = std::getenv("DATABASE_URL");
68+
const unsigned num_threads = std::thread::hardware_concurrency();
69+
70+
auto conn_pool = db::CreateConnectionPool(num_threads, db_url.data());
71+
db::InitDatabase(conn_pool);
72+
6773
// 1. Загружаем карту из файла и строим модель игры
6874
model::Game game = json_loader::LoadGame(arg.config);
6975
game.SetDefaultTickTime(static_cast<double>(tick_time.count()));
76+
game.SetDBConnPool(&conn_pool);
7077

71-
app::Application app(game);
78+
app::Application app(game, conn_pool);
7279

7380
serialization::SerializingListener serializer(app, arg.state_file, std::chrono::milliseconds{3500});
7481
if (!arg.state_file.empty()) {
@@ -79,7 +86,6 @@ int main(int argc, const char* argv[]) {
7986
}
8087

8188
// 2. Инициализируем io_context
82-
const unsigned num_threads = std::thread::hardware_concurrency();
8389
net::io_context ioc(num_threads);
8490
net::strand strand = net::make_strand(ioc);
8591

0 commit comments

Comments
 (0)