Project Alice
Loading...
Searching...
No Matches
network.cpp
Go to the documentation of this file.
1#ifdef _WIN64 // WINDOWS
2#ifndef _MSC_VER
3#include <unistd.h>
4#endif
5#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
6#ifndef WINSOCK2_IMPORTED
7#define WINSOCK2_IMPORTED
8#include <winsock2.h>
9#include <ws2tcpip.h>
10#endif
11#include <windows.h>
12#include <natupnp.h>
13#include <iphlpapi.h>
14#include <Mstcpip.h>
15#include <ip2string.h>
16#include "pcp.h"
17#else // NIX
18#include <netinet/in.h>
19#include <sys/socket.h>
20#include <netdb.h>
21#include <arpa/inet.h>
22#include <stdio.h>
23#include <unistd.h>
24#endif // ...
25#include <string_view>
26#include "system_state.hpp"
27#include "commands.hpp"
28#include "SPSCQueue.h"
29#include "network.hpp"
30#include "serialization.hpp"
31#include "gui_error_window.hpp"
32
33#define ZSTD_STATIC_LINKING_ONLY
34#define XXH_NAMESPACE ZSTD_
35#include "zstd.h"
36
37#ifdef _WIN64
38#pragma comment(lib, "Iphlpapi.lib")
39#pragma comment(lib, "ntdll.lib")
40#endif
41
42namespace network {
43
44//
45// platform specific
46//
47
49 std::string address;
50 bool ipv6 = false;
51};
52
54
56#ifdef _WIN64
57 if(started)
58 return;
59
60 internal_wait.lock();
61 started = true;
62 do_forwarding = std::thread{ [&]() {
63 //setup forwarding
64 if(!SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)))
65 return;
66
67
68 std::vector<local_addresses> found_locals;
69
70 // try to figure out what the computer's local address is
71 {
72 IP_ADAPTER_ADDRESSES* adapter_addresses(NULL);
73 IP_ADAPTER_ADDRESSES* adapter(NULL);
74
75 // Start with a 16 KB buffer and resize if needed -
76 // multiple attempts in case interfaces change while
77 // we are in the middle of querying them.
78 DWORD adapter_addresses_buffer_size = 16 * 1024;
79 for(int attempts = 0; attempts != 3; ++attempts) {
80 adapter_addresses = (IP_ADAPTER_ADDRESSES*)malloc(adapter_addresses_buffer_size);
81 assert(adapter_addresses);
82
83 DWORD error = GetAdaptersAddresses(
84 AF_UNSPEC,
85 GAA_FLAG_SKIP_ANYCAST |
86 GAA_FLAG_SKIP_MULTICAST |
87 GAA_FLAG_SKIP_DNS_SERVER |
88 GAA_FLAG_SKIP_FRIENDLY_NAME,
89 NULL,
90 adapter_addresses,
91 &adapter_addresses_buffer_size);
92
93 if(ERROR_SUCCESS == error) {
94 // We're done here, people!
95 break;
96 } else if(ERROR_BUFFER_OVERFLOW == error) {
97 // Try again with the new size
98 free(adapter_addresses);
99 adapter_addresses = NULL;
100
101 continue;
102 } else {
103 // Unexpected error code - log and throw
104 free(adapter_addresses);
105 adapter_addresses = NULL;
106
107 // @todo
108 }
109 }
110
111 // Iterate through all of the adapters
112 for(adapter = adapter_addresses; NULL != adapter; adapter = adapter->Next) {
113 // Skip loopback adapters
114 if(IF_TYPE_SOFTWARE_LOOPBACK == adapter->IfType) {
115 continue;
116 }
117
118 // Parse all IPv4 and IPv6 addresses
119 for(
120 IP_ADAPTER_UNICAST_ADDRESS* address = adapter->FirstUnicastAddress;
121 NULL != address;
122 address = address->Next) {
123 auto family = address->Address.lpSockaddr->sa_family;
124 if(address->DadState != NldsPreferred && address->DadState != IpDadStatePreferred)
125 continue;
126
127 if(AF_INET == family) {
128 // IPv4
129 SOCKADDR_IN* ipv4 = reinterpret_cast<SOCKADDR_IN*>(address->Address.lpSockaddr);
130
131 char str_buffer[INET_ADDRSTRLEN] = { 0 };
132 inet_ntop(AF_INET, &(ipv4->sin_addr), str_buffer, INET_ADDRSTRLEN);
133 found_locals.push_back(local_addresses{ std::string(str_buffer), false});
134 } else if(AF_INET6 == family) {
135 // IPv6
136 SOCKADDR_IN6* ipv6 = reinterpret_cast<SOCKADDR_IN6*>(address->Address.lpSockaddr);
137
138 char str_buffer[INET6_ADDRSTRLEN] = { 0 };
139 inet_ntop(AF_INET6, &(ipv6->sin6_addr), str_buffer, INET6_ADDRSTRLEN);
140
141 std::string ipv6_str(str_buffer);
142
143 // Detect and skip non-external addresses
144 bool is_link_local(false);
145 bool is_special_use(false);
146
147 if(0 == ipv6_str.find("fe")) {
148 char c = ipv6_str[2];
149 if(c == '8' || c == '9' || c == 'a' || c == 'b') {
150 is_link_local = true;
151 }
152 } else if(0 == ipv6_str.find("2001:0:")) {
153 is_special_use = true;
154 }
155
156 if(!(is_link_local || is_special_use)) {
157 found_locals.push_back(local_addresses{ std::string(ipv6_str), true });
158 }
159 } else {
160 // Skip all other types of addresses
161 continue;
162 }
163 }
164 }
165
166 // Cleanup
167 free(adapter_addresses);
168 adapter_addresses = NULL;
169 }
170
171 // try to add port mapping
172 bool mapped_ports_with_upnp = false;
173 if(found_locals.size() != 0) {
174 IUPnPNAT* nat_interface = nullptr;
175 IStaticPortMappingCollection* port_mappings = nullptr;
176 IStaticPortMapping* opened_port = nullptr;
177
178 BSTR proto = SysAllocString(L"TCP");
179 BSTR desc = SysAllocString(L"Project Alice Host");
180 auto tmpwstr = simple_fs::utf8_to_native(found_locals[0].address);
181 BSTR local_host = SysAllocString(tmpwstr.c_str());
182 VARIANT_BOOL enabled = VARIANT_TRUE;
183
184 if(SUCCEEDED(CoCreateInstance(__uuidof(UPnPNAT), NULL, CLSCTX_ALL, __uuidof(IUPnPNAT), (void**)&nat_interface)) && nat_interface) {
185 if(SUCCEEDED(nat_interface->get_StaticPortMappingCollection(&port_mappings)) && port_mappings) {
186 if(SUCCEEDED(port_mappings->Add(default_server_port, proto, default_server_port, local_host, enabled, desc, &opened_port)) && opened_port) {
187 mapped_ports_with_upnp = true;
188 }
189 }
190 }
191
192 pcp_ctx_t* pcp_obj = nullptr;
193 if(!mapped_ports_with_upnp) {
194 pcp_obj = pcp_init(ENABLE_AUTODISCOVERY, NULL);
195
196 sockaddr_storage source_ip;
197 sockaddr_storage ext_ip;
198
199 WORD wVersionRequested;
200 WSADATA wsaData;
201
202 wVersionRequested = MAKEWORD(2, 2);
203 auto err = WSAStartup(wVersionRequested, &wsaData);
204
205 memset(&source_ip, 0, sizeof(source_ip));
206 memset(&ext_ip, 0, sizeof(ext_ip));
207
208 if(found_locals[0].ipv6 == false) {
209 ((sockaddr_in*)(&source_ip))->sin_port = 1984;
210 ((sockaddr_in*)(&source_ip))->sin_addr.s_addr = inet_addr(found_locals[0].address.c_str());
211 ((sockaddr_in*)(&source_ip))->sin_family = AF_INET;
212
213 ((sockaddr_in*)(&ext_ip))->sin_port = 1984;
214 ((sockaddr_in*)(&ext_ip))->sin_family = AF_INET;
215 } else {
216 ((sockaddr_in6*)(&source_ip))->sin6_port = 1984;
217 PCSTR term = nullptr;
218 RtlIpv6StringToAddressA(found_locals[0].address.c_str(), &term, &(((sockaddr_in6*)(&source_ip))->sin6_addr));
219 ((sockaddr_in6*)(&source_ip))->sin6_family = AF_INET6;
220
221 ((sockaddr_in6*)(&ext_ip))->sin6_port = 1984;
222 ((sockaddr_in6*)(&ext_ip))->sin6_family = AF_INET6;
223 }
224
225 auto flow = pcp_new_flow(pcp_obj, (sockaddr*)&source_ip, nullptr, (sockaddr*)&ext_ip, IPPROTO_TCP, 3600, nullptr);
226 if(flow)
227 pcp_wait(flow, 10000, 0);
228
229 }
230
231 // wait for destructor
232 internal_wait.lock();
233
234 if(pcp_obj) {
235 pcp_terminate(pcp_obj, 0);
236 }
237
238 //cleanup forwarding
239 if(port_mappings)
240 port_mappings->Remove(default_server_port, proto);
241
242 if(opened_port)
243 opened_port->Release();
244 if(port_mappings)
245 port_mappings->Release();
246 if(nat_interface)
247 nat_interface->Release();
248
249 SysFreeString(proto);
250 SysFreeString(local_host);
251 SysFreeString(desc);
252
253 internal_wait.unlock();
254 }
255 CoUninitialize();
256 } };
257#else
258#endif
259}
260
262#ifdef _WIN64
263 if(started) {
264 internal_wait.unlock();
265 do_forwarding.join();
266 }
267#else
268#endif
269}
270
271std::string get_last_error_msg() {
272#ifdef _WIN64
273 auto err = WSAGetLastError();
274 LPTSTR err_buf = nullptr;
275 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, err, 0, (LPTSTR)&err_buf, 0, nullptr);
276 native_string err_text = err_buf;
277 LocalFree(err_buf);
278 return std::to_string(err) + " = " + simple_fs::native_to_utf8(err_text);
279#else
280 return std::string("Dummy");
281#endif
282}
283
284static int internal_socket_recv(socket_t socket_fd, void *data, size_t n) {
285#ifdef _WIN64
286 u_long has_pending = 0;
287 auto r = ioctlsocket(socket_fd, FIONREAD, &has_pending);
288 if(has_pending)
289 return static_cast<int>(recv(socket_fd, reinterpret_cast<char *>(data), static_cast<int>(n), 0));
290 return 0;
291#else
292 return recv(socket_fd, data, n, MSG_DONTWAIT);
293#endif
294}
295
296static int internal_socket_send(socket_t socket_fd, const void *data, size_t n) {
297#ifdef _WIN64
298 return static_cast<int>(send(socket_fd, reinterpret_cast<const char *>(data), static_cast<int>(n), 0));
299#else
300 return send(socket_fd, data, n, MSG_NOSIGNAL);
301#endif
302}
303
304template<typename F>
305static int socket_recv(socket_t socket_fd, void* data, size_t len, size_t* m, F&& func) {
306 while(*m < len) {
307 int r = internal_socket_recv(socket_fd, reinterpret_cast<uint8_t*>(data) + *m, len - *m);
308 if(r > 0) {
309 *m += static_cast<size_t>(r);
310 } else if(r < 0) { // error
311#ifdef _WIN32
312 int err = WSAGetLastError();
313 if(err == WSAENOBUFS || err == WSAEWOULDBLOCK) {
314 return 0;
315 }
316 return err;
317#else
318 return r;
319#endif
320 } else if(r == 0) {
321 break;
322 }
323 }
324 // Did we receive a command?
325 if(*m >= len) {
326 assert(*m <= len);
327 *m = 0; // reset
328 func();
329 }
330 return 0;
331}
332
333static int socket_send(socket_t socket_fd, std::vector<char>& buffer) {
334 while(!buffer.empty()) {
335 int r = internal_socket_send(socket_fd, buffer.data(), buffer.size());
336 if(r > 0) {
337 buffer.erase(buffer.begin(), buffer.begin() + static_cast<size_t>(r));
338 } else if(r < 0) {
339#ifdef _WIN32
340 int err = WSAGetLastError();
341 if(err == WSAENOBUFS || err == WSAEWOULDBLOCK) {
342 return 0;
343 }
344 return err;
345#else
346 return r;
347#endif
348 } else if(r == 0) {
349 break;
350 }
351 }
352 return 0;
353}
354
355static void socket_add_to_send_queue(std::vector<char>& buffer, const void *data, size_t n) {
356 buffer.resize(buffer.size() + n);
357 std::memcpy(buffer.data() + buffer.size() - n, data, n);
358}
359
360static void socket_shutdown(socket_t socket_fd) {
361 if(socket_fd > 0) {
362#ifdef _WIN64
363 shutdown(socket_fd, SD_BOTH);
364 closesocket(socket_fd);
365#else
366 shutdown(socket_fd, SHUT_RDWR);
367 close(socket_fd);
368#endif
369 }
370}
371
372static socket_t socket_init_server(bool as_v6, struct sockaddr_storage& server_address) {
373 socket_t socket_fd = static_cast<socket_t>(socket(as_v6 ? AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_TCP));
374#ifdef _WIN64
375 if(socket_fd == static_cast<socket_t>(INVALID_SOCKET)) {
376 window::emit_error_message("Network socket error: " + get_last_error_msg(), true);
377 }
378#else
379 if(socket_fd < 0)
380 std::abort();
381#endif
382 struct timeval timeout;
383 timeout.tv_sec = 60;
384 timeout.tv_usec = 0;
385 int opt = 1;
386#ifdef _WIN64
387 if(setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt))) {
388 window::emit_error_message("Network setsockopt [reuseaddr] error: " + get_last_error_msg(), true);
389 }
390 if(setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0) {
391 window::emit_error_message("Network setsockopt [rcvtimeo] error: " + get_last_error_msg(), true);
392 }
393 if(setsockopt(socket_fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout)) < 0) {
394 window::emit_error_message("Network setsockopt [sndtimeo] error: " + get_last_error_msg(), true);
395 }
396#else
397 if(setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
398 std::abort();
399 }
400 if(setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
401 std::abort();
402 }
403 if(setsockopt(socket_fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0) {
404 std::abort();
405 }
406#endif
407 if(as_v6) {
408 struct sockaddr_in6 v6_server_address;
409 v6_server_address.sin6_addr = IN6ADDR_ANY_INIT;
410 v6_server_address.sin6_family = AF_INET6;
411 v6_server_address.sin6_port = htons(default_server_port);
412 std::memcpy(&server_address, &v6_server_address, sizeof(v6_server_address));
413 } else {
414 struct sockaddr_in v4_server_address;
415 v4_server_address.sin_addr.s_addr = INADDR_ANY;
416 v4_server_address.sin_family = AF_INET;
417 v4_server_address.sin_port = htons(default_server_port);
418 std::memcpy(&server_address, &v4_server_address, sizeof(v4_server_address));
419 }
420 if(bind(socket_fd, (struct sockaddr*)&server_address, as_v6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in)) < 0) {
421 window::emit_error_message("Network bind error: " + get_last_error_msg(), true);
422 }
423 if(listen(socket_fd, SOMAXCONN) < 0) {
424 window::emit_error_message("Network listen error: " + get_last_error_msg(), true);
425 }
426#ifdef _WIN64
427 u_long mode = 1; // 1 to enable non-blocking socket
428 ioctlsocket(socket_fd, FIONBIO, &mode);
429#endif
430 return socket_fd;
431}
432
433static socket_t socket_init_client(bool& as_v6, struct sockaddr_storage& client_address, const char *ip_address) {
434 struct addrinfo hints;
435 std::memset(&hints, 0, sizeof(hints));
436 hints.ai_family = AF_UNSPEC;
437 hints.ai_socktype = SOCK_STREAM;
438 hints.ai_protocol = IPPROTO_TCP;
439 struct addrinfo* result = NULL;
440 if(getaddrinfo(ip_address, "1984", &hints, &result) != 0) {
441 window::emit_error_message("Network getaddrinfo error: " + get_last_error_msg(), true);
442 }
443 as_v6 = false;
444 bool found = false;
445 for(struct addrinfo* ptr = result; ptr != NULL; ptr = ptr->ai_next) {
446 if(ptr->ai_socktype == SOCK_STREAM && ptr->ai_protocol == IPPROTO_TCP) {
447 if(ptr->ai_family == AF_INET || ptr->ai_family == AF_INET6) {
448 as_v6 = ptr->ai_family == AF_INET6;
449 std::memcpy(&client_address, ptr->ai_addr, sizeof(sockaddr));
450 found = true;
451 }
452 }
453 }
454 freeaddrinfo(result);
455 if(!found) {
456 window::emit_error_message("No suitable host found", true);
457 }
458 socket_t socket_fd = static_cast<socket_t>(socket(as_v6 ? AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_TCP));
459#ifdef _WIN64
460 if(socket_fd == static_cast<socket_t>(INVALID_SOCKET)) {
461 window::emit_error_message("Network socket error: " + get_last_error_msg(), true);
462 }
463#else
464 if(socket_fd < 0) {
465 window::emit_error_message("Network socket error: " + get_last_error_msg(), true);
466 }
467#endif
468 if(connect(socket_fd, (const struct sockaddr*)&client_address, as_v6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in)) < 0) {
469 window::emit_error_message("Network connect error: " + get_last_error_msg(), true);
470 }
471 return socket_fd;
472}
473
474//
475// non-platform specific
476//
477
478static dcon::nation_id get_temp_nation(sys::state& state) {
479 // give the client a "joining" nation, basically a temporal nation choosen
480 // "randomly" that is tied to the client iself
481 for(auto n : state.nations_by_rank)
482 if(!state.world.nation_get_is_player_controlled(n) && state.world.nation_get_owned_province_count(n) > 0) {
483 bool is_taken = false;
484 for(auto& client : state.network_state.clients) {
485 if(client.playing_as == n) {
486 is_taken = true;
487 break;
488 }
489 }
490 if(!is_taken) {
491 return n;
492 }
493 }
494 return dcon::nation_id{ };
495}
496
497void init(sys::state& state) {
498 if(state.network_mode == sys::network_mode_type::single_player)
499 return; // Do nothing in singleplayer
500
501 state.network_state.finished = false;
502#ifdef _WIN64
503 WSADATA data;
504 if(WSAStartup(MAKEWORD(2, 2), &data) != 0) {
505 window::emit_error_message("WSA startup error: " + get_last_error_msg(), true);
506 }
507#endif
508 if(state.network_mode == sys::network_mode_type::host) {
509 state.network_state.socket_fd = socket_init_server(state.network_state.as_v6, state.network_state.address);
510 } else {
511 assert(state.network_state.ip_address.size() > 0);
512 state.network_state.socket_fd = socket_init_client(state.network_state.as_v6, state.network_state.address, state.network_state.ip_address.c_str());
513 }
514
515 // Host must have an already selected nation, to prevent issues...
516 if(state.network_mode == sys::network_mode_type::host) {
517 state.local_player_nation = get_temp_nation(state);
518 assert(bool(state.local_player_nation));
519 /* Materialize it into a command we send to new clients who connect and have to replay everything... */
521 memset(&c, 0, sizeof(c));
523 c.source = state.local_player_nation;
524 c.data.player_name = state.network_state.nickname;
525 state.network_state.outgoing_commands.push(c);
526 }
527}
528
529static void disconnect_client(sys::state& state, client_data& client, bool graceful) {
530 if(command::can_notify_player_leaves(state, client.playing_as, graceful)) {
531 command::notify_player_leaves(state, client.playing_as, graceful);
532 }
533 socket_shutdown(client.socket_fd);
534 client.socket_fd = 0;
535 client.send_buffer.clear();
536 client.early_send_buffer.clear();
537 client.total_sent_bytes = 0;
538 client.save_stream_size = 0;
539 client.save_stream_offset = 0;
540 client.playing_as = dcon::nation_id{};
541 client.recv_count = 0;
542 client.handshake = true;
543}
544
545static uint8_t* write_network_compressed_section(uint8_t* ptr_out, uint8_t const* ptr_in, uint32_t uncompressed_size) {
546 uint32_t decompressed_length = uncompressed_size;
547 uint32_t section_length = uint32_t(ZSTD_compress(ptr_out + sizeof(uint32_t) * 2, ZSTD_compressBound(uncompressed_size), ptr_in,
548 uncompressed_size, ZSTD_maxCLevel())); // write compressed data
549 memcpy(ptr_out, &section_length, sizeof(uint32_t));
550 memcpy(ptr_out + sizeof(uint32_t), &decompressed_length, sizeof(uint32_t));
551 return ptr_out + sizeof(uint32_t) * 2 + section_length;
552}
553
554template<typename T>
555static uint8_t const* with_network_decompressed_section(uint8_t const* ptr_in, T const& function) {
556 uint32_t section_length = 0;
557 uint32_t decompressed_length = 0;
558 memcpy(&section_length, ptr_in, sizeof(uint32_t));
559 memcpy(&decompressed_length, ptr_in + sizeof(uint32_t), sizeof(uint32_t));
560 auto temp_buffer = std::unique_ptr<uint8_t[]>(new uint8_t[decompressed_length]);
561 ZSTD_decompress(temp_buffer.get(), decompressed_length, ptr_in + sizeof(uint32_t) * 2, section_length);
562 function(temp_buffer.get(), decompressed_length);
563 return ptr_in + sizeof(uint32_t) * 2 + section_length;
564}
565
567 if(state.network_state.as_v6) {
568 auto sa = (struct sockaddr_in6 const*)&address;
569 return std::find_if(state.network_state.v6_banlist.begin(), state.network_state.v6_banlist.end(), [&](auto const& a) {
570 return std::memcmp(&sa->sin6_addr, &a, sizeof(a)) == 0;
571 }) != state.network_state.v6_banlist.end();
572 } else {
573 auto sa = (struct sockaddr_in const*)&address;
574 return std::find_if(state.network_state.v4_banlist.begin(), state.network_state.v4_banlist.end(), [&](auto const& a) {
575 return std::memcmp(&sa->sin_addr, &a, sizeof(a)) == 0;
576 }) != state.network_state.v4_banlist.end();
577 }
578}
579
580static void send_post_handshake_commands(sys::state& state, network::client_data& client) {
581 std::vector<char> tmp = client.send_buffer;
582 client.send_buffer.clear();
583 if(state.current_scene.starting_scene) {
584 /* Send the savefile to the newly connected client (if not a new game) */
585 if(!state.network_state.is_new_game) {
587 memset(&c, 0, sizeof(command::payload));
589 c.source = state.local_player_nation;
590 c.data.notify_save_loaded.target = client.playing_as;
591 network::broadcast_save_to_clients(state, c, state.network_state.current_save_buffer.get(), state.network_state.current_save_length, state.network_state.current_save_checksum);
592#ifndef NDEBUG
593 state.console_log("host:send:cmd: (new(2)->save_loaded)");
594#endif
595 }
596 { /* Tell this client about every other client */
598 memset(&c, 0, sizeof(c));
600 for(const auto n : state.world.in_nation) {
601 if(n == client.playing_as) {
602 c.source = client.playing_as;
603 c.data.player_name = client.hshake_buffer.nickname;
604 broadcast_to_clients(state, c);
605 command::execute_command(state, c);
606#ifndef NDEBUG
607 state.console_log("host:send:cmd: (new(2)->self_join)");
608#endif
609 } else if(n.get_is_player_controlled()) {
610 c.source = n;
611 c.data.player_name = state.network_state.map_of_player_names[n.id.index()];
612 socket_add_to_send_queue(client.send_buffer, &c, sizeof(c));
613#ifndef NDEBUG
614 state.console_log("host:send:cmd: (new(2)->others_join) " + std::to_string(n.id.index()));
615#endif
616 }
617 }
618 }
619 } else if(state.current_scene.game_in_progress) {
620 { /* Tell this client about every other client */
622 memset(&c, 0, sizeof(c));
624 for(const auto n : state.world.in_nation) {
625 if(n == client.playing_as) {
626 c.source = client.playing_as;
627 c.data.player_name = client.hshake_buffer.nickname;
628 broadcast_to_clients(state, c);
629 command::execute_command(state, c);
630#ifndef NDEBUG
631 state.console_log("host:send:cmd: (new->self_join)");
632#endif
633 } else if(n.get_is_player_controlled()) {
634 c.source = n;
635 c.data.player_name = state.network_state.map_of_player_names[n.id.index()];
636 socket_add_to_send_queue(client.send_buffer, &c, sizeof(c));
637#ifndef NDEBUG
638 state.console_log("host:send:cmd: (new->others_join) " + std::to_string(n.id.index()));
639#endif
640 }
641 }
642 }
643 /* Reload clients */
644 if(!state.network_state.is_new_game) {
645 std::vector<dcon::nation_id> players;
646 for(const auto n : state.world.in_nation)
647 if(state.world.nation_get_is_player_controlled(n))
648 players.push_back(n);
649 dcon::nation_id old_local_player_nation = state.local_player_nation;
650 state.local_player_nation = dcon::nation_id{ };
652 /* Then reload as if we loaded the save data */
653 state.preload();
654 with_network_decompressed_section(state.network_state.current_save_buffer.get(), [&state](uint8_t const* ptr_in, uint32_t length) {
655 read_save_section(ptr_in, ptr_in + length, state);
656 });
657 state.fill_unsaved_data();
658 for(const auto n : players)
659 state.world.nation_set_is_player_controlled(n, true);
660 state.local_player_nation = old_local_player_nation;
661 assert(state.world.nation_get_is_player_controlled(state.local_player_nation));
662 { /* Reload all the other clients except the newly connected one */
664 memset(&c, 0, sizeof(command::payload));
666 c.source = state.local_player_nation;
667 c.data.notify_reload.checksum = state.get_save_checksum();
668 for(auto& other_client : state.network_state.clients) {
669 if(other_client.playing_as != client.playing_as) {
670 socket_add_to_send_queue(other_client.send_buffer, &c, sizeof(c));
671#ifndef NDEBUG
672 state.console_log("host:send:cmd: (new->reload)");
673#endif
674 }
675 }
676 }
677 { /* Send the savefile to the newly connected client (if not a new game) */
679 memset(&c, 0, sizeof(command::payload));
681 c.source = state.local_player_nation;
682 c.data.notify_save_loaded.target = client.playing_as;
683 network::broadcast_save_to_clients(state, c, state.network_state.current_save_buffer.get(), state.network_state.current_save_length, state.network_state.current_save_checksum);
684#ifndef NDEBUG
685 state.console_log("host:send:cmd: (new->save_loaded)");
686#endif
687 }
688 }
689 {
691 memset(&c, 0, sizeof(c));
693 c.source = state.local_player_nation;
694 socket_add_to_send_queue(client.send_buffer, &c, sizeof(c));
695#ifndef NDEBUG
696 state.console_log("host:send:cmd: (new->start_game)");
697#endif
698 }
699 }
700 auto old_size = client.send_buffer.size();
701 client.send_buffer.resize(old_size + tmp.size());
702 std::memcpy(client.send_buffer.data() + old_size, tmp.data(), tmp.size());
703}
704
705static void receive_from_clients(sys::state& state) {
706 for(auto& client : state.network_state.clients) {
707 if(!client.is_active())
708 continue;
709 int r = 0;
710 if(client.handshake) {
711 r = socket_recv(client.socket_fd, &client.hshake_buffer, sizeof(client.hshake_buffer), &client.recv_count, [&]() {
712 if(std::memcmp(client.hshake_buffer.password, state.network_state.password, sizeof(state.network_state.password)) != 0) {
713 disconnect_client(state, client, false);
714 return;
715 }
716 send_post_handshake_commands(state, client);
717 /* Exit from handshake mode */
718 client.handshake = false;
719 state.game_state_updated.store(true, std::memory_order::release);
720 });
721 } else {
722 r = socket_recv(client.socket_fd, &client.recv_buffer, sizeof(client.recv_buffer), &client.recv_count, [&]() {
723 switch(client.recv_buffer.type) {
724 case command::command_type::invalid:
725 case command::command_type::notify_player_ban:
726 case command::command_type::notify_player_kick:
727 case command::command_type::notify_save_loaded:
728 case command::command_type::notify_reload:
729 case command::command_type::advance_tick:
730 case command::command_type::notify_start_game:
731 case command::command_type::notify_stop_game:
732 case command::command_type::notify_pause_game:
733 case command::command_type::notify_player_joins:
734 case command::command_type::save_game:
735 break; // has to be valid/sendable by client
736 default:
737 /* Has to be from the nation of the client proper - and early
738 discard invalid commands */
739 if(client.recv_buffer.source == client.playing_as
740 && command::can_perform_command(state, client.recv_buffer)) {
741 state.network_state.outgoing_commands.push(client.recv_buffer);
742 }
743 break;
744 }
745#ifndef NDEBUG
746 state.console_log("host:recv:client_cmd: " + std::to_string(uint32_t(client.recv_buffer.type)));
747#endif
748 });
749 }
750 if(r != 0) { // error
751#if !defined(NDEBUG) && defined(_WIN32)
752 state.console_log("host:disconnect: in-receive err=" + std::to_string(int32_t(r)) + "::" + get_last_error_msg());
753#endif
754 network::disconnect_client(state, client, false);
755 }
756 }
757}
758
760 /* A save lock will be set when we load a save, naturally loading a save implies
761 that we have done preload/fill_unsaved so we will skip doing that again, to save a
762 bit of sanity on our miserable CPU */
763 size_t length = sizeof_save_section(state);
764 auto save_buffer = std::unique_ptr<uint8_t[]>(new uint8_t[length]);
765 /* Clear the player nation */
766 assert(state.local_player_nation == dcon::nation_id{ });
767 write_save_section(save_buffer.get(), state); //writeoff data
768 // this is an upper bound, since compacting the data may require less space
769 state.network_state.current_save_buffer.reset(new uint8_t[ZSTD_compressBound(length) + sizeof(uint32_t) * 2]);
770 auto buffer_position = write_network_compressed_section(state.network_state.current_save_buffer.get(), save_buffer.get(), uint32_t(length));
771 state.network_state.current_save_length = uint32_t(buffer_position - state.network_state.current_save_buffer.get());
772 state.network_state.current_save_checksum = state.get_save_checksum();
773}
774
776 assert(length > 0);
779 for(auto& client : state.network_state.clients) {
780 if(!client.is_active())
781 continue;
782 bool send_full = (client.playing_as == c.data.notify_save_loaded.target) || (!c.data.notify_save_loaded.target);
783 if(send_full && !state.network_state.is_new_game) {
784 /* And then we have to first send the command payload itself */
785 client.save_stream_size = size_t(length);
786 c.data.notify_save_loaded.length = size_t(length);
787 socket_add_to_send_queue(client.send_buffer, &c, sizeof(c));
788 /* And then the bulk payload! */
789 client.save_stream_offset = client.total_sent_bytes + client.send_buffer.size();
790 socket_add_to_send_queue(client.send_buffer, buffer, size_t(length));
791#ifndef NDEBUG
792 state.console_log("host:send:save: " + std::to_string(uint32_t(length)));
793#endif
794 }
795 }
796}
797
800 return;
802 /* Propagate to all the clients */
803 for(auto& client : state.network_state.clients) {
804 if(client.is_active()) {
805 socket_add_to_send_queue(client.send_buffer, &c, sizeof(c));
806 }
807 }
808}
809
810static void accept_new_clients(sys::state& state) {
811 /* Check if any new clients are to join us */
812 fd_set rfds;
813 FD_ZERO(&rfds);
814 FD_SET(state.network_state.socket_fd, &rfds);
815 struct timeval tv{};
816 tv.tv_sec = 0;
817 tv.tv_usec = 1000;
818 if(select(socket_t(int(state.network_state.socket_fd) + 1), &rfds, nullptr, nullptr, &tv) <= 0)
819 return;
820
821 // Find available slot for client
822 for(auto& client : state.network_state.clients) {
823 if(client.is_active())
824 continue;
825 socklen_t addr_len = state.network_state.as_v6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in);
826 client.socket_fd = accept(state.network_state.socket_fd, (struct sockaddr*)&client.address, &addr_len);
827 if(client.is_banned(state)) {
828 disconnect_client(state, client, false);
829 break;
830 }
831 if(state.current_scene.final_scene) {
832 disconnect_client(state, client, false);
833 break;
834 }
835 /* Send it data so she is in sync with everyone else! */
836 client.playing_as = get_temp_nation(state);
837 assert(client.playing_as);
838 { /* Tell the client their assigned nation */
839 server_handshake_data hshake;
840 hshake.seed = state.game_seed;
841 hshake.assigned_nation = client.playing_as;
842 hshake.scenario_checksum = state.scenario_checksum;
843 hshake.save_checksum = state.get_save_checksum();
844 socket_add_to_send_queue(client.early_send_buffer, &hshake, sizeof(hshake));
845 }
846#ifndef NDEBUG
847 state.console_log("host:send:cmd: handshake -> " + std::to_string(client.playing_as.index()));
848#endif
849 return;
850 }
851}
852
854 /* An issue that arose in multiplayer is that the UI was loading the savefile
855 directly, while the game state loop was running, this was fine with the
856 assumption that commands weren't executed while the save was being loaded
857 HOWEVER in multiplayer this is often the case, so we have to block all
858 commands until the savefile is finished loading
859 This way, we're able to effectively and safely queue commands until we
860 can receive them AFTER loading the savefile. */
861 if(state.network_state.save_slock.load(std::memory_order::acquire) == true)
862 return;
863
864 if(state.network_state.finished)
865 return;
866
867 bool command_executed = false;
868 if(state.network_mode == sys::network_mode_type::host) {
869 accept_new_clients(state); // accept new connections
870 receive_from_clients(state); // receive new commands
871 // send the commands of the server to all the clients
872 auto* c = state.network_state.outgoing_commands.front();
873 while(c) {
875 // Generate checksum on the spot
877 if(state.current_date.to_ymd(state.start_date).day == 1 || state.cheat_data.daily_oos_check) {
878 c->data.advance_tick.checksum = state.get_save_checksum();
879 }
880 }
881 broadcast_to_clients(state, *c);
882 command::execute_command(state, *c);
883 command_executed = true;
884#ifndef NDEBUG
885 state.console_log("host:receive:cmd: " + std::to_string(uint32_t(c->type)));
886#endif
887 }
888 state.network_state.outgoing_commands.pop();
889 c = state.network_state.outgoing_commands.front();
890 }
891
892 for(auto& client : state.network_state.clients) {
893 if(!client.is_active())
894 continue;
895 if(client.handshake) {
896 if(client.early_send_buffer.size() > 0) {
897 size_t old_size = client.early_send_buffer.size();
898 int r = socket_send(client.socket_fd, client.early_send_buffer);
899 if(r != 0) { // error
900#if !defined(NDEBUG) && defined(_WIN32)
901 state.console_log("host:disconnect: in-send-EARLY err=" + std::to_string(int32_t(r)) + "::" + get_last_error_msg());
902#endif
903 disconnect_client(state, client, false);
904 continue;
905 }
906 client.total_sent_bytes += old_size - client.early_send_buffer.size();
907#ifndef NDEBUG
908 if(old_size != client.early_send_buffer.size())
909 state.console_log("host:send:stats: [EARLY] " + std::to_string(uint32_t(client.total_sent_bytes)) + " bytes");
910#endif
911 }
912 } else {
913 if(client.send_buffer.size() > 0) {
914 size_t old_size = client.send_buffer.size();
915 int r = socket_send(client.socket_fd, client.send_buffer);
916 if(r != 0) { // error
917#if !defined(NDEBUG) && defined(_WIN32)
918 state.console_log("host:disconnect: in-send-INGAME err=" + std::to_string(int32_t(r)) + "::" + get_last_error_msg());
919#endif
920 disconnect_client(state, client, false);
921 continue;
922 }
923 client.total_sent_bytes += old_size - client.send_buffer.size();
924#ifndef NDEBUG
925 if(old_size != client.send_buffer.size())
926 state.console_log("host:send:stats: [SEND] " + std::to_string(uint32_t(client.total_sent_bytes)) + " bytes");
927#endif
928 }
929 }
930 }
931 } else if(state.network_mode == sys::network_mode_type::client) {
932 if(state.network_state.handshake) {
933 /* Send our client's handshake */
934 int r = socket_recv(state.network_state.socket_fd, &state.network_state.s_hshake, sizeof(state.network_state.s_hshake), &state.network_state.recv_count, [&]() {
935#ifndef NDEBUG
936 state.console_log("client:recv:handshake: OK");
937#endif
938 if(!state.scenario_checksum.is_equal(state.network_state.s_hshake.scenario_checksum)) {
939 bool found_match = false;
940 // Find a scenario with a matching checksum
941 auto dir = simple_fs::get_or_create_scenario_directory();
942 for(const auto& uf : simple_fs::list_files(dir, NATIVE(".bin"))) {
943 auto f = simple_fs::open_file(uf);
944 if(f) {
945 auto contents = simple_fs::view_contents(*f);
946 sys::scenario_header scen_header;
947 if(contents.file_size > sizeof(sys::scenario_header)) {
948 sys::read_scenario_header(reinterpret_cast<const uint8_t*>(contents.data), scen_header);
949 if(!scen_header.checksum.is_equal(state.network_state.s_hshake.scenario_checksum))
950 continue; // Same checksum
951 if(scen_header.version != sys::scenario_file_version)
952 continue; // Same version of scenario
953 if(sys::try_read_scenario_and_save_file(state, simple_fs::get_file_name(uf))) {
954 state.fill_unsaved_data();
955 found_match = true;
956 break;
957 }
958 }
959 }
960 }
961 if(!found_match) {
962 std::string msg = "Could not find a scenario with a matching checksum!"
963 "This is most likely a false positive, so just ask the host for their"
964 "scenario file and it should work. Or you haven't clicked on 'Make scenario'!";
965 msg += "\n";
966 msg += "Host should give you the scenario from:\n"
967 "'My Documents\\Project Alice\\scenarios\\<Most recent scenario>'";
968 msg += "And you place it on:\n"
969 "'My Documents\\Project Alice\\scenarios\\'\n";
970
971 window::emit_error_message(msg.c_str(), true);
972 }
973 }
974 state.session_host_checksum = state.network_state.s_hshake.save_checksum;
975 state.game_seed = state.network_state.s_hshake.seed;
976 state.local_player_nation = state.network_state.s_hshake.assigned_nation;
977 state.world.nation_set_is_player_controlled(state.local_player_nation, true);
978 /* Send our client handshake back */
980 hshake.nickname = state.network_state.nickname;
981 std::memcpy(hshake.password, state.network_state.password, sizeof(hshake.password));
982 socket_add_to_send_queue(state.network_state.send_buffer, &hshake, sizeof(hshake));
983 state.network_state.handshake = false;
984
985 //update map
986 state.map_state.set_selected_province(dcon::province_id{});
987 state.map_state.unhandled_province_selection = true;
988 });
989 if(r != 0) { // error
990 ui::popup_error_window(state, "Network Error", "Network client handshake receive error: " + get_last_error_msg());
991 network::finish(state, false);
992 return;
993 }
994 } else if(state.network_state.save_stream) {
995 int r = socket_recv(state.network_state.socket_fd, state.network_state.save_data.data(), state.network_state.save_data.size(), &state.network_state.recv_count, [&]() {
996#ifndef NDEBUG
997 state.console_log("client:recv:save: len=" + std::to_string(uint32_t(state.network_state.save_data.size())));
998#endif
999 std::vector<dcon::nation_id> players;
1000 for(const auto n : state.world.in_nation)
1001 if(state.world.nation_get_is_player_controlled(n))
1002 players.push_back(n);
1003 dcon::nation_id old_local_player_nation = state.local_player_nation;
1004 state.preload();
1005 with_network_decompressed_section(state.network_state.save_data.data(), [&state](uint8_t const* ptr_in, uint32_t length) {
1006 read_save_section(ptr_in, ptr_in + length, state);
1007 });
1008 state.local_player_nation = dcon::nation_id{ };
1009 state.fill_unsaved_data();
1010 for(const auto n : players)
1011 state.world.nation_set_is_player_controlled(n, true);
1012 state.local_player_nation = old_local_player_nation;
1013 assert(state.world.nation_get_is_player_controlled(state.local_player_nation));
1014#ifndef NDEBUG
1015 auto save_checksum = state.get_save_checksum();
1016 assert(save_checksum.is_equal(state.session_host_checksum));
1017#endif
1018 state.railroad_built.store(true, std::memory_order::release);
1019 state.game_state_updated.store(true, std::memory_order::release);
1020 state.network_state.save_data.clear();
1021 state.network_state.save_stream = false; // go back to normal command loop stuff
1022 });
1023 if(r != 0) { // error
1024 ui::popup_error_window(state, "Network Error", "Network client save stream receive error: " + get_last_error_msg());
1025 network::finish(state, false);
1026 return;
1027 }
1028 } else {
1029 // receive commands from the server and immediately execute them
1030 int r = socket_recv(state.network_state.socket_fd, &state.network_state.recv_buffer, sizeof(state.network_state.recv_buffer), &state.network_state.recv_count, [&]() {
1031 command::execute_command(state, state.network_state.recv_buffer);
1032 command_executed = true;
1033 // start save stream!
1034 if(state.network_state.recv_buffer.type == command::command_type::notify_save_loaded) {
1035 uint32_t save_size = state.network_state.recv_buffer.data.notify_save_loaded.length;
1036 state.network_state.save_stream = true;
1037 assert(save_size > 0);
1038 if(save_size >= 32 * 1000 * 1000) { // 32 MB
1039 ui::popup_error_window(state, "Network Error", "Network client save stream too big: " + get_last_error_msg());
1040 network::finish(state, false);
1041 return;
1042 }
1043 state.network_state.save_data.resize(static_cast<size_t>(save_size));
1044 }
1045#ifndef NDEBUG
1046 state.console_log("client:recv:cmd: " + std::to_string(uint32_t(state.network_state.recv_buffer.type)));
1047#endif
1048 });
1049 if(r != 0) { // error
1050 ui::popup_error_window(state, "Network Error", "Network client command receive error: " + get_last_error_msg());
1051 network::finish(state, false);
1052 return;
1053 }
1054 // send the outgoing commands to the server and flush the entire queue
1055 auto* c = state.network_state.outgoing_commands.front();
1056 while(c) {
1057#ifndef NDEBUG
1058 state.console_log("client:send:cmd: " + std::to_string(uint32_t(c->type)));
1059#endif
1061 command::execute_command(state, *c);
1062 command_executed = true;
1063 } else {
1064 socket_add_to_send_queue(state.network_state.send_buffer, c, sizeof(*c));
1065 }
1066 state.network_state.outgoing_commands.pop();
1067 c = state.network_state.outgoing_commands.front();
1068 }
1069 }
1070 /* Do not send commands while we're on save stream mode! */
1071 if(!state.network_state.save_stream) {
1072 if(socket_send(state.network_state.socket_fd, state.network_state.send_buffer) != 0) { // error
1073 ui::popup_error_window(state, "Network Error", "Network client command send error: " + get_last_error_msg());
1074 network::finish(state, false);
1075 return;
1076 }
1077 }
1078 assert(state.network_state.early_send_buffer.empty()); //do not use the early send buffer
1079 }
1080
1081 if(command_executed) {
1082 if(state.network_state.out_of_sync && !state.network_state.reported_oos) {
1083 command::notify_player_oos(state, state.local_player_nation);
1084 state.network_state.reported_oos = true;
1085 }
1086 state.game_state_updated.store(true, std::memory_order::release);
1087 }
1088}
1089
1090void finish(sys::state& state, bool notify_host) {
1091 if(state.network_mode == sys::network_mode_type::single_player)
1092 return; // Do nothing in singleplayer
1093
1094 state.network_state.finished = true;
1095 if(notify_host && state.network_mode == sys::network_mode_type::client) {
1096 if(!state.network_state.save_stream) {
1097 // send the outgoing commands to the server and flush the entire queue
1098 {
1099 auto* c = state.network_state.outgoing_commands.front();
1100 while(c) {
1102 command::execute_command(state, *c);
1103 } else {
1104 socket_add_to_send_queue(state.network_state.send_buffer, c, sizeof(*c));
1105 }
1106 state.network_state.outgoing_commands.pop();
1107 c = state.network_state.outgoing_commands.front();
1108 }
1109 }
1111 memset(&c, 0, sizeof(c));
1113 c.source = state.local_player_nation;
1114 c.data.notify_leave.make_ai = true;
1115 socket_add_to_send_queue(state.network_state.send_buffer, &c, sizeof(c));
1116 while(state.network_state.send_buffer.size() > 0) {
1117 if(socket_send(state.network_state.socket_fd, state.network_state.send_buffer) != 0) { // error
1118 //ui::popup_error_window(state, "Network Error", "Network client command send error: " + get_last_error_msg());
1119 break;
1120 }
1121 }
1122 }
1123 }
1124
1125 socket_shutdown(state.network_state.socket_fd);
1126#ifdef _WIN64
1127 WSACleanup();
1128#endif
1129}
1130
1131void ban_player(sys::state& state, client_data& client) {
1132 if(!client.is_active())
1133 return;
1134 socket_shutdown(client.socket_fd);
1135 client.socket_fd = 0;
1136 if(state.network_state.as_v6) {
1137 auto sa = (struct sockaddr_in6*)&client.address;
1138 state.network_state.v6_banlist.push_back(sa->sin6_addr);
1139 } else {
1140 auto sa = (struct sockaddr_in*)&client.address;
1141 state.network_state.v4_banlist.push_back(sa->sin_addr);
1142 }
1143}
1144
1145void kick_player(sys::state& state, client_data& client) {
1146 if(!client.is_active())
1147 return;
1148 socket_shutdown(client.socket_fd);
1149 client.socket_fd = 0;
1150}
1151
1152void switch_player(sys::state& state, dcon::nation_id new_n, dcon::nation_id old_n) {
1153 state.network_state.map_of_player_names.insert_or_assign(new_n.index(), state.network_state.map_of_player_names[old_n.index()]);
1154 if(state.network_mode == sys::network_mode_type::host) {
1155 for(auto& client : state.network_state.clients) {
1156 if(!client.is_active())
1157 continue;
1158 if(client.playing_as == old_n) {
1159 client.playing_as = new_n;
1160 }
1161 }
1162 }
1163}
1164
1165}
#define assert(condition)
Definition: debug.h:74
#define INVALID_SOCKET
Definition: findsaddr-udp.c:68
bool is_console_command(command_type t)
Definition: commands.cpp:19
bool can_notify_player_leaves(sys::state &state, dcon::nation_id source, bool make_ai)
Definition: commands.cpp:4544
void execute_command(sys::state &state, payload &c)
Definition: commands.cpp:5158
void accept(sys::state &state, message const &m)
void broadcast_save_to_clients(sys::state &state, command::payload &c, uint8_t const *buffer, uint32_t length, sys::checksum_key const &k)
Definition: network.cpp:775
void write_network_save(sys::state &state)
Definition: network.cpp:759
std::string get_last_error_msg()
Definition: network.cpp:271
void broadcast_to_clients(sys::state &state, command::payload &c)
Definition: network.cpp:798
void kick_player(sys::state &state, client_data &client)
Definition: network.cpp:1145
constexpr short default_server_port
Definition: network.hpp:26
void finish(sys::state &state, bool notify_host)
Definition: network.cpp:1090
void switch_player(sys::state &state, dcon::nation_id new_n, dcon::nation_id old_n)
Definition: network.cpp:1152
void ban_player(sys::state &state, client_data &client)
Definition: network.cpp:1131
void send_and_receive_commands(sys::state &state)
Definition: network.cpp:853
int socket_t
Definition: network.hpp:31
void init(sys::state &state)
Definition: network.cpp:497
native_string utf8_to_native(std::string_view data_in)
std::string native_to_utf8(native_string_view data_in)
void popup_error_window(sys::state &state, std::string_view title, std::string_view body)
void send(sys::state &state, element_base *parent, T value)
T select(bool v, T a, T b)
void emit_error_message(std::string const &content, bool fatal)
Definition: window_nix.cpp:355
std::string native_string
uint uint32_t
uchar uint8_t
#define ENABLE_AUTODISCOVERY
Definition: pcp.h:120
pcp_ctx_t * pcp_init(uint8_t autodiscovery, pcp_socket_vt_t *socket_vt)
Definition: pcp_api.c:93
pcp_fstate_e pcp_wait(pcp_flow_t *flow, int timeout, int exit_on_partial_res)
Definition: pcp_api.c:187
pcp_flow_t * pcp_new_flow(pcp_ctx_t *ctx, struct sockaddr *src_addr, struct sockaddr *dst_addr, struct sockaddr *ext_addr, uint8_t protocol, uint32_t lifetime, void *userdata)
Definition: pcp_api.c:389
void pcp_terminate(pcp_ctx_t *ctx, int close_flows)
Definition: pcp_api.c:662
#define inet_ntop
#define MSG_DONTWAIT
sys::checksum_key checksum
Definition: commands.hpp:439
sys::checksum_key checksum
Definition: commands.hpp:449
command_type type
Definition: commands.hpp:521
union command::payload::dtype data
dcon::nation_id source
Definition: commands.hpp:520
struct sockaddr_storage address
Definition: network.hpp:51
bool is_banned(sys::state &state) const
Definition: network.cpp:566
sys::player_name nickname
Definition: network.hpp:35
notify_reload_data notify_reload
Definition: commands.hpp:511
advance_tick_data advance_tick
Definition: commands.hpp:508
notify_save_loaded_data notify_save_loaded
Definition: commands.hpp:510
notify_leaves_data notify_leave
Definition: commands.hpp:514
sys::player_name player_name
Definition: commands.hpp:512
size_t ZSTD_compressBound(size_t srcSize)
Definition: zstd_compress.c:69
int ZSTD_maxCLevel(void)
size_t ZSTD_compress(void *dst, size_t dstCapacity, const void *src, size_t srcSize, int compressionLevel)
size_t ZSTD_decompress(void *dst, size_t dstCapacity, const void *src, size_t srcSize)