diff --git a/src/OverteClient.cpp b/src/OverteClient.cpp index 7e5ef8f..9831b28 100644 --- a/src/OverteClient.cpp +++ b/src/OverteClient.cpp @@ -7,33 +7,66 @@ #include #include #include +#include #include +#include +#include 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(&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(&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(&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(std::chrono::steady_clock::now() - t0).count(); diff --git a/src/OverteClient.hpp b/src/OverteClient.hpp index ecccf7f..e43b05c 100644 --- a/src/OverteClient.hpp +++ b/src/OverteClient.hpp @@ -8,6 +8,11 @@ #include +// Networking types needed for member declarations (sockaddr_storage, socklen_t) +#include +#include +#include + 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 m_entities; std::vector 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}; }; diff --git a/src/main.cpp b/src/main.cpp index 31e3790..15ed671 100644 --- a/src/main.cpp +++ b/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";