-
Notifications
You must be signed in to change notification settings - Fork 57
/
connection.h
298 lines (264 loc) · 9.86 KB
/
connection.h
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
#ifdef USE_SSL
#ifndef CONNECTION_H
#define CONNECTION_H
/*!
* \file
* \brief Handling the connection with the server
*/
#ifdef __cplusplus
#include <cstdint>
#include <mutex>
#include <string>
#include <vector>
#include "queue.h"
#include "socket.h"
#include "textpopup.h"
namespace eternal_lands
{
/*!
* \brief Class for the connection with the server
*
* Class Connection handles the network connection with the game server. It has functions for
* sending message to, and receiving messages from, the game server.
*/
class Connection
{
public:
//! Return the singleton Connection object
static Connection& get_instance()
{
static Connection connection;
return connection;
}
//! Return whether the connection to the game server is currently broken
bool is_disconnected() const { return !_socket.is_connected() && !_socket.in_tls_handshake(); }
/*!
* \brief Set server to connect to
*
* Set the parameters of the server to connect to. This information is cached here so that we
* can easily reconnect after a network failure.
*
* \param name The host name of the server to connect to
* \param port The port number on the host to connect to
* \param encrypted If \c true, encrypt the connection.
*/
void set_server(const char* name, std::uint16_t port, bool encrypted);
/*!
* \brief Connect to the game server
*
* Set up a connection to the game server specified by a previous call to set_server(). If the
* user was previously logged in, this also resends the login information so the user can
* immediately continue playing.
*/
void connect_to_server();
//! Break the connection with the server with a default message
void disconnect_from_server() { disconnect_from_server("Grue?"); }
/*!
* \brief Disconnect from the server
*
* Break the connection with the game server. and write a message to the text console.
*
* \param message The reason for the disconnect
* \sa disconnect_from_server_locked
*/
void disconnect_from_server(const std::string& message)
{
std::lock_guard<std::mutex> guard(_out_mutex);
disconnect_from_server_locked(message);
}
/*!
* \brief Start encrypting the connection
*
* If \a encrypt is \c true, Set up an encrypted connection with the server. This is done
* after a LETS_ENCRYPT message has been sent to the server, and it responds with an affirmative
* LETS_ENCRYPT reply. If the server does not support encryption, \a encrypt is \c false, and
* the connection will be broken.
* \param encrypt Whether the server supports encryption.
*/
void start_tls_handshake(bool encrypt);
void start_connection_test();
void check_connection_test();
void stop_connection_test() { _connection_test_tick = 0; }
//! Send a single byte command \a cmd without further data to the server
std::size_t send(std::uint8_t cmd) { return send(cmd, nullptr, 0); }
/*!
* \brief Send a message to the server
*
* Send a message with command code \a cmd and \a len bytes of additional data in \a data
* to the server. It is possible that the message is buffered first, to be sent later.
*
* \param cmd The command code for the message
* \param data Additional data for the command
* \param len The number of bytes in \a data
* \return The number of bytes actually sent or buffered.
*/
std::size_t send(std::uint8_t cmd, const std::uint8_t* data, std::size_t len);
/*!
* \brief Flush the output buffer
*
* Send all data in the output buffer to the server.
* \sa flush_locked
*/
std::size_t flush()
{
std::lock_guard<std::mutex> guard(_out_mutex);
return flush_locked();
}
void receive(queue_t* queue, int *done);
void check_heart_beat()
{
if (!is_disconnected() && time(nullptr) - _last_heart_beat >= 25)
send_heart_beat();
}
void send_login_info();
/*!
* \brief Set whether a login attempt succeeded
*
* Set whether the last attemp to log in on the server succeeeded. This information is used
* to determine whether to automaticcaly send the login information again after a reconnect,
* \param success \c true if the login attempt was successful, \c false otherwise.
*/
void set_logged_in(bool success) { _previously_logged_in = success; }
/*!
* \brief Send command to move to a tile.
*
* When \a try_pathfinding is \c true and the configuration option \a always_pathfinding is
* also \c true, this function tries to find a path (near) to destination tile (\a *x, \a *y)
* using the client-side pathfinder, and when successful, updates \a x and \a y with the
* coordinates of the actual destination being walked to. If the pathfinder is not used, or
* fails to find a path, a request to move the actor to (\a *x, \a *y) is sent to the server.
* The return value can be used to determine whether to highlight the destintion position.
* It is \c true when the path finder succeeds, or when the pathfinder was not tried and
* the destination tile is walkable; in all other cases \c false is returned.
* \param x x-coordinate of the position to move to
* \param y y-coordinate of the position to move to
* \param try_pathfinder Whether to try using the client-side pathfinder
* \return \c true if a path was found by the pathfinder, or the pathfinder was bypassed and
* the destination tile is walkable.
*/
bool send_move_to(std::int16_t *x, std::int16_t *y, bool try_pathfinder);
void send_new_char(const std::string& username, const std::string& password,
std::uint8_t skin, std::uint8_t hair, std::uint8_t eyes, std::uint8_t shirt,
std::uint8_t pants, std::uint8_t boots, std::uint8_t head, std::uint8_t type);
//! Send a ping request to the server
void send_ping_request();
void clean_up()
{
_socket.close();
_error_popup.reset();
}
private:
static const std::uint16_t protocol_version_first_digit = 10; // protocol/game version sent to server
static const std::uint16_t protocol_version_second_digit = 30;
static const size_t max_out_buffer_size = 8192;
static const size_t max_in_buffer_size = 8192;
static const size_t max_cache_size = 256;
//! How long too wait for a response to an invitation to encrypt, in milliseconds
std::uint32_t lets_encrypt_timeout = 5000;
std::string _server_name;
unsigned short _server_port;
bool _encrypted;
TCPSocket _socket;
std::unique_ptr<TextPopup> _error_popup;
std::mutex _out_mutex;
std::vector<std::uint8_t> _out_buffer;
std::vector<std::uint8_t> _cache;
std::array<std::uint8_t, max_in_buffer_size> _in_buffer;
size_t _in_buffer_used;
//! \c true when we are currently waiting for a response from the server to an encryption request
bool _awaiting_encrypt_response;
//! Time tick of when invitation to encrypt was sent
std::uint32_t _lets_encrypt_tick;
time_t _last_heart_beat;
std::uint32_t _last_sit_tick;
std::uint32_t _last_turn_tick;
std::uint32_t _cache_tick;
std::uint32_t _connection_test_tick;
bool _invalid_version;
bool _previously_logged_in;
/*!
* \brief Constructor
*
* Set up a new Connection object, that is not yet connected to a game server,
* \sa set_server, connect_to_server
*/
Connection(): _server_name(), _server_port(2000), _encrypted(false), _socket(), _error_popup(),
_out_mutex(), _out_buffer(), _cache(), _in_buffer(), _in_buffer_used(0),
_awaiting_encrypt_response(false), _lets_encrypt_tick(0), _last_heart_beat(0),
_last_sit_tick(0), _last_turn_tick(0), _connection_test_tick(0),
_invalid_version(false), _previously_logged_in(false) {}
//! Destructor
~Connection() { clean_up(); }
/*!
* \brief Disconnect from the server
*
* Break the connection with the game server. and write a message to the text console. The
* output mutex must be locked by the caller.
*
* \param message The reason for the disconnect
* \sa disconnect_from_server
*/
void disconnect_from_server_locked(const std::string& message);
/*!
* \brief Actually send the data
*
* Send \a data_len bytes of data in \a data to the server. The output mutex must be locked
* by the caller.
* \param data The data bytes to send
* \param data_len The number of bytes in \a data to send
* \return The number of bytes actuallt sent
*/
std::size_t send_data_locked(const std::uint8_t* data, size_t data_len);
/*!
* \brief Flush the output buffer
*
* Send all data in the output buffer to the server. The output mutex must be locked by the
* caller.
* \sa flush
*/
std::size_t flush_locked();
void process_incoming_data(queue_t *queue);
void send_heart_beat();
void send_version();
void close_after_invalid_certificate();
void finish_connect_to_server();
/*!
* \brief Finish connecting to the server
*
* Finish connecting to the server after encryption was succesfully set up. Logs a message to
* the console, then calls finish_connect_to_server().
*/
void finish_connect_to_server_encrypted();
};
} // namespace eternal_lands
#endif // __cplusplus
#ifdef __cplusplus
extern "C"
{
#endif
int is_disconnected(void);
void start_tls_handshake(int encrypt);
void connection_set_server(const char* name, uint16_t port, int encrypted);
void connect_to_server(void);
void force_server_disconnect(const char *message);
void start_testing_server_connection(void);
void check_if_testing_server_connection(void);
void stop_testing_server_connection(void);
void check_heart_beat(void);
void send_login_info(void);
void set_logged_in(int success);
void send_new_char(const char* user_str, const char* pass_str, char skin, char hair, char eyes,
char shirt, char pants, char boots,char head, char type);
int move_to(short int *x, short int *y, int try_pathfinder);
void send_ping_request(void);
void handle_encryption_invitation(void);
int my_tcp_send(const Uint8* str, int len);
int my_tcp_flush(void);
void cleanup_tcp(void);
int get_message_from_server(void *thread_args);
extern int server_connect_timeout_s; /*! Config option. The timeout in seconds to wait when connecting to a server. */
#ifdef __cplusplus
} // extern "C"
#endif
#endif // CONNECTION_H
#endif // USE_SSL