feat: enhance OverteClient connection handling with TCP and UDP support; add environment variable and command-line argument overrides for URL

This commit is contained in:
MayaTheShy
2025-11-08 14:26:24 -05:00
parent f8170456ca
commit f9a7e61ff5
3 changed files with 94 additions and 23 deletions

View File

@@ -7,33 +7,66 @@
#include <glm/gtc/matrix_transform.hpp>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>
using namespace std::chrono_literals;
bool OverteClient::connect() {
// Basic reachability check (TCP) if ws://host:port specified.
// Format expected: ws://host:port
auto posScheme = m_domainUrl.find("ws://");
if (posScheme != 0) {
std::cerr << "[OverteClient] Unexpected URL scheme; expected ws://" << std::endl;
}
auto hostPort = m_domainUrl.substr(5); // strip ws://
auto colon = hostPort.find(':');
std::string host = colon == std::string::npos ? hostPort : hostPort.substr(0, colon);
int port = colon == std::string::npos ? 40102 : std::stoi(hostPort.substr(colon + 1));
// Parse ws://host:port
std::string url = m_domainUrl;
if (url.empty()) url = "ws://127.0.0.1:40102";
if (url.rfind("ws://", 0) == 0) url = url.substr(5);
auto colon = url.find(':');
m_host = colon == std::string::npos ? url : url.substr(0, colon);
m_port = colon == std::string::npos ? 40102 : std::stoi(url.substr(colon + 1));
// Attempt a non-blocking TCP connect (best-effort; ignore failure but warn).
int fd = ::socket(AF_INET, SOCK_STREAM, 0);
if (fd != -1) {
sockaddr_in addr{}; addr.sin_family = AF_INET; addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY; // Skip DNS for stub; real impl would resolve host.
if (::connect(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
std::cerr << "[OverteClient] Warning: unable to reach Overte domain (stub)." << std::endl;
} else {
std::cout << "[OverteClient] (Stub) TCP connect succeeded to " << host << ':' << port << std::endl;
// Resolve host:port
addrinfo hints{}; hints.ai_socktype = SOCK_STREAM; hints.ai_family = AF_UNSPEC;
addrinfo* res = nullptr;
int gai = ::getaddrinfo(m_host.c_str(), std::to_string(m_port).c_str(), &hints, &res);
if (gai != 0) {
std::cerr << "[OverteClient] getaddrinfo failed for " << m_host << ":" << m_port << " - " << gai_strerror(gai) << std::endl;
} else {
// Attempt TCP reachability for diagnostics
int fd = -1; addrinfo* rp = res;
for (; rp; rp = rp->ai_next) {
fd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (fd == -1) continue;
::fcntl(fd, F_SETFL, O_NONBLOCK);
int c = ::connect(fd, rp->ai_addr, rp->ai_addrlen);
if (c == 0 || (c == -1 && errno == EINPROGRESS)) {
std::cout << "[OverteClient] TCP reachable (non-blocking) to " << m_host << ":" << m_port << std::endl;
::close(fd); fd = -1; break;
}
::close(fd); fd = -1;
}
::close(fd);
::freeaddrinfo(res);
if (fd == -1) {
// Not necessarily fatal; mixers are UDP. Continue with UDP.
}
}
// Setup UDP to target (avatar mixer guess: same port by default)
addrinfo uhints{}; uhints.ai_socktype = SOCK_DGRAM; uhints.ai_family = AF_UNSPEC;
addrinfo* ures = nullptr;
int ugai = ::getaddrinfo(m_host.c_str(), std::to_string(m_port).c_str(), &uhints, &ures);
if (ugai != 0) {
std::cerr << "[OverteClient] UDP resolve failed: " << gai_strerror(ugai) << std::endl;
} else {
for (addrinfo* rp = ures; rp; rp = rp->ai_next) {
m_udpFd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (m_udpFd == -1) continue;
::fcntl(m_udpFd, F_SETFL, O_NONBLOCK);
std::memcpy(&m_udpAddr, rp->ai_addr, rp->ai_addrlen);
m_udpAddrLen = rp->ai_addrlen;
m_udpReady = true;
std::cout << "[OverteClient] UDP socket ready for " << m_host << ":" << m_port << std::endl;
break;
}
::freeaddrinfo(ures);
}
// Simulate successful connections to mixers.
@@ -54,8 +87,8 @@ bool OverteClient::connect() {
}
bool OverteClient::connectAvatarMixer() {
// TODO: Use Overte networking layer (NodeList) to connect to AvatarMixer.
m_avatarMixer = true;
// For now, consider UDP socket readiness as mixer connectivity proxy.
m_avatarMixer = m_udpReady;
return true;
}
@@ -74,6 +107,21 @@ bool OverteClient::connectAudioMixer() {
void OverteClient::poll() {
if (!m_connected) return;
// Try a lightweight UDP ping if ready
if (m_udpReady && m_udpFd != -1) {
const char ping[4] = {'P','I','N','G'};
ssize_t s = ::sendto(m_udpFd, ping, sizeof(ping), 0, reinterpret_cast<sockaddr*>(&m_udpAddr), m_udpAddrLen);
if (s == -1 && errno != EWOULDBLOCK && errno != EAGAIN) {
std::cerr << "[OverteClient] UDP send failed: " << std::strerror(errno) << std::endl;
}
char buf[1500];
sockaddr_storage from{}; socklen_t fromlen = sizeof(from);
ssize_t r = ::recvfrom(m_udpFd, buf, sizeof(buf), 0, reinterpret_cast<sockaddr*>(&from), &fromlen);
if (r > 0) {
std::cout << "[OverteClient] UDP packet received (" << r << " bytes)" << std::endl;
}
}
// Simulate entity transforms changing slightly over time.
static auto t0 = std::chrono::steady_clock::now();
const float t = std::chrono::duration<float>(std::chrono::steady_clock::now() - t0).count();

View File

@@ -8,6 +8,11 @@
#include <glm/glm.hpp>
// Networking types needed for member declarations (sockaddr_storage, socklen_t)
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
struct OverteEntity {
std::uint64_t id{0};
std::string name;
@@ -41,6 +46,8 @@ public:
private:
std::string m_domainUrl;
std::string m_host{"127.0.0.1"};
int m_port{40102};
bool m_connected{false};
bool m_avatarMixer{false};
bool m_entityServer{false};
@@ -50,5 +57,11 @@ private:
std::unordered_map<std::uint64_t, OverteEntity> m_entities;
std::vector<std::uint64_t> m_updateQueue; // ids of entities updated since last consume
std::uint64_t m_nextEntityId{1};
// Networking
int m_udpFd{-1};
bool m_udpReady{false};
struct sockaddr_storage m_udpAddr{};
socklen_t m_udpAddrLen{0};
};

View File

@@ -24,7 +24,17 @@ int main(int argc, char** argv) {
return 1;
}
OverteClient overte("ws://example.overte.domain:40102");
// Overte localhost default assumption (can override via OVERTE_URL env or --overte=ws://host:port)
std::string overteUrl = "ws://127.0.0.1:40102";
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
const std::string ov = "--overte=";
if (arg.rfind(ov, 0) == 0) overteUrl = arg.substr(ov.size());
}
if (const char* envOv = std::getenv("OVERTE_URL")) {
overteUrl = envOv;
}
OverteClient overte(overteUrl);
// Overte is optional; warn if unreachable but continue in offline mode.
if (!overte.connect()) {
std::cerr << "[Overte] Domain unreachable; running in offline mode.\n";