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();
|
||||
|
||||
@@ -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};
|
||||
};
|
||||
|
||||
|
||||
12
src/main.cpp
12
src/main.cpp
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user