5#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
6#ifndef WINSOCK2_IMPORTED
7#define WINSOCK2_IMPORTED
18#include <netinet/in.h>
19#include <sys/socket.h>
33#define ZSTD_STATIC_LINKING_ONLY
34#define XXH_NAMESPACE ZSTD_
38#pragma comment(lib, "Iphlpapi.lib")
39#pragma comment(lib, "ntdll.lib")
62 do_forwarding = std::thread{ [&]() {
64 if(!SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)))
68 std::vector<local_addresses> found_locals;
72 IP_ADAPTER_ADDRESSES* adapter_addresses(NULL);
73 IP_ADAPTER_ADDRESSES* adapter(NULL);
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);
83 DWORD error = GetAdaptersAddresses(
85 GAA_FLAG_SKIP_ANYCAST |
86 GAA_FLAG_SKIP_MULTICAST |
87 GAA_FLAG_SKIP_DNS_SERVER |
88 GAA_FLAG_SKIP_FRIENDLY_NAME,
91 &adapter_addresses_buffer_size);
93 if(ERROR_SUCCESS == error) {
96 }
else if(ERROR_BUFFER_OVERFLOW == error) {
98 free(adapter_addresses);
99 adapter_addresses = NULL;
104 free(adapter_addresses);
105 adapter_addresses = NULL;
112 for(adapter = adapter_addresses; NULL != adapter; adapter = adapter->Next) {
114 if(IF_TYPE_SOFTWARE_LOOPBACK == adapter->IfType) {
120 IP_ADAPTER_UNICAST_ADDRESS* address = adapter->FirstUnicastAddress;
122 address = address->Next) {
123 auto family = address->Address.lpSockaddr->sa_family;
124 if(address->DadState != NldsPreferred && address->DadState != IpDadStatePreferred)
127 if(AF_INET == family) {
129 SOCKADDR_IN* ipv4 =
reinterpret_cast<SOCKADDR_IN*
>(address->Address.lpSockaddr);
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) {
136 SOCKADDR_IN6* ipv6 =
reinterpret_cast<SOCKADDR_IN6*
>(address->Address.lpSockaddr);
138 char str_buffer[INET6_ADDRSTRLEN] = { 0 };
139 inet_ntop(AF_INET6, &(ipv6->sin6_addr), str_buffer, INET6_ADDRSTRLEN);
141 std::string ipv6_str(str_buffer);
144 bool is_link_local(
false);
145 bool is_special_use(
false);
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;
152 }
else if(0 == ipv6_str.find(
"2001:0:")) {
153 is_special_use =
true;
156 if(!(is_link_local || is_special_use)) {
157 found_locals.push_back(
local_addresses{ std::string(ipv6_str),
true });
167 free(adapter_addresses);
168 adapter_addresses = NULL;
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;
178 BSTR proto = SysAllocString(L
"TCP");
179 BSTR desc = SysAllocString(L
"Project Alice Host");
181 BSTR local_host = SysAllocString(tmpwstr.c_str());
182 VARIANT_BOOL enabled = VARIANT_TRUE;
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) {
187 mapped_ports_with_upnp =
true;
193 if(!mapped_ports_with_upnp) {
196 sockaddr_storage source_ip;
197 sockaddr_storage ext_ip;
199 WORD wVersionRequested;
202 wVersionRequested = MAKEWORD(2, 2);
203 auto err = WSAStartup(wVersionRequested, &wsaData);
205 memset(&source_ip, 0,
sizeof(source_ip));
206 memset(&ext_ip, 0,
sizeof(ext_ip));
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;
213 ((sockaddr_in*)(&ext_ip))->sin_port = 1984;
214 ((sockaddr_in*)(&ext_ip))->sin_family = AF_INET;
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;
221 ((sockaddr_in6*)(&ext_ip))->sin6_port = 1984;
222 ((sockaddr_in6*)(&ext_ip))->sin6_family = AF_INET6;
225 auto flow =
pcp_new_flow(pcp_obj, (sockaddr*)&source_ip,
nullptr, (sockaddr*)&ext_ip, IPPROTO_TCP, 3600,
nullptr);
232 internal_wait.lock();
243 opened_port->Release();
245 port_mappings->Release();
247 nat_interface->Release();
249 SysFreeString(proto);
250 SysFreeString(local_host);
253 internal_wait.unlock();
264 internal_wait.unlock();
265 do_forwarding.join();
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);
280 return std::string(
"Dummy");
284static int internal_socket_recv(
socket_t socket_fd,
void *data,
size_t n) {
286 u_long has_pending = 0;
287 auto r = ioctlsocket(socket_fd, FIONREAD, &has_pending);
289 return static_cast<int>(recv(socket_fd,
reinterpret_cast<char *
>(data),
static_cast<int>(n), 0));
296static int internal_socket_send(
socket_t socket_fd,
const void *data,
size_t n) {
298 return static_cast<int>(
send(socket_fd,
reinterpret_cast<const char *
>(data),
static_cast<int>(n), 0));
300 return send(socket_fd, data, n, MSG_NOSIGNAL);
305static int socket_recv(
socket_t socket_fd,
void* data,
size_t len,
size_t* m, F&& func) {
307 int r = internal_socket_recv(socket_fd,
reinterpret_cast<uint8_t*
>(data) + *m, len - *m);
309 *
m +=
static_cast<size_t>(r);
312 int err = WSAGetLastError();
313 if(err == WSAENOBUFS || err == WSAEWOULDBLOCK) {
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());
337 buffer.erase(buffer.begin(), buffer.begin() +
static_cast<size_t>(r));
340 int err = WSAGetLastError();
341 if(err == WSAENOBUFS || err == WSAEWOULDBLOCK) {
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);
360static void socket_shutdown(
socket_t socket_fd) {
363 shutdown(socket_fd, SD_BOTH);
364 closesocket(socket_fd);
366 shutdown(socket_fd, SHUT_RDWR);
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));
382 struct timeval timeout;
387 if(setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (
char*)&opt,
sizeof(opt))) {
390 if(setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, (
char*)&timeout,
sizeof(timeout)) < 0) {
393 if(setsockopt(socket_fd, SOL_SOCKET, SO_SNDTIMEO, (
char*)&timeout,
sizeof(timeout)) < 0) {
397 if(setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt,
sizeof(opt))) {
400 if(setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout,
sizeof(timeout)) < 0) {
403 if(setsockopt(socket_fd, SOL_SOCKET, SO_SNDTIMEO, &timeout,
sizeof(timeout)) < 0) {
408 struct sockaddr_in6 v6_server_address;
409 v6_server_address.sin6_addr = IN6ADDR_ANY_INIT;
410 v6_server_address.sin6_family = AF_INET6;
412 std::memcpy(&server_address, &v6_server_address,
sizeof(v6_server_address));
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;
418 std::memcpy(&server_address, &v4_server_address,
sizeof(v4_server_address));
420 if(bind(socket_fd, (
struct sockaddr*)&server_address, as_v6 ?
sizeof(sockaddr_in6) :
sizeof(sockaddr_in)) < 0) {
423 if(listen(socket_fd, SOMAXCONN) < 0) {
428 ioctlsocket(socket_fd, FIONBIO, &mode);
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) {
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));
454 freeaddrinfo(result);
458 socket_t socket_fd =
static_cast<socket_t>(socket(as_v6 ? AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_TCP));
468 if(connect(socket_fd, (
const struct sockaddr*)&client_address, as_v6 ?
sizeof(sockaddr_in6) :
sizeof(sockaddr_in)) < 0) {
478static dcon::nation_id get_temp_nation(
sys::state& state) {
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) {
494 return dcon::nation_id{ };
501 state.network_state.finished =
false;
504 if(WSAStartup(MAKEWORD(2, 2), &data) != 0) {
509 state.network_state.socket_fd = socket_init_server(state.network_state.as_v6, state.network_state.address);
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());
517 state.local_player_nation = get_temp_nation(state);
518 assert(
bool(state.local_player_nation));
521 memset(&c, 0,
sizeof(c));
523 c.
source = state.local_player_nation;
525 state.network_state.outgoing_commands.push(c);
529static void disconnect_client(
sys::state& state, client_data& client,
bool graceful) {
533 socket_shutdown(
client.socket_fd);
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{};
546 uint32_t decompressed_length = uncompressed_size;
549 memcpy(ptr_out, §ion_length,
sizeof(
uint32_t));
551 return ptr_out +
sizeof(
uint32_t) * 2 + section_length;
555static uint8_t const* with_network_decompressed_section(
uint8_t const* ptr_in, T
const& function) {
558 memcpy(§ion_length, ptr_in,
sizeof(
uint32_t));
560 auto temp_buffer = std::unique_ptr<uint8_t[]>(
new uint8_t[decompressed_length]);
562 function(temp_buffer.get(), decompressed_length);
563 return ptr_in +
sizeof(
uint32_t) * 2 + section_length;
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();
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();
581 std::vector<char> tmp = client.send_buffer;
582 client.send_buffer.clear();
583 if(state.current_scene.starting_scene) {
585 if(!state.network_state.is_new_game) {
589 c.
source = state.local_player_nation;
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);
593 state.console_log(
"host:send:cmd: (new(2)->save_loaded)");
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;
607 state.console_log(
"host:send:cmd: (new(2)->self_join)");
609 }
else if(
n.get_is_player_controlled()) {
612 socket_add_to_send_queue(
client.send_buffer, &c,
sizeof(c));
614 state.console_log(
"host:send:cmd: (new(2)->others_join) " + std::to_string(
n.id.index()));
619 }
else if(
state.current_scene.game_in_progress) {
622 memset(&c, 0,
sizeof(c));
624 for(
const auto n :
state.world.in_nation) {
625 if(n ==
client.playing_as) {
631 state.console_log(
"host:send:cmd: (new->self_join)");
633 }
else if(
n.get_is_player_controlled()) {
636 socket_add_to_send_queue(
client.send_buffer, &c,
sizeof(c));
638 state.console_log(
"host:send:cmd: (new->others_join) " + std::to_string(
n.id.index()));
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))
649 dcon::nation_id old_local_player_nation =
state.local_player_nation;
650 state.local_player_nation = dcon::nation_id{ };
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);
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));
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));
672 state.console_log(
"host:send:cmd: (new->reload)");
685 state.console_log(
"host:send:cmd: (new->save_loaded)");
691 memset(&c, 0,
sizeof(c));
694 socket_add_to_send_queue(
client.send_buffer, &c,
sizeof(c));
696 state.console_log(
"host:send:cmd: (new->start_game)");
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());
705static void receive_from_clients(
sys::state& state) {
706 for(
auto& client :
state.network_state.clients) {
712 if(std::memcmp(client.hshake_buffer.password, state.network_state.password, sizeof(state.network_state.password)) != 0) {
713 disconnect_client(state, client, false);
716 send_post_handshake_commands(state, client);
719 state.game_state_updated.store(
true, std::memory_order::release);
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:
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);
746 state.console_log(
"host:recv:client_cmd: " + std::to_string(
uint32_t(
client.recv_buffer.type)));
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());
754 network::disconnect_client(state, client,
false);
763 size_t length = sizeof_save_section(state);
764 auto save_buffer = std::unique_ptr<uint8_t[]>(
new uint8_t[length]);
766 assert(state.local_player_nation == dcon::nation_id{ });
767 write_save_section(save_buffer.get(), state);
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();
779 for(
auto& client : state.network_state.clients) {
780 if(!client.is_active())
783 if(send_full && !state.network_state.is_new_game) {
785 client.save_stream_size = size_t(length);
787 socket_add_to_send_queue(client.send_buffer, &c,
sizeof(c));
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));
792 state.console_log(
"host:send:save: " + std::to_string(
uint32_t(length)));
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));
810static void accept_new_clients(
sys::state& state) {
814 FD_SET(state.network_state.socket_fd, &rfds);
818 if(
select(
socket_t(
int(
state.network_state.socket_fd) + 1), &rfds,
nullptr,
nullptr, &tv) <= 0)
822 for(
auto& client :
state.network_state.clients) {
825 socklen_t addr_len =
state.network_state.as_v6 ?
sizeof(sockaddr_in6) :
sizeof(sockaddr_in);
827 if(
client.is_banned(state)) {
828 disconnect_client(state, client,
false);
831 if(
state.current_scene.final_scene) {
832 disconnect_client(state, client,
false);
836 client.playing_as = get_temp_nation(state);
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));
847 state.console_log(
"host:send:cmd: handshake -> " + std::to_string(
client.playing_as.index()));
861 if(state.network_state.save_slock.load(std::memory_order::acquire) ==
true)
864 if(state.network_state.finished)
867 bool command_executed =
false;
869 accept_new_clients(state);
870 receive_from_clients(state);
872 auto* c = state.network_state.outgoing_commands.front();
877 if(state.current_date.to_ymd(state.start_date).day == 1 || state.cheat_data.daily_oos_check) {
883 command_executed =
true;
885 state.console_log(
"host:receive:cmd: " + std::to_string(
uint32_t(c->
type)));
888 state.network_state.outgoing_commands.pop();
889 c = state.network_state.outgoing_commands.front();
892 for(
auto& client : state.network_state.clients) {
893 if(!client.is_active())
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);
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());
903 disconnect_client(state, client,
false);
906 client.total_sent_bytes += old_size - client.early_send_buffer.size();
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");
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);
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());
920 disconnect_client(state, client,
false);
923 client.total_sent_bytes += old_size - client.send_buffer.size();
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");
932 if(state.network_state.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, [&]() {
936 state.console_log(
"client:recv:handshake: OK");
938 if(!state.scenario_checksum.is_equal(state.network_state.s_hshake.scenario_checksum)) {
939 bool found_match = false;
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);
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))
951 if(scen_header.version != sys::scenario_file_version)
953 if(sys::try_read_scenario_and_save_file(state, simple_fs::get_file_name(uf))) {
954 state.fill_unsaved_data();
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'!";
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";
971 window::emit_error_message(msg.c_str(), true);
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);
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;
986 state.map_state.set_selected_province(dcon::province_id{});
987 state.map_state.unhandled_province_selection =
true;
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, [&]() {
997 state.console_log(
"client:recv:save: len=" + std::to_string(uint32_t(state.network_state.save_data.size())));
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;
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);
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));
1015 auto save_checksum = state.get_save_checksum();
1016 assert(save_checksum.is_equal(state.session_host_checksum));
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;
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;
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) {
1039 ui::popup_error_window(state,
"Network Error",
"Network client save stream too big: " + get_last_error_msg());
1040 network::finish(state, false);
1043 state.network_state.save_data.resize(static_cast<size_t>(save_size));
1046 state.console_log(
"client:recv:cmd: " + std::to_string(
uint32_t(state.network_state.recv_buffer.type)));
1055 auto* c =
state.network_state.outgoing_commands.front();
1062 command_executed =
true;
1064 socket_add_to_send_queue(
state.network_state.send_buffer, c,
sizeof(*c));
1066 state.network_state.outgoing_commands.pop();
1067 c =
state.network_state.outgoing_commands.front();
1071 if(!
state.network_state.save_stream) {
1072 if(socket_send(
state.network_state.socket_fd,
state.network_state.send_buffer) != 0) {
1078 assert(
state.network_state.early_send_buffer.empty());
1081 if(command_executed) {
1082 if(
state.network_state.out_of_sync && !
state.network_state.reported_oos) {
1084 state.network_state.reported_oos =
true;
1086 state.game_state_updated.store(
true, std::memory_order::release);
1094 state.network_state.finished =
true;
1096 if(!state.network_state.save_stream) {
1099 auto* c = state.network_state.outgoing_commands.front();
1104 socket_add_to_send_queue(state.network_state.send_buffer, c,
sizeof(*c));
1106 state.network_state.outgoing_commands.pop();
1107 c = state.network_state.outgoing_commands.front();
1111 memset(&c, 0,
sizeof(c));
1113 c.
source = state.local_player_nation;
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) {
1125 socket_shutdown(state.network_state.socket_fd);
1132 if(!client.is_active())
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);
1140 auto sa = (
struct sockaddr_in*)&client.address;
1141 state.network_state.v4_banlist.push_back(sa->sin_addr);
1146 if(!client.is_active())
1148 socket_shutdown(client.socket_fd);
1149 client.socket_fd = 0;
1153 state.network_state.map_of_player_names.insert_or_assign(new_n.index(), state.network_state.map_of_player_names[old_n.index()]);
1155 for(
auto& client : state.network_state.clients) {
1156 if(!client.is_active())
1158 if(client.playing_as == old_n) {
1159 client.playing_as = new_n;
#define assert(condition)
bool is_console_command(command_type t)
bool can_notify_player_leaves(sys::state &state, dcon::nation_id source, bool make_ai)
void execute_command(sys::state &state, payload &c)
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)
void write_network_save(sys::state &state)
std::string get_last_error_msg()
void broadcast_to_clients(sys::state &state, command::payload &c)
void kick_player(sys::state &state, client_data &client)
constexpr short default_server_port
void finish(sys::state &state, bool notify_host)
void switch_player(sys::state &state, dcon::nation_id new_n, dcon::nation_id old_n)
void ban_player(sys::state &state, client_data &client)
void send_and_receive_commands(sys::state &state)
void init(sys::state &state)
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)
std::string native_string
#define ENABLE_AUTODISCOVERY
pcp_ctx_t * pcp_init(uint8_t autodiscovery, pcp_socket_vt_t *socket_vt)
pcp_fstate_e pcp_wait(pcp_flow_t *flow, int timeout, int exit_on_partial_res)
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)
void pcp_terminate(pcp_ctx_t *ctx, int close_flows)
sys::checksum_key checksum
sys::checksum_key checksum
sys::checksum_key checksum
union command::payload::dtype data
struct sockaddr_storage address
bool is_banned(sys::state &state) const
sys::player_name nickname
notify_reload_data notify_reload
advance_tick_data advance_tick
notify_save_loaded_data notify_save_loaded
notify_leaves_data notify_leave
sys::player_name player_name
size_t ZSTD_compressBound(size_t srcSize)
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)