Cavoke  1.1.0
A Platform for creating and hosting multiplayer turn-based board games
Loading...
Searching...
No Matches
game_logic_manager.cpp
1#include "game_logic_manager.h"
2// workaround for a boost bug...
3// https://github.com/boostorg/process/issues/96
4#ifndef __kernel_entry
5#define __kernel_entry
6#endif
7#include <boost/asio.hpp>
8#include <boost/process.hpp>
9#include <utility>
10
11namespace cavoke::server::model {
12
13GameLogicManager::GameLogicManager(std::shared_ptr<GamesStorage> games_storage)
14 : m_games_storage(std::move(games_storage)) {
15}
16
17std::string GameLogicManager::invoke_logic_local(const Game &game,
18 const std::string &input) {
19 namespace bp = boost::process;
20 boost::asio::io_service ios;
21
22 auto input_buffer = boost::asio::buffer(input);
23 bp::async_pipe input_pipe(ios);
24 std::vector<char> output_buffer(MAX_GAME_RESPONSE_B);
25
26 bp::child c(
27 system_complete(game.logic_file), bp::start_dir(game.directory),
28 bp::std_in<input_pipe, bp::std_out> boost::asio::buffer(output_buffer),
29 ios);
30
31 boost::asio::async_write(
32 input_pipe, input_buffer,
33 [&input_pipe](const boost::system::error_code &ec, std::size_t n) {
34 input_pipe.async_close();
35 });
36
37 ios.run();
38
39 if (!c.wait_for(std::chrono::milliseconds{LOGIC_TIMEOUT_MS})) {
40 LOG_ERROR << game.logic_file.string() << " logic executable timeout ("
41 << LOGIC_TIMEOUT_MS << " ms) exceeded";
42 c.terminate();
43 }
44
45 return std::string(output_buffer.begin(), output_buffer.end());
46}
47
48cavoke::ValidationResult GameLogicManager::validate_settings(
49 const std::string &game_id,
50 const json &settings,
51 const std::vector<int> &occupied_positions) {
52 std::optional<Game> game_info = m_games_storage->get_game_by_id(game_id);
53 if (!game_info.has_value()) {
54 throw validation_error("no game with id '" + game_id + "'");
55 }
56
57 std::string response;
58
59 if (game_info->config.app_type == "LOCAL") {
60 std::ostringstream request;
61 request << "VALIDATE ";
62 json request_data = InitSettings{settings, occupied_positions};
63 request << request_data;
64 response = invoke_logic_local(game_info.value(), request.str());
65 } else if (game_info->config.app_type == "EXTERNAL") {
66 response = invoke_logic_external(
67 game_info.value(), "validate",
68 json(InitSettings{settings, occupied_positions}).dump());
69 }
70
71 json response_json = json::parse(response);
72 auto result = response_json.get<ValidationResult>();
73
74 return result;
75}
76
77GameStateStorage::GameState GameLogicManager::init_state(
78 const std::string &game_id,
79 const json &settings,
80 const std::vector<int> &occupied_positions) {
81 std::optional<Game> game_info = m_games_storage->get_game_by_id(game_id);
82 if (!game_info.has_value()) {
83 return {};
84 }
85
86 std::string response;
87
88 if (game_info->config.app_type == "LOCAL") {
89 std::ostringstream request;
90 request << "INIT ";
91 json request_data = InitSettings{settings, occupied_positions};
92 request << request_data;
93 response = invoke_logic_local(game_info.value(), request.str());
94 } else if (game_info->config.app_type == "EXTERNAL") {
95 response = invoke_logic_external(
96 game_info.value(), "init_state",
97 json(InitSettings{settings, occupied_positions}).dump());
98 }
99
100 json response_json = json::parse(response);
101 auto result = response_json.get<GameStateStorage::GameState>();
102
103 return result;
104}
105
106GameStateStorage::GameState GameLogicManager::send_move(
107 const std::string &game_id,
108 const cavoke::GameMove &move) {
109 std::optional<Game> game_info = m_games_storage->get_game_by_id(game_id);
110 if (!game_info.has_value()) {
111 return {};
112 }
113
114 std::string response;
115
116 if (game_info->config.app_type == "LOCAL") {
117 std::ostringstream request;
118 request << "MOVE ";
119 json request_data = move;
120 request << request_data;
121 response = invoke_logic_local(game_info.value(), request.str());
122 } else if (game_info->config.app_type == "EXTERNAL") {
123 response = invoke_logic_external(game_info.value(), "apply_move",
124 json(move).dump());
125 }
126
127 json response_json = json::parse(response);
128 auto result = response_json.get<GameStateStorage::GameState>();
129
130 return result;
131}
132
133std::string GameLogicManager::invoke_logic_external(const Game &game,
134 const std::string &method,
135 const std::string &input) {
136 auto client = drogon::HttpClient::newHttpClient(game.config.url,
137 drogon::app().getLoop());
138 auto request = drogon::HttpRequest::newHttpRequest();
139 request->setPath("/" + method);
140 request->setMethod(drogon::Post);
141 request->setBody(input);
142 auto result = client->sendRequest(request, 30.0);
143
144 if (result.first != drogon::ReqResult::Ok) {
145 LOG_ERROR << "Failed to connect with game logic on address "
146 << game.config.url << " " << request->getPath() << ": "
147 << to_string(result.first);
148 return "";
149 }
150
151 return std::string(result.second->getBody());
152}
153
154validation_error::validation_error(std::string message)
155 : cavoke_base_exception(std::move(message),
156 InvalidConfig,
157 "cavoke/validation") {
158}
159
160} // namespace cavoke::server::model