-
-
Notifications
You must be signed in to change notification settings - Fork 988
/
wesnothd_connection.hpp
232 lines (194 loc) · 6.14 KB
/
wesnothd_connection.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/*
Copyright (C) 2011 - 2017 by Sergey Popov <loonycyborg@gmail.com>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#pragma once
#ifdef _WIN32
// MSVC compilation throws deprecation warnings on boost's use of gethostbyaddr and
// gethostbyname in socket_ops.ipp. This define silences that.
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define BOOST_ASIO_DISABLE_IOCP
#ifdef INADDR_ANY
#undef INADDR_ANY
#endif
#ifdef INADDR_BROADCAST
#undef INADDR_BROADCAST
#endif
#ifdef INADDR_NONE
#undef INADDR_NONE
#endif
#endif // endif _WIN32
#include <boost/asio.hpp>
#include <deque>
#include <list>
#include <mutex>
#include "exceptions.hpp"
#include "wesnothd_connection_error.hpp"
#include "configr_assign.hpp"
namespace boost
{
class thread;
}
class config;
class wesnothd_connection_ptr;
enum class loading_stage;
/** A class that represents a TCP/IP connection to the wesnothd server. */
class wesnothd_connection : public std::enable_shared_from_this<wesnothd_connection>
{
public:
using error = wesnothd_connection_error;
wesnothd_connection(const wesnothd_connection&) = delete;
wesnothd_connection& operator=(const wesnothd_connection&) = delete;
~wesnothd_connection();
private:
/**
* Constructor.
*
* May only be called via wesnothd_connection_ptr
* @param host Name of the host to connect to
* @param service Service identifier such as "80" or "http"
*/
wesnothd_connection(const std::string& host, const std::string& service);
public:
static wesnothd_connection_ptr create(const std::string& host, const std::string& service);
bool fetch_data_with_loading_screen(config& cfg, loading_stage stage);
void send_data(const configr_of& request);
bool receive_data(config& result);
/**
* Helper function that spins until data has been received.
* Should be used in tandem with the loading screen or other multi-threaded components.
*/
bool wait_and_receive_data(config& data);
/** Handle all pending asynchonous events and return */
std::size_t poll();
/** Run asio's event loop
*
* Handle asynchronous events blocking until all asynchronous
* operations have finished
*/
void cancel();
// Destroys this object.
void stop();
/** True if connected and no high-level operation is in progress */
bool handshake_finished() const { return handshake_finished_; }
std::size_t bytes_to_write() const
{
return bytes_to_write_;
}
std::size_t bytes_written() const
{
return bytes_written_;
}
std::size_t bytes_to_read() const
{
return bytes_to_read_;
}
std::size_t bytes_read() const
{
return bytes_read_;
}
bool has_data_received() const
{
return !recv_queue_.empty();
}
bool is_sending_data() const
{
return !send_queue_.empty();
}
private:
std::unique_ptr<boost::thread> worker_thread_;
boost::asio::io_service io_service_;
typedef boost::asio::ip::tcp::resolver resolver;
resolver resolver_;
typedef boost::asio::ip::tcp::socket socket;
socket socket_;
boost::system::error_code last_error_;
std::mutex last_error_mutex_;
bool handshake_finished_;
boost::asio::streambuf read_buf_;
void handle_resolve(
const boost::system::error_code& ec,
resolver::iterator iterator
);
void connect(resolver::iterator iterator);
void handle_connect(
const boost::system::error_code& ec,
resolver::iterator iterator
);
void handshake();
void handle_handshake(
const boost::system::error_code& ec
);
union {
char binary[4];
uint32_t num;
} handshake_response_;
std::size_t is_write_complete(const boost::system::error_code& error, std::size_t bytes_transferred);
void handle_write(const boost::system::error_code& ec, std::size_t bytes_transferred);
std::size_t is_read_complete(const boost::system::error_code& error, std::size_t bytes_transferred);
void handle_read(const boost::system::error_code& ec, std::size_t bytes_transferred);
void send();
void recv();
std::list<std::shared_ptr<boost::asio::streambuf>> send_queue_;
std::list<config> recv_queue_;
std::mutex recv_queue_mutex_;
uint32_t payload_size_;
// TODO: do i need to guard the follwing 4 values with a mutex?
std::size_t bytes_to_write_;
std::size_t bytes_written_;
std::size_t bytes_to_read_;
std::size_t bytes_read_;
};
/*
* This class acts like a unique_ptr<wesnothd_connection>, wesnothd_connection objects may only be owned though this pointer.
* The reason why we need this is that wesnothd_connection runs a workerthread so we use a shared_ptr to make sure
* the wesnothd_connection isn't destroyed before the worker thread has finished. When this object is destroed, it calls
* wesnothd_connection::stop() which stops the worker thread which will then destroy the other shared_ptr<wesnothd_connection>
* which destroys the wesnothd_connection object.
*/
class wesnothd_connection_ptr
{
private:
friend class wesnothd_connection;
wesnothd_connection_ptr(std::shared_ptr<wesnothd_connection>&& ptr)
: ptr_(std::move(ptr))
{}
public:
wesnothd_connection_ptr() = default;
wesnothd_connection_ptr(const wesnothd_connection_ptr&) = delete;
wesnothd_connection_ptr& operator=(const wesnothd_connection_ptr&) = delete;
#if defined(_MSC_VER) &&_MSC_VER == 1800
wesnothd_connection_ptr(wesnothd_connection_ptr&& other) : ptr_(std::move(other.ptr_)) {}
#else
wesnothd_connection_ptr(wesnothd_connection_ptr&&) = default;
#endif
wesnothd_connection_ptr& operator=(wesnothd_connection_ptr&&);
~wesnothd_connection_ptr();
explicit operator bool() const {
return !!ptr_;
}
wesnothd_connection& operator*() {
return *ptr_;
}
const wesnothd_connection& operator*() const {
return *ptr_;
}
wesnothd_connection* operator->() {
return ptr_.get();
}
const wesnothd_connection* operator->() const {
return ptr_.get();
}
wesnothd_connection* get() const {
return ptr_.get();
}
private:
std::shared_ptr<wesnothd_connection> ptr_;
};