mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-25 08:23:35 +08:00
network-reader tidying
This commit is contained in:
parent
816e9b993b
commit
6749e9ee5e
@ -3995,26 +3995,26 @@
|
||||
"assets/src/ba_data/python/ba/_generated/__init__.py": "https://files.ballistica.net/cache/ba1/ee/e8/cad05aa531c7faf7ff7b96db7f6e",
|
||||
"assets/src/ba_data/python/ba/_generated/enums.py": "https://files.ballistica.net/cache/ba1/b2/e5/0ee0561e16257a32830645239f34",
|
||||
"ballisticacore-windows/Generic/BallisticaCore.ico": "https://files.ballistica.net/cache/ba1/89/c0/e32c7d2a35dc9aef57cc73b0911a",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/b7/97/058ec9b0958eb0c16d4b73e46c77",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/fc/87/a43180a273071e2297727dae55e0",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ab/84/d648830f95c5adaa26e259267524",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a3/af/3c0f8a3884cd4896172790e49789",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/f1/53/567de5c38f219eea734d5d780cfe",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/0e/29/2b6acec2f043af8ffa28405518ba",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ec/e1/5b85a04e38545aa015c92819b1cc",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/62/6d/8440e0fccd5482c6cf06f5f8eb74",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/17/41/98b75e3c0dd041875081cb230c08",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/df/6e/8992acdcf955eec1df42f97c6559",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/cd/2e/a7985d25068b8c44bf64123131c2",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/be/f1/e13ed7948ffd0da65cbe0528ea94",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/1d/b6/58b8b43dc5071445e0a9d666487a",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/c6/fe/fa0c92655a72b7c4eadb3d12ba32",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e1/23/5572f9e4822e6798a0f1a249dd5a",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/42/4f/609cd8f582a6e7f26772816a60c7",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/db/f3/e9270a5d334a7f97f11cc151552a",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/ae/67/9c59c7e046c568adc89819afc445",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/60/9a/8e4789f6f0b4f394a00043ab8ec7",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/25/c6/2d9856f3b32b71c3b222786edadb",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/79/79/0a70bdd5f157f35fcf756a187db5",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/d1/d8/96e209445da48374062ed2b2aa7e",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/1e/aa/26758a2a412fd07628f1c1435c3f",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/9f/98/acfca6199ed45238036a1f2f14f8",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/3d/fa/64690be1890135b82a302c6fce22",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/33/ae/00c76bb8d2048e9c110f698b8a20",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/8a/91/3e25e98763c1a80bc2d3b49b6c24",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b7/60/2ad75fe1d4c776e220ab49a985a3",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/ab/82/bf728df59dbcde87f8514d26bf19",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/4f/fa/e5a5ba4ea3dff37c05e93ac99f30",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/df/36/174625b380539106b67d5bf67373",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/20/fe/077a7249888f313bc1251c85001b",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/f9/eb/5e6c3396ea7ea88aa35eea3313ff",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/63/cc/7647e6afc47bc16cab506bfbc32f",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/24/84/2f5eeb0c76bb594a60f9d1cbad96",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/1f/fb/a7f13102bc81737fa5a624ddb96e",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/3f/a5/dca6a30b4a2e0bdeab62f746d16f",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/58/fb/6a1b9965d43820d72449f1771b43",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/4a/dd/3e69ddf532e414418ef2b1bed182",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/8b/03/f80c40e0efbbdf73d372eb78c7b2",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/6a/91/ddafd190b867e23110b18ddce9ab",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/07/5f/246e5ef83e81f516aa82033470c5",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/18/04/ed17d0ac8813451038b4831dd3c1",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
### 1.7.7 (build 20702, api 7, 2022-08-23)
|
||||
### 1.7.7 (build 20704, api 7, 2022-08-23)
|
||||
- Added `ba.app.meta.load_exported_classes()` for loading classes discovered by the meta subsystem cleanly in a background thread.
|
||||
- Improved logging of missing playlist game types.
|
||||
- Some ba.Lstr functionality can now be used in background threads.
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
namespace ballistica {
|
||||
|
||||
// These are set automatically via script; don't modify them here.
|
||||
const int kAppBuildNumber = 20702;
|
||||
const int kAppBuildNumber = 20704;
|
||||
const char* kAppVersion = "1.7.7";
|
||||
|
||||
// Our standalone globals.
|
||||
|
||||
@ -453,9 +453,9 @@ auto ConnectionSet::UDPConnectionPacket(const std::vector<uint8_t>& data_in,
|
||||
// Client is telling us (host) that it wants to disconnect.
|
||||
|
||||
uint8_t client_id = data[1];
|
||||
if (!VerifyClientAddr(client_id, addr)) {
|
||||
break;
|
||||
}
|
||||
// if (!VerifyClientAddr(client_id, addr)) {
|
||||
// break;
|
||||
// }
|
||||
|
||||
// Wipe that client out (if it still exists).
|
||||
PushClientDisconnectedCall(client_id);
|
||||
|
||||
@ -84,6 +84,88 @@ void NetworkReader::PokeSelf() {
|
||||
}
|
||||
}
|
||||
|
||||
static auto HandleJSONPing(const std::string& data_str) -> std::string {
|
||||
cJSON* data = cJSON_Parse(data_str.c_str());
|
||||
if (data == nullptr) {
|
||||
return "";
|
||||
}
|
||||
cJSON_Delete(data);
|
||||
|
||||
// Ok lets include some basic info that might be pertinent to someone pinging
|
||||
// us. Currently that includes our current/max connection count.
|
||||
char buffer[256];
|
||||
int party_size = 0;
|
||||
int party_size_max = 10;
|
||||
if (g_python != nullptr) {
|
||||
party_size = g_game->public_party_size();
|
||||
party_size_max = g_game->public_party_max_size();
|
||||
}
|
||||
snprintf(buffer, sizeof(buffer), R"({"b":%d,"ps":%d,"psmx":%d})",
|
||||
kAppBuildNumber, party_size, party_size_max);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static auto HandleGameQuery(const char* buffer, size_t size,
|
||||
struct sockaddr_storage* from) -> void {
|
||||
if (size == 5) {
|
||||
// If we're already in a party, don't advertise since they
|
||||
// wouldn't be able to join us anyway.
|
||||
if (g_game->connections()->has_connection_to_host()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Pull the query id from the packet.
|
||||
uint32_t query_id;
|
||||
memcpy(&query_id, buffer + 1, 4);
|
||||
|
||||
// Ship them a response packet containing the query id,
|
||||
// our protocol version, our unique-session-id, and our
|
||||
// player_spec.
|
||||
char msg[400];
|
||||
|
||||
std::string usid = GetAppInstanceUUID();
|
||||
std::string player_spec_string;
|
||||
|
||||
// If we're signed in, send our account spec.
|
||||
// Otherwise just send a dummy made with our device name.
|
||||
player_spec_string = PlayerSpec::GetAccountPlayerSpec().GetSpecString();
|
||||
|
||||
// This should always be the case (len needs to be 1 byte)
|
||||
BA_PRECONDITION_FATAL(player_spec_string.size() < 256);
|
||||
|
||||
BA_PRECONDITION_FATAL(!usid.empty());
|
||||
if (usid.size() > 100) {
|
||||
Log("had to truncate session-id; shouldn't happen");
|
||||
usid.resize(100);
|
||||
}
|
||||
if (usid.empty()) {
|
||||
usid = "error";
|
||||
}
|
||||
|
||||
msg[0] = BA_PACKET_GAME_QUERY_RESPONSE;
|
||||
memcpy(msg + 1, &query_id, 4);
|
||||
uint32_t protocol_version = kProtocolVersion;
|
||||
memcpy(msg + 5, &protocol_version, 4);
|
||||
msg[9] = static_cast<char>(usid.size());
|
||||
msg[10] = static_cast<char>(player_spec_string.size());
|
||||
|
||||
memcpy(msg + 11, usid.c_str(), usid.size());
|
||||
memcpy(msg + 11 + usid.size(), player_spec_string.c_str(),
|
||||
player_spec_string.size());
|
||||
size_t msg_len = 11 + player_spec_string.size() + usid.size();
|
||||
BA_PRECONDITION_FATAL(msg_len <= sizeof(msg));
|
||||
|
||||
std::vector<uint8_t> msg_buffer(msg_len);
|
||||
memcpy(msg_buffer.data(), msg, msg_len);
|
||||
|
||||
g_network_write_module->PushSendToCall(msg_buffer, SockAddr(*from));
|
||||
|
||||
} else {
|
||||
Log("Error: Got invalid game-query packet of len " + std::to_string(size)
|
||||
+ "; expected 5.");
|
||||
}
|
||||
}
|
||||
|
||||
auto NetworkReader::RunThread() -> int {
|
||||
if (!HeadlessMode()) {
|
||||
remote_server_ = std::make_unique<RemoteAppServer>();
|
||||
@ -96,139 +178,11 @@ auto NetworkReader::RunThread() -> int {
|
||||
std::unique_lock<std::mutex> lock(paused_mutex_);
|
||||
paused_cv_.wait(lock, [this] { return (!paused_); });
|
||||
}
|
||||
{
|
||||
// This needs to be locked during any socket-descriptor changes/writes.
|
||||
std::lock_guard<std::mutex> lock(sd_mutex_);
|
||||
|
||||
int result;
|
||||
int print_port_unavailable = false;
|
||||
int initial_requested_port = port4_;
|
||||
|
||||
sd4_ = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sd4_ < 0) {
|
||||
Log("ERROR: Unable to open host socket; errno "
|
||||
+ g_platform->GetSocketErrorString());
|
||||
} else {
|
||||
g_platform->SetSocketNonBlocking(sd4_);
|
||||
|
||||
// Bind to local server port.
|
||||
struct sockaddr_in serv_addr {};
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // NOLINT
|
||||
|
||||
// Try our requested port for v4, then go with any available if that
|
||||
// doesn't work.
|
||||
serv_addr.sin_port = htons(port4_); // NOLINT
|
||||
result = ::bind(sd4_, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
|
||||
if (result != 0) {
|
||||
// If we're headless then we abort here; we're useless if we don't get
|
||||
// the port we wanted.
|
||||
if (HeadlessMode()) {
|
||||
Log("FATAL ERROR: unable to bind to requested udp port "
|
||||
+ std::to_string(port4_) + " (ipv4)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Primary ipv4 bind failed; try on any port as a backup.
|
||||
print_port_unavailable = true;
|
||||
serv_addr.sin_port = htons(0); // NOLINT
|
||||
result =
|
||||
::bind(sd4_, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
|
||||
|
||||
// Wuh oh; no ipv6 for us i guess.
|
||||
if (result != 0) {
|
||||
g_platform->CloseSocket(sd4_);
|
||||
sd4_ = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See what v4 port we actually wound up with.
|
||||
if (sd4_ != -1) {
|
||||
struct sockaddr_in sa {};
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
if (getsockname(sd4_, reinterpret_cast<sockaddr*>(&sa), &sa_len) == 0) {
|
||||
port4_ = ntohs(sa.sin_port); // NOLINT
|
||||
|
||||
// Aim for a v6 port to match whatever we wound up with on the v4
|
||||
// side.
|
||||
port6_ = port4_;
|
||||
}
|
||||
}
|
||||
|
||||
// Ok now lets try to create an ipv6 socket on the same port.
|
||||
// (its actually possible to just create a v6 socket and let the OSs
|
||||
// dual-stack support provide v4 connectivity too, but that's not
|
||||
// available everywhere (win XP, etc) so let's do this for now.
|
||||
sd6_ = socket(AF_INET6, SOCK_DGRAM, 0);
|
||||
if (sd6_ < 0) {
|
||||
Log("ERROR: Unable to open ipv6 socket: "
|
||||
+ g_platform->GetSocketErrorString());
|
||||
} else {
|
||||
// Since we're explicitly creating both a v4 and v6 socket, tell the v6
|
||||
// to *not* do both itself (not sure if this is necessary; on mac it
|
||||
// still seems to come up.. but apparently that's not always the case).
|
||||
int on = 1;
|
||||
if (setsockopt(sd6_, IPPROTO_IPV6, IPV6_V6ONLY,
|
||||
reinterpret_cast<char*>(&on), sizeof(on))
|
||||
== -1) {
|
||||
Log("error setting socket as ipv6-only");
|
||||
}
|
||||
|
||||
g_platform->SetSocketNonBlocking(sd6_);
|
||||
struct sockaddr_in6 serv_addr {};
|
||||
memset(&serv_addr, 0, sizeof(serv_addr));
|
||||
serv_addr.sin6_family = AF_INET6;
|
||||
serv_addr.sin6_port = htons(port6_); // NOLINT
|
||||
serv_addr.sin6_addr = in6addr_any;
|
||||
result = ::bind(sd6_, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
|
||||
|
||||
if (result != 0) {
|
||||
if (HeadlessMode()) {
|
||||
Log("FATAL ERROR: unable to bind to requested udp port "
|
||||
+ std::to_string(port6_) + " (ipv6)");
|
||||
exit(1);
|
||||
}
|
||||
// Primary ipv6 bind failed; try backup.
|
||||
|
||||
// We don't care if our random backup ports don't match; only if our
|
||||
// target port failed.
|
||||
if (port6_ == initial_requested_port) {
|
||||
print_port_unavailable = true;
|
||||
}
|
||||
serv_addr.sin6_port = htons(0); // NOLINT
|
||||
result =
|
||||
::bind(sd6_, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
|
||||
if (result != 0) {
|
||||
// Wuh oh; no ipv6 for us i guess.
|
||||
g_platform->CloseSocket(sd6_);
|
||||
sd6_ = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See what v6 port we actually wound up with.
|
||||
if (sd6_ != -1) {
|
||||
struct sockaddr_in sa {};
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
if (getsockname(sd6_, reinterpret_cast<sockaddr*>(&sa), &sa_len) == 0) {
|
||||
port6_ = ntohs(sa.sin_port); // NOLINT
|
||||
}
|
||||
}
|
||||
if (print_port_unavailable) {
|
||||
// FIXME - use translations here
|
||||
ScreenMessage("Unable to bind udp port "
|
||||
+ std::to_string(initial_requested_port)
|
||||
+ "; some network functionality may fail.",
|
||||
{1, 0.5f, 0});
|
||||
Log("Unable to bind udp port " + std::to_string(initial_requested_port)
|
||||
+ "; some network functionality may fail.",
|
||||
true, false);
|
||||
}
|
||||
}
|
||||
char buffer[10000];
|
||||
OpenSockets();
|
||||
|
||||
// Now just listen and forward messages along.
|
||||
char buffer[10000];
|
||||
while (true) {
|
||||
struct sockaddr_storage from {};
|
||||
socklen_t from_size = sizeof(from);
|
||||
@ -272,207 +226,140 @@ auto NetworkReader::RunThread() -> int {
|
||||
Log("Error on select: " + g_platform->GetSocketErrorString());
|
||||
}
|
||||
} else {
|
||||
int* fds[2] = {&sd4_, &sd6_};
|
||||
|
||||
// Wait for any data on either of our sockets.
|
||||
for (auto& fd : fds) {
|
||||
int& sd(*fd);
|
||||
if (sd != -1) {
|
||||
if (FD_ISSET(sd, &readset)) {
|
||||
ssize_t rresult =
|
||||
recvfrom(sd, buffer, sizeof(buffer), 0,
|
||||
reinterpret_cast<sockaddr*>(&from), &from_size);
|
||||
if (rresult == 0) {
|
||||
Log("ERROR: NetworkReader Recv got length 0; this shouldn't "
|
||||
"happen");
|
||||
} else if (rresult == -1) {
|
||||
for (int sd : {sd4_, sd6_}) {
|
||||
if (sd == -1 || !(FD_ISSET(sd, &readset))) {
|
||||
continue;
|
||||
}
|
||||
ssize_t rresult =
|
||||
recvfrom(sd, buffer, sizeof(buffer), 0,
|
||||
reinterpret_cast<sockaddr*>(&from), &from_size);
|
||||
if (rresult == 0) {
|
||||
Log("ERROR: NetworkReader Recv got length 0; this shouldn't "
|
||||
"happen");
|
||||
} else if (rresult == -1) {
|
||||
// This needs to be locked during any sd changes/writes.
|
||||
std::lock_guard<std::mutex> lock(sd_mutex_);
|
||||
|
||||
// If either of our sockets goes down lets close *both* of
|
||||
// them.
|
||||
if (sd4_ != -1) {
|
||||
g_platform->CloseSocket(sd4_);
|
||||
sd4_ = -1;
|
||||
}
|
||||
if (sd6_ != -1) {
|
||||
g_platform->CloseSocket(sd6_);
|
||||
sd6_ = -1;
|
||||
}
|
||||
} else {
|
||||
assert(from_size >= 0);
|
||||
auto rresult2{static_cast<size_t>(rresult)};
|
||||
// If we get *any* data while paused, kill both our
|
||||
// sockets (we ping ourself for this purpose).
|
||||
if (paused_) {
|
||||
// This needs to be locked during any sd changes/writes.
|
||||
std::lock_guard<std::mutex> lock(sd_mutex_);
|
||||
if (sd4_ != -1) {
|
||||
g_platform->CloseSocket(sd4_);
|
||||
sd4_ = -1;
|
||||
}
|
||||
if (sd6_ != -1) {
|
||||
g_platform->CloseSocket(sd6_);
|
||||
sd6_ = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch (buffer[0]) {
|
||||
case BA_PACKET_POKE:
|
||||
break;
|
||||
case BA_PACKET_SIMPLE_PING: {
|
||||
// This needs to be locked during any sd changes/writes.
|
||||
std::lock_guard<std::mutex> lock(sd_mutex_);
|
||||
|
||||
// If either of our sockets goes down lets close *both* of
|
||||
// them.
|
||||
if (sd4_ != -1) {
|
||||
g_platform->CloseSocket(sd4_);
|
||||
sd4_ = -1;
|
||||
}
|
||||
if (sd6_ != -1) {
|
||||
g_platform->CloseSocket(sd6_);
|
||||
sd6_ = -1;
|
||||
}
|
||||
} else {
|
||||
assert(from_size >= 0);
|
||||
auto rresult2 = static_cast<size_t>(rresult);
|
||||
// If we get *any* data while paused, kill both our
|
||||
// sockets (we ping ourself for this purpose).
|
||||
if (paused_) {
|
||||
// This needs to be locked during any sd changes/writes.
|
||||
std::lock_guard<std::mutex> lock(sd_mutex_);
|
||||
if (sd4_ != -1) {
|
||||
g_platform->CloseSocket(sd4_);
|
||||
sd4_ = -1;
|
||||
}
|
||||
if (sd6_ != -1) {
|
||||
g_platform->CloseSocket(sd6_);
|
||||
sd6_ = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch (buffer[0]) {
|
||||
case BA_PACKET_POKE:
|
||||
break;
|
||||
case BA_PACKET_SIMPLE_PING: {
|
||||
// This needs to be locked during any sd changes/writes.
|
||||
std::lock_guard<std::mutex> lock(sd_mutex_);
|
||||
char msg[1] = {BA_PACKET_SIMPLE_PONG};
|
||||
sendto(sd, msg, 1, 0, reinterpret_cast<sockaddr*>(&from),
|
||||
from_size);
|
||||
break;
|
||||
}
|
||||
case BA_PACKET_JSON_PING: {
|
||||
if (rresult2 > 1) {
|
||||
std::vector<char> s_buffer(rresult2);
|
||||
memcpy(s_buffer.data(), buffer + 1, rresult2 - 1);
|
||||
s_buffer[rresult2 - 1] = 0; // terminate string
|
||||
std::string response = HandleJSONPing(s_buffer.data());
|
||||
if (!response.empty()) {
|
||||
std::vector<char> msg(1 + response.size());
|
||||
msg[0] = BA_PACKET_JSON_PONG;
|
||||
memcpy(msg.data() + 1, response.c_str(),
|
||||
response.size());
|
||||
std::lock_guard<std::mutex> lock(sd_mutex_);
|
||||
sendto(sd, msg.data(),
|
||||
static_cast_check_fit<socket_send_length_t>(
|
||||
msg.size()),
|
||||
0, reinterpret_cast<sockaddr*>(&from),
|
||||
from_size);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BA_PACKET_JSON_PONG: {
|
||||
if (rresult2 > 1) {
|
||||
std::vector<char> s_buffer(rresult2);
|
||||
memcpy(s_buffer.data(), buffer + 1, rresult2 - 1);
|
||||
s_buffer[rresult2 - 1] = 0; // terminate string
|
||||
cJSON* data = cJSON_Parse(s_buffer.data());
|
||||
if (data != nullptr) {
|
||||
cJSON_Delete(data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BA_PACKET_REMOTE_PING:
|
||||
case BA_PACKET_REMOTE_PONG:
|
||||
case BA_PACKET_REMOTE_ID_REQUEST:
|
||||
case BA_PACKET_REMOTE_ID_RESPONSE:
|
||||
case BA_PACKET_REMOTE_DISCONNECT:
|
||||
case BA_PACKET_REMOTE_STATE:
|
||||
case BA_PACKET_REMOTE_STATE2:
|
||||
case BA_PACKET_REMOTE_STATE_ACK:
|
||||
case BA_PACKET_REMOTE_DISCONNECT_ACK:
|
||||
case BA_PACKET_REMOTE_GAME_QUERY:
|
||||
case BA_PACKET_REMOTE_GAME_RESPONSE:
|
||||
// These packets are associated with the remote app; let the
|
||||
// remote server handle them.
|
||||
if (remote_server_) {
|
||||
remote_server_->HandleData(
|
||||
sd, reinterpret_cast<uint8_t*>(buffer), rresult2,
|
||||
reinterpret_cast<sockaddr*>(&from),
|
||||
static_cast<size_t>(from_size));
|
||||
}
|
||||
break;
|
||||
|
||||
case BA_PACKET_CLIENT_REQUEST:
|
||||
case BA_PACKET_CLIENT_ACCEPT:
|
||||
case BA_PACKET_CLIENT_DENY:
|
||||
case BA_PACKET_CLIENT_DENY_ALREADY_IN_PARTY:
|
||||
case BA_PACKET_CLIENT_DENY_VERSION_MISMATCH:
|
||||
case BA_PACKET_CLIENT_DENY_PARTY_FULL:
|
||||
case BA_PACKET_DISCONNECT_FROM_CLIENT_REQUEST:
|
||||
case BA_PACKET_DISCONNECT_FROM_CLIENT_ACK:
|
||||
case BA_PACKET_DISCONNECT_FROM_HOST_REQUEST:
|
||||
case BA_PACKET_DISCONNECT_FROM_HOST_ACK:
|
||||
case BA_PACKET_CLIENT_GAMEPACKET_COMPRESSED:
|
||||
case BA_PACKET_HOST_GAMEPACKET_COMPRESSED: {
|
||||
// These messages are associated with udp host/client
|
||||
// connections.. pass them to the game thread to wrangle.
|
||||
std::vector<uint8_t> msg_buffer(rresult2);
|
||||
memcpy(&(msg_buffer[0]), buffer, rresult2);
|
||||
g_game->connections()->PushUDPConnectionPacketCall(
|
||||
msg_buffer, SockAddr(from));
|
||||
break;
|
||||
}
|
||||
|
||||
case BA_PACKET_GAME_QUERY: {
|
||||
if (rresult2 == 5) {
|
||||
// If we're already in a party, don't advertise since they
|
||||
// wouldn't be able to join us anyway.
|
||||
if (g_game->connections()->has_connection_to_host()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Pull the query id from the packet.
|
||||
uint32_t query_id;
|
||||
memcpy(&query_id, buffer + 1, 4);
|
||||
|
||||
// Ship them a response packet containing the query id,
|
||||
// our protocol version, our unique-session-id, and our
|
||||
// player_spec.
|
||||
char msg[400];
|
||||
|
||||
std::string usid = GetAppInstanceUUID();
|
||||
std::string player_spec_string;
|
||||
|
||||
// If we're signed in, send our account spec.
|
||||
// Otherwise just send a dummy made with our device name.
|
||||
player_spec_string =
|
||||
PlayerSpec::GetAccountPlayerSpec().GetSpecString();
|
||||
|
||||
// This should always be the case (len needs to be 1 byte)
|
||||
BA_PRECONDITION_FATAL(player_spec_string.size() < 256);
|
||||
|
||||
BA_PRECONDITION_FATAL(!usid.empty());
|
||||
if (usid.size() > 100) {
|
||||
Log("had to truncate session-id; shouldn't happen");
|
||||
usid.resize(100);
|
||||
}
|
||||
if (usid.empty()) {
|
||||
usid = "error";
|
||||
}
|
||||
|
||||
msg[0] = BA_PACKET_GAME_QUERY_RESPONSE;
|
||||
memcpy(msg + 1, &query_id, 4);
|
||||
uint32_t protocol_version = kProtocolVersion;
|
||||
memcpy(msg + 5, &protocol_version, 4);
|
||||
msg[9] = static_cast<char>(usid.size());
|
||||
msg[10] = static_cast<char>(player_spec_string.size());
|
||||
|
||||
memcpy(msg + 11, usid.c_str(), usid.size());
|
||||
memcpy(msg + 11 + usid.size(), player_spec_string.c_str(),
|
||||
player_spec_string.size());
|
||||
size_t msg_len =
|
||||
11 + player_spec_string.size() + usid.size();
|
||||
BA_PRECONDITION_FATAL(msg_len <= sizeof(msg));
|
||||
|
||||
std::vector<uint8_t> msg_buffer(msg_len);
|
||||
memcpy(msg_buffer.data(), msg, msg_len);
|
||||
|
||||
g_network_write_module->PushSendToCall(msg_buffer,
|
||||
SockAddr(from));
|
||||
break;
|
||||
|
||||
} else {
|
||||
Log("Error: Got invalid game-query packet of len "
|
||||
+ std::to_string(rresult2) + "; expected 5.");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
char msg[1] = {BA_PACKET_SIMPLE_PONG};
|
||||
sendto(sd, msg, 1, 0, reinterpret_cast<sockaddr*>(&from),
|
||||
from_size);
|
||||
break;
|
||||
}
|
||||
case BA_PACKET_JSON_PING: {
|
||||
if (rresult2 > 1) {
|
||||
std::vector<char> s_buffer(rresult2);
|
||||
memcpy(s_buffer.data(), buffer + 1, rresult2 - 1);
|
||||
s_buffer[rresult2 - 1] = 0; // terminate string
|
||||
std::string response = HandleJSONPing(s_buffer.data());
|
||||
if (!response.empty()) {
|
||||
std::vector<char> msg(1 + response.size());
|
||||
msg[0] = BA_PACKET_JSON_PONG;
|
||||
memcpy(msg.data() + 1, response.c_str(), response.size());
|
||||
std::lock_guard<std::mutex> lock(sd_mutex_);
|
||||
sendto(
|
||||
sd, msg.data(),
|
||||
static_cast_check_fit<socket_send_length_t>(msg.size()),
|
||||
0, reinterpret_cast<sockaddr*>(&from), from_size);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BA_PACKET_JSON_PONG: {
|
||||
if (rresult2 > 1) {
|
||||
std::vector<char> s_buffer(rresult2);
|
||||
memcpy(s_buffer.data(), buffer + 1, rresult2 - 1);
|
||||
s_buffer[rresult2 - 1] = 0; // terminate string
|
||||
cJSON* data = cJSON_Parse(s_buffer.data());
|
||||
if (data != nullptr) {
|
||||
cJSON_Delete(data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BA_PACKET_REMOTE_PING:
|
||||
case BA_PACKET_REMOTE_PONG:
|
||||
case BA_PACKET_REMOTE_ID_REQUEST:
|
||||
case BA_PACKET_REMOTE_ID_RESPONSE:
|
||||
case BA_PACKET_REMOTE_DISCONNECT:
|
||||
case BA_PACKET_REMOTE_STATE:
|
||||
case BA_PACKET_REMOTE_STATE2:
|
||||
case BA_PACKET_REMOTE_STATE_ACK:
|
||||
case BA_PACKET_REMOTE_DISCONNECT_ACK:
|
||||
case BA_PACKET_REMOTE_GAME_QUERY:
|
||||
case BA_PACKET_REMOTE_GAME_RESPONSE:
|
||||
// These packets are associated with the remote app; let the
|
||||
// remote server handle them.
|
||||
if (remote_server_) {
|
||||
remote_server_->HandleData(
|
||||
sd, reinterpret_cast<uint8_t*>(buffer), rresult2,
|
||||
reinterpret_cast<sockaddr*>(&from),
|
||||
static_cast<size_t>(from_size));
|
||||
}
|
||||
break;
|
||||
|
||||
case BA_PACKET_CLIENT_REQUEST:
|
||||
case BA_PACKET_CLIENT_ACCEPT:
|
||||
case BA_PACKET_CLIENT_DENY:
|
||||
case BA_PACKET_CLIENT_DENY_ALREADY_IN_PARTY:
|
||||
case BA_PACKET_CLIENT_DENY_VERSION_MISMATCH:
|
||||
case BA_PACKET_CLIENT_DENY_PARTY_FULL:
|
||||
case BA_PACKET_DISCONNECT_FROM_CLIENT_REQUEST:
|
||||
case BA_PACKET_DISCONNECT_FROM_CLIENT_ACK:
|
||||
case BA_PACKET_DISCONNECT_FROM_HOST_REQUEST:
|
||||
case BA_PACKET_DISCONNECT_FROM_HOST_ACK:
|
||||
case BA_PACKET_CLIENT_GAMEPACKET_COMPRESSED:
|
||||
case BA_PACKET_HOST_GAMEPACKET_COMPRESSED: {
|
||||
// These messages are associated with udp host/client
|
||||
// connections.. pass them to the game thread to wrangle.
|
||||
std::vector<uint8_t> msg_buffer(rresult2);
|
||||
memcpy(&(msg_buffer[0]), buffer, rresult2);
|
||||
g_game->connections()->PushUDPConnectionPacketCall(
|
||||
msg_buffer, SockAddr(from));
|
||||
break;
|
||||
}
|
||||
|
||||
case BA_PACKET_GAME_QUERY: {
|
||||
HandleGameQuery(buffer, rresult2, &from);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -489,27 +376,135 @@ auto NetworkReader::RunThread() -> int {
|
||||
}
|
||||
}
|
||||
|
||||
NetworkReader::~NetworkReader() = default;
|
||||
auto NetworkReader::OpenSockets() -> void {
|
||||
// This needs to be locked during any socket-descriptor changes/writes.
|
||||
std::lock_guard<std::mutex> lock(sd_mutex_);
|
||||
|
||||
auto NetworkReader::HandleJSONPing(const std::string& data_str) -> std::string {
|
||||
cJSON* data = cJSON_Parse(data_str.c_str());
|
||||
if (data == nullptr) {
|
||||
return "";
|
||||
}
|
||||
cJSON_Delete(data);
|
||||
int result;
|
||||
int print_port_unavailable = false;
|
||||
int initial_requested_port = port4_;
|
||||
|
||||
// Ok lets include some basic info that might be pertinent to someone pinging
|
||||
// us. Currently that includes our current/max connection count.
|
||||
char buffer[256];
|
||||
int party_size = 0;
|
||||
int party_size_max = 10;
|
||||
if (g_python != nullptr) {
|
||||
party_size = g_game->public_party_size();
|
||||
party_size_max = g_game->public_party_max_size();
|
||||
sd4_ = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sd4_ < 0) {
|
||||
Log("ERROR: Unable to open host socket; errno "
|
||||
+ g_platform->GetSocketErrorString());
|
||||
} else {
|
||||
g_platform->SetSocketNonBlocking(sd4_);
|
||||
|
||||
// Bind to local server port.
|
||||
struct sockaddr_in serv_addr {};
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // NOLINT
|
||||
|
||||
// Try our requested port for v4, then go with any available if that
|
||||
// doesn't work.
|
||||
serv_addr.sin_port = htons(port4_); // NOLINT
|
||||
result = ::bind(sd4_, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
|
||||
if (result != 0) {
|
||||
// If we're headless then we abort here; we're useless if we don't get
|
||||
// the port we wanted.
|
||||
if (HeadlessMode()) {
|
||||
Log("FATAL ERROR: unable to bind to requested udp port "
|
||||
+ std::to_string(port4_) + " (ipv4)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Primary ipv4 bind failed; try on any port as a backup.
|
||||
print_port_unavailable = true;
|
||||
serv_addr.sin_port = htons(0); // NOLINT
|
||||
result = ::bind(sd4_, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
|
||||
|
||||
// Wuh oh; no ipv6 for us i guess.
|
||||
if (result != 0) {
|
||||
g_platform->CloseSocket(sd4_);
|
||||
sd4_ = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See what v4 port we actually wound up with.
|
||||
if (sd4_ != -1) {
|
||||
struct sockaddr_in sa {};
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
if (getsockname(sd4_, reinterpret_cast<sockaddr*>(&sa), &sa_len) == 0) {
|
||||
port4_ = ntohs(sa.sin_port); // NOLINT
|
||||
|
||||
// Aim for a v6 port to match whatever we wound up with on the v4
|
||||
// side.
|
||||
port6_ = port4_;
|
||||
}
|
||||
}
|
||||
|
||||
// Ok now lets try to create an ipv6 socket on the same port.
|
||||
// (its actually possible to just create a v6 socket and let the OSs
|
||||
// dual-stack support provide v4 connectivity too, but that's not
|
||||
// available everywhere (win XP, etc) so let's do this for now.
|
||||
sd6_ = socket(AF_INET6, SOCK_DGRAM, 0);
|
||||
if (sd6_ < 0) {
|
||||
Log("ERROR: Unable to open ipv6 socket: "
|
||||
+ g_platform->GetSocketErrorString());
|
||||
} else {
|
||||
// Since we're explicitly creating both a v4 and v6 socket, tell the v6
|
||||
// to *not* do both itself (not sure if this is necessary; on mac it
|
||||
// still seems to come up.. but apparently that's not always the case).
|
||||
int on = 1;
|
||||
if (setsockopt(sd6_, IPPROTO_IPV6, IPV6_V6ONLY,
|
||||
reinterpret_cast<char*>(&on), sizeof(on))
|
||||
== -1) {
|
||||
Log("error setting socket as ipv6-only");
|
||||
}
|
||||
|
||||
g_platform->SetSocketNonBlocking(sd6_);
|
||||
struct sockaddr_in6 serv_addr {};
|
||||
memset(&serv_addr, 0, sizeof(serv_addr));
|
||||
serv_addr.sin6_family = AF_INET6;
|
||||
serv_addr.sin6_port = htons(port6_); // NOLINT
|
||||
serv_addr.sin6_addr = in6addr_any;
|
||||
result = ::bind(sd6_, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
|
||||
|
||||
if (result != 0) {
|
||||
if (HeadlessMode()) {
|
||||
Log("FATAL ERROR: unable to bind to requested udp port "
|
||||
+ std::to_string(port6_) + " (ipv6)");
|
||||
exit(1);
|
||||
}
|
||||
// Primary ipv6 bind failed; try backup.
|
||||
|
||||
// We don't care if our random backup ports don't match; only if our
|
||||
// target port failed.
|
||||
if (port6_ == initial_requested_port) {
|
||||
print_port_unavailable = true;
|
||||
}
|
||||
serv_addr.sin6_port = htons(0); // NOLINT
|
||||
result = ::bind(sd6_, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
|
||||
if (result != 0) {
|
||||
// Wuh oh; no ipv6 for us i guess.
|
||||
g_platform->CloseSocket(sd6_);
|
||||
sd6_ = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See what v6 port we actually wound up with.
|
||||
if (sd6_ != -1) {
|
||||
struct sockaddr_in sa {};
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
if (getsockname(sd6_, reinterpret_cast<sockaddr*>(&sa), &sa_len) == 0) {
|
||||
port6_ = ntohs(sa.sin_port); // NOLINT
|
||||
}
|
||||
}
|
||||
if (print_port_unavailable) {
|
||||
// FIXME - use translations here
|
||||
ScreenMessage("Unable to bind udp port "
|
||||
+ std::to_string(initial_requested_port)
|
||||
+ "; some network functionality may fail.",
|
||||
{1, 0.5f, 0});
|
||||
Log("Unable to bind udp port " + std::to_string(initial_requested_port)
|
||||
+ "; some network functionality may fail.",
|
||||
true, false);
|
||||
}
|
||||
snprintf(buffer, sizeof(buffer), R"({"b":%d,"ps":%d,"psmx":%d})",
|
||||
kAppBuildNumber, party_size, party_size_max);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
NetworkReader::~NetworkReader() = default;
|
||||
|
||||
} // namespace ballistica
|
||||
|
||||
@ -32,7 +32,7 @@ class NetworkReader {
|
||||
auto sd6() const { return sd6_; }
|
||||
|
||||
private:
|
||||
auto HandleJSONPing(const std::string& data) -> std::string;
|
||||
auto OpenSockets() -> void;
|
||||
auto PokeSelf() -> void;
|
||||
auto RunThread() -> int;
|
||||
static auto RunThreadStatic(void* self) -> int {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user