netcode-revival: pack the relay wire structs + heartbeat + transport logging
- #pragma pack(push,1) on the wire structs (the relay's proto is packed; the transport's were not, which would inflate sizes and corrupt every datagram) plus static_asserts pinning sizes to 7/31/1410/25/801. - Heartbeat re-JOIN every 4s so the relay never times the transport out. - REXLOG info for connect/join/session-reply so the flow is observable.
This commit is contained in:
parent
bf3635d7ad
commit
01fe706c13
3 changed files with 43 additions and 1 deletions
|
|
@ -32,6 +32,10 @@ namespace system {
|
||||||
|
|
||||||
class XSocket;
|
class XSocket;
|
||||||
|
|
||||||
|
// The wire structs below must match the relay's byte-for-byte (the main repo
|
||||||
|
// packs them with #pragma pack(1); leaving padding here would inflate the
|
||||||
|
// sizes and silently corrupt every datagram).
|
||||||
|
#pragma pack(push, 1)
|
||||||
// Header of every relay datagram ("GHST" magic, little-endian type + size).
|
// Header of every relay datagram ("GHST" magic, little-endian type + size).
|
||||||
// Must match kGhostMagic / PacketHeader in skate3_native_ghost_proto.h.
|
// Must match kGhostMagic / PacketHeader in skate3_native_ghost_proto.h.
|
||||||
struct XNetRelayHeader {
|
struct XNetRelayHeader {
|
||||||
|
|
@ -69,6 +73,7 @@ struct XNetRelaySessionReply {
|
||||||
uint8_t count;
|
uint8_t count;
|
||||||
XNetRelaySessionEntry list[32];
|
XNetRelaySessionEntry list[32];
|
||||||
};
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
// One discovered session (a member of our room).
|
// One discovered session (a member of our room).
|
||||||
struct XNetSession {
|
struct XNetSession {
|
||||||
|
|
@ -143,6 +148,9 @@ class XNetTransport {
|
||||||
// header stays platform-independent.
|
// header stays platform-independent.
|
||||||
uint8_t relay_addr_[16] = {};
|
uint8_t relay_addr_[16] = {};
|
||||||
bool relay_resolved_ = false;
|
bool relay_resolved_ = false;
|
||||||
|
uint8_t join_pkt_[7 + sizeof(XNetRelayJoin)] = {};
|
||||||
|
uint16_t join_pkt_size_ = 0;
|
||||||
|
double last_join_ = 0.0; // monotonic seconds (heartbeat to beat the timeout)
|
||||||
std::thread thread_;
|
std::thread thread_;
|
||||||
bool running_ = false;
|
bool running_ = false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include <rex/platform.h>
|
#include <rex/platform.h>
|
||||||
|
#include <rex/logging.h>
|
||||||
#include <rex/system/kernel_state.h>
|
#include <rex/system/kernel_state.h>
|
||||||
#include <rex/system/xsocket.h>
|
#include <rex/system/xsocket.h>
|
||||||
#include <rex/system/xam/user_profile.h>
|
#include <rex/system/xam/user_profile.h>
|
||||||
|
|
@ -29,6 +30,15 @@
|
||||||
namespace rex {
|
namespace rex {
|
||||||
namespace system {
|
namespace system {
|
||||||
|
|
||||||
|
// Pin the packed wire sizes to the values the relay expects (the game-side
|
||||||
|
// proto packs them identically). Catches accidental un-packing or member
|
||||||
|
// edits before they corrupt the wire.
|
||||||
|
static_assert(sizeof(XNetRelayHeader) == 7, "relay header must be 7 bytes");
|
||||||
|
static_assert(sizeof(XNetRelayJoin) == 31, "relay join must be 31 bytes");
|
||||||
|
static_assert(sizeof(XNetRelayGame) == 1410, "relay game payload must be 1410 bytes");
|
||||||
|
static_assert(sizeof(XNetRelaySessionEntry) == 25, "session entry must be 25 bytes");
|
||||||
|
static_assert(sizeof(XNetRelaySessionReply) == 801, "session reply must be 801 bytes");
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Virtual address range: 10.80.0.0/16 (host order prefix 0x0A500000).
|
// Virtual address range: 10.80.0.0/16 (host order prefix 0x0A500000).
|
||||||
|
|
@ -156,14 +166,19 @@ void XNetTransport::Reconnect() {
|
||||||
}
|
}
|
||||||
sockaddr_in relay_addr = {};
|
sockaddr_in relay_addr = {};
|
||||||
if (!Resolve(host_, port_, &relay_addr)) {
|
if (!Resolve(host_, port_, &relay_addr)) {
|
||||||
|
REXLOG_WARN("xnet transport: cannot resolve relay '{}:{}'", host_, port_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::memcpy(relay_addr_, &relay_addr, sizeof(relay_addr));
|
std::memcpy(relay_addr_, &relay_addr, sizeof(relay_addr));
|
||||||
relay_resolved_ = true;
|
relay_resolved_ = true;
|
||||||
sock_ = SocketCreate();
|
sock_ = SocketCreate();
|
||||||
if (sock_ == -1) {
|
if (sock_ == -1) {
|
||||||
|
REXLOG_WARN("xnet transport: cannot open relay socket");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
REXLOG_INFO("xnet transport: connecting to relay '{}:{}' room '{}' (vip {}.{}.{}.{})", host_,
|
||||||
|
port_, room_.c_str(), (LocalVirtualIp() >> 24) & 0xFF, (LocalVirtualIp() >> 16) & 0xFF,
|
||||||
|
(LocalVirtualIp() >> 8) & 0xFF, LocalVirtualIp() & 0xFF);
|
||||||
// Join the room so the relay routes our game traffic among its members.
|
// Join the room so the relay routes our game traffic among its members.
|
||||||
XNetRelayJoin jp = {};
|
XNetRelayJoin jp = {};
|
||||||
jp.version = kVersion;
|
jp.version = kVersion;
|
||||||
|
|
@ -184,6 +199,12 @@ void XNetTransport::Reconnect() {
|
||||||
std::memcpy(pkt + 7, &jp, sizeof(jp));
|
std::memcpy(pkt + 7, &jp, sizeof(jp));
|
||||||
SocketSendTo(sock_, &relay_addr, pkt, sizeof(pkt));
|
SocketSendTo(sock_, &relay_addr, pkt, sizeof(pkt));
|
||||||
|
|
||||||
|
// Keep the JOIN around for the heartbeat (the relay drops silent clients
|
||||||
|
// after ~8s).
|
||||||
|
std::memcpy(join_pkt_, pkt, sizeof(pkt));
|
||||||
|
join_pkt_size_ = uint16_t(sizeof(pkt));
|
||||||
|
last_join_ = 0.0;
|
||||||
|
|
||||||
running_ = true;
|
running_ = true;
|
||||||
thread_ = std::thread(&XNetTransport::Run, this);
|
thread_ = std::thread(&XNetTransport::Run, this);
|
||||||
}
|
}
|
||||||
|
|
@ -271,6 +292,14 @@ void XNetTransport::Run() {
|
||||||
if (!running_) {
|
if (!running_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Heartbeat: re-JOIN every ~4s so the relay never times us out.
|
||||||
|
const double now =
|
||||||
|
double(std::chrono::steady_clock::now().time_since_epoch().count()) / 1e9;
|
||||||
|
if (join_pkt_size_ != 0 && now - last_join_ >= 4.0) {
|
||||||
|
SocketSendTo(sock_, reinterpret_cast<const sockaddr_in*>(relay_addr_), join_pkt_,
|
||||||
|
join_pkt_size_);
|
||||||
|
last_join_ = now;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
uint8_t buf[7 + sizeof(XNetRelayGame)];
|
uint8_t buf[7 + sizeof(XNetRelayGame)];
|
||||||
sockaddr_in from = {};
|
sockaddr_in from = {};
|
||||||
|
|
@ -307,6 +336,9 @@ void XNetTransport::HandlePacket(const uint8_t* data, int size) {
|
||||||
// session_id != 0 means accepted (0 = refused).
|
// session_id != 0 means accepted (0 = refused).
|
||||||
active_ = payload[0] != 0;
|
active_ = payload[0] != 0;
|
||||||
joined_ = true;
|
joined_ = true;
|
||||||
|
REXLOG_INFO("xnet transport: relay {} (vip {}.{}.{}.{})", active_ ? "joined" : "refused",
|
||||||
|
(LocalVirtualIp() >> 24) & 0xFF, (LocalVirtualIp() >> 16) & 0xFF,
|
||||||
|
(LocalVirtualIp() >> 8) & 0xFF, LocalVirtualIp() & 0xFF);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kGame: {
|
case kGame: {
|
||||||
|
|
@ -350,6 +382,8 @@ void XNetTransport::HandlePacket(const uint8_t* data, int size) {
|
||||||
std::lock_guard<std::mutex> lock(mu_);
|
std::lock_guard<std::mutex> lock(mu_);
|
||||||
sessions_ = std::move(fresh);
|
sessions_ = std::move(fresh);
|
||||||
++reply_seq_;
|
++reply_seq_;
|
||||||
|
REXLOG_INFO("xnet transport: session reply with {} session(s) in room '{}'", count,
|
||||||
|
room_.c_str());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
2
thirdparty/imgui
vendored
2
thirdparty/imgui
vendored
|
|
@ -1 +1 @@
|
||||||
Subproject commit cdda62349d6068e906c2e0ee340d6ec31eedbb9c
|
Subproject commit 0af8f475d1cf8b98d603702059c0c73deeb81ac2
|
||||||
Loading…
Add table
Reference in a new issue