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:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user