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 <glm/gtc/matrix_transform.hpp>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <netdb.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
bool OverteClient::connect() {
|
bool OverteClient::connect() {
|
||||||
// Basic reachability check (TCP) if ws://host:port specified.
|
// Parse ws://host:port
|
||||||
// Format expected: ws://host:port
|
std::string url = m_domainUrl;
|
||||||
auto posScheme = m_domainUrl.find("ws://");
|
if (url.empty()) url = "ws://127.0.0.1:40102";
|
||||||
if (posScheme != 0) {
|
if (url.rfind("ws://", 0) == 0) url = url.substr(5);
|
||||||
std::cerr << "[OverteClient] Unexpected URL scheme; expected ws://" << std::endl;
|
auto colon = url.find(':');
|
||||||
}
|
m_host = colon == std::string::npos ? url : url.substr(0, colon);
|
||||||
auto hostPort = m_domainUrl.substr(5); // strip ws://
|
m_port = colon == std::string::npos ? 40102 : std::stoi(url.substr(colon + 1));
|
||||||
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));
|
|
||||||
|
|
||||||
// Attempt a non-blocking TCP connect (best-effort; ignore failure but warn).
|
// Resolve host:port
|
||||||
int fd = ::socket(AF_INET, SOCK_STREAM, 0);
|
addrinfo hints{}; hints.ai_socktype = SOCK_STREAM; hints.ai_family = AF_UNSPEC;
|
||||||
if (fd != -1) {
|
addrinfo* res = nullptr;
|
||||||
sockaddr_in addr{}; addr.sin_family = AF_INET; addr.sin_port = htons(port);
|
int gai = ::getaddrinfo(m_host.c_str(), std::to_string(m_port).c_str(), &hints, &res);
|
||||||
addr.sin_addr.s_addr = INADDR_ANY; // Skip DNS for stub; real impl would resolve host.
|
if (gai != 0) {
|
||||||
if (::connect(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
|
std::cerr << "[OverteClient] getaddrinfo failed for " << m_host << ":" << m_port << " - " << gai_strerror(gai) << std::endl;
|
||||||
std::cerr << "[OverteClient] Warning: unable to reach Overte domain (stub)." << std::endl;
|
} else {
|
||||||
} else {
|
// Attempt TCP reachability for diagnostics
|
||||||
std::cout << "[OverteClient] (Stub) TCP connect succeeded to " << host << ':' << port << std::endl;
|
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.
|
// Simulate successful connections to mixers.
|
||||||
@@ -54,8 +87,8 @@ bool OverteClient::connect() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool OverteClient::connectAvatarMixer() {
|
bool OverteClient::connectAvatarMixer() {
|
||||||
// TODO: Use Overte networking layer (NodeList) to connect to AvatarMixer.
|
// For now, consider UDP socket readiness as mixer connectivity proxy.
|
||||||
m_avatarMixer = true;
|
m_avatarMixer = m_udpReady;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +107,21 @@ bool OverteClient::connectAudioMixer() {
|
|||||||
void OverteClient::poll() {
|
void OverteClient::poll() {
|
||||||
if (!m_connected) return;
|
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.
|
// Simulate entity transforms changing slightly over time.
|
||||||
static auto t0 = std::chrono::steady_clock::now();
|
static auto t0 = std::chrono::steady_clock::now();
|
||||||
const float t = std::chrono::duration<float>(std::chrono::steady_clock::now() - t0).count();
|
const float t = std::chrono::duration<float>(std::chrono::steady_clock::now() - t0).count();
|
||||||
|
|||||||
@@ -8,6 +8,11 @@
|
|||||||
|
|
||||||
#include <glm/glm.hpp>
|
#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 {
|
struct OverteEntity {
|
||||||
std::uint64_t id{0};
|
std::uint64_t id{0};
|
||||||
std::string name;
|
std::string name;
|
||||||
@@ -41,6 +46,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_domainUrl;
|
std::string m_domainUrl;
|
||||||
|
std::string m_host{"127.0.0.1"};
|
||||||
|
int m_port{40102};
|
||||||
bool m_connected{false};
|
bool m_connected{false};
|
||||||
bool m_avatarMixer{false};
|
bool m_avatarMixer{false};
|
||||||
bool m_entityServer{false};
|
bool m_entityServer{false};
|
||||||
@@ -50,5 +57,11 @@ private:
|
|||||||
std::unordered_map<std::uint64_t, OverteEntity> m_entities;
|
std::unordered_map<std::uint64_t, OverteEntity> m_entities;
|
||||||
std::vector<std::uint64_t> m_updateQueue; // ids of entities updated since last consume
|
std::vector<std::uint64_t> m_updateQueue; // ids of entities updated since last consume
|
||||||
std::uint64_t m_nextEntityId{1};
|
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;
|
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.
|
// Overte is optional; warn if unreachable but continue in offline mode.
|
||||||
if (!overte.connect()) {
|
if (!overte.connect()) {
|
||||||
std::cerr << "[Overte] Domain unreachable; running in offline mode.\n";
|
std::cerr << "[Overte] Domain unreachable; running in offline mode.\n";
|
||||||
|
|||||||
Reference in New Issue
Block a user