23 #ifndef JWT_GAME_SERVER_GAME_SERVER_HPP
24 #define JWT_GAME_SERVER_GAME_SERVER_HPP
26 #include "base_server.hpp"
34 using namespace std::chrono_literals;
41 template<
typename game_instance,
typename jwt_clock,
typename json_traits,
42 typename server_config,
typename close_reasons = default_close_reasons>
47 typename game_instance::player_traits,
59 using message = pair<player_id, std::string>;
67 struct connection_update {
68 connection_update(
const combined_id& i) : id(i), disconnection(
true) {}
69 connection_update(
const combined_id& i, json&& d) : id(i),
70 data(std::move(d)), disconnection(
false) {}
84 const jwt::verifier<jwt_clock, json_traits>& v,
85 function<std::string(
const combined_id&,
const json&)> f,
86 std::chrono::milliseconds t
87 ) : m_game_count(0), m_jwt_server(v, f, t)
89 m_jwt_server.set_open_handler(
91 &game_server::player_connect,
93 simple_web_game_server::_1,
94 simple_web_game_server::_2
97 m_jwt_server.set_close_handler(
99 &game_server::player_disconnect,
101 simple_web_game_server::_1
104 m_jwt_server.set_message_handler(
106 &game_server::process_message,
108 simple_web_game_server::_1,
109 simple_web_game_server::_2
116 const jwt::verifier<jwt_clock, json_traits>& v,
117 function<std::string(
const combined_id&,
const json&)> f
123 m_jwt_server.set_tls_init_handler(f);
127 void run(uint16_t port,
bool unlock_address =
false) {
128 m_jwt_server.run(port, unlock_address);
133 m_jwt_server.process_messages();
139 m_jwt_server.reset();
145 m_game_condition.notify_one();
147 lock_guard<mutex> guard(m_game_list_lock);
149 m_out_messages.clear();
150 m_connection_updates.second.clear();
151 m_in_messages.second.clear();
154 lock_guard<mutex> guard(m_in_message_list_lock);
155 m_in_messages.first.clear();
158 lock_guard<mutex> guard(m_connection_update_list_lock);
159 m_connection_updates.first.clear();
162 lock_guard<mutex> guard(m_game_count_lock);
169 return m_jwt_server.get_player_count();
173 return m_jwt_server.is_running();
185 auto time_start = clock::now();
186 vector<session_id> finished_games;
188 while(m_jwt_server.is_running()) {
189 const auto delta_time =
190 std::chrono::duration_cast<std::chrono::milliseconds>(
191 clock::now() - time_start
194 unique_lock<mutex> game_lock(m_game_list_lock);
195 if(m_games.empty()) {
197 unique_lock<mutex> conn_lock(m_connection_update_list_lock);
198 while(m_connection_updates.first.empty()) {
199 m_game_condition.wait(conn_lock);
200 if(!m_jwt_server.is_running()) {
203 time_start = clock::now();
208 if(delta_time < timestep) {
210 std::this_thread::sleep_for(std::min(1ms, timestep-delta_time));
212 time_start = clock::now();
213 process_connection_updates();
218 lock_guard<mutex> gc_guard(m_game_count_lock);
219 for(session_id sid : finished_games) {
220 spdlog::trace(
"erasing game session {}", sid);
221 m_out_messages.erase(sid);
226 finished_games.clear();
228 process_game_updates(delta_time.count());
230 for(
auto it = m_out_messages.begin(); it != m_out_messages.end();
233 for(message& msg : it->second) {
234 m_jwt_server.send_message(
235 { msg.first, it->first },
236 std::move(msg.second)
242 for(
auto it = m_games.begin(); it != m_games.end(); ++it) {
243 if(it->second.is_done()) {
244 spdlog::debug(
"game session {} ended", it->first);
245 m_jwt_server.complete_session(
248 it->second.get_state()
250 finished_games.push_back(it->first);
259 lock_guard<mutex> guard(m_game_count_lock);
264 void process_connection_updates() {
266 lock_guard<mutex> conn_guard(m_connection_update_list_lock);
267 std::swap(m_connection_updates.first, m_connection_updates.second);
270 for(connection_update& update : m_connection_updates.second) {
271 auto games_it = m_games.find(update.id.session);
272 auto out_messages_it = m_out_messages.find(update.id.session);
274 if(update.disconnection) {
275 if(games_it != m_games.end()) {
276 games_it->second.disconnect(
277 out_messages_it->second,
282 if(games_it == m_games.end()) {
283 game_instance game{update.data};
285 if(!game.is_valid()) {
286 spdlog::error(
"connection provided invalid game data");
287 m_jwt_server.complete_session(
288 update.id.session, update.id.session, game.get_state()
293 spdlog::debug(
"creating game session {}", update.id.session);
294 games_it = m_games.emplace(
295 update.id.session, std::move(game)
297 out_messages_it = m_out_messages.emplace(
298 update.id.session, vector<message>{}
301 lock_guard<mutex> gc_guard(m_game_count_lock);
306 games_it->second.connect(out_messages_it->second, update.id.player);
310 m_connection_updates.second.clear();
313 void process_game_updates(
long delta_time) {
315 lock_guard<mutex> msg_guard(m_in_message_list_lock);
316 std::swap(m_in_messages.first, m_in_messages.second);
324 [&](
auto& key_val_pair){
325 auto in_msg_it = m_in_messages.second.find(key_val_pair.first);
326 if(in_msg_it != m_in_messages.second.end()) {
327 key_val_pair.second.update(
328 m_out_messages.at(key_val_pair.first),
333 key_val_pair.second.update(
334 m_out_messages.at(key_val_pair.first),
342 m_in_messages.second.clear();
345 void process_message(
const combined_id&
id, std::string&& data) {
346 lock_guard<mutex> msg_guard(m_in_message_list_lock);
347 m_in_messages.first[
id.session].emplace_back(
348 id.player, std::move(data)
352 void player_connect(
const combined_id&
id, json&& data) {
354 lock_guard<mutex> guard(m_connection_update_list_lock);
355 m_connection_updates.first.emplace_back(
359 m_game_condition.notify_one();
362 void player_disconnect(
const combined_id&
id) {
363 lock_guard<mutex> guard(m_connection_update_list_lock);
364 m_connection_updates.first.emplace_back(
id);
373 mutex m_game_list_lock;
375 std::size_t m_game_count;
376 mutex m_game_count_lock;
390 mutex m_in_message_list_lock;
393 vector<connection_update>,
394 vector<connection_update>
395 > m_connection_updates;
396 mutex m_connection_update_list_lock;
398 condition_variable m_game_condition;
400 unordered_map<session_id, vector<message>, id_hash> m_out_messages;
402 jwt_base_server m_jwt_server;
A WebSocket server that performs authentication and manages sessions.
Definition: base_server.hpp:107
std::chrono::high_resolution_clock clock
The type of clock for server time-step management.
Definition: base_server.hpp:131
typename combined_id::player_id player_id
The type of the player component of a client id.
Definition: base_server.hpp:122
typename player_traits::id combined_id
The type of a client id.
Definition: base_server.hpp:120
typename combined_id::session_id session_id
The type of the session component of a client id.
Definition: base_server.hpp:124
websocketpp::lib::shared_ptr< websocketpp::lib::asio::ssl::context > ssl_context_ptr
The type of a pointer to an asio ssl context.
Definition: base_server.hpp:135
typename json_traits::json json
The type of a json object.
Definition: base_server.hpp:129
typename combined_id::hash id_hash
The type of the hash struct for all id types.
Definition: base_server.hpp:126
A game server built on the base_server class.
Definition: game_server.hpp:43
void update_games(std::chrono::milliseconds timestep)
Loop to run games.
Definition: game_server.hpp:184
game_server(const jwt::verifier< jwt_clock, json_traits > &v, function< std::string(const combined_id &, const json &)> f)
Constructs the underlying base_server with a default time-step.
Definition: game_server.hpp:115
std::size_t get_player_count()
Returns the number of verified clients connected.
Definition: game_server.hpp:168
std::size_t get_game_count()
Returns the number of running game sessions.
Definition: game_server.hpp:258
void process_messages()
Runs the process_messages loop on the underlying base_server.
Definition: game_server.hpp:132
void stop()
Stops the server and clears all data and connections.
Definition: game_server.hpp:143
void set_tls_init_handler(function< ssl_context_ptr(connection_hdl)> f)
Sets the tls_init_handler for the underlying base_server.
Definition: game_server.hpp:122
void run(uint16_t port, bool unlock_address=false)
Runs the underlying base_server.
Definition: game_server.hpp:127
void reset()
Stops, clears, and resets the server so it may be run again.
Definition: game_server.hpp:137
game_server(const jwt::verifier< jwt_clock, json_traits > &v, function< std::string(const combined_id &, const json &)> f, std::chrono::milliseconds t)
The constructor for the game_server class.
Definition: game_server.hpp:83
Definition: base_server.hpp:52