feat: enhance domain list reply handling with timing metadata and assignment client parsing
This commit is contained in:
@@ -823,106 +823,195 @@ void OverteClient::handleDomainListReply(const char* data, size_t len) {
|
||||
|
||||
std::cout << "[OverteClient] Authenticated: " << (authenticated ? "yes" : "no") << std::endl;
|
||||
|
||||
// Now mark as connected since we got a valid DomainList
|
||||
m_domainConnected = true;
|
||||
|
||||
// Send EntityQuery to request entity data from the server
|
||||
std::cout << "[OverteClient] Domain connected! Sending entity query..." << std::endl;
|
||||
std::cout << "[OverteClient] Waiting to receive entities from server..." << std::endl;
|
||||
sendEntityQuery();
|
||||
|
||||
// Read number of nodes - Qt QDataStream format (signed int32, big-endian)
|
||||
// But for node list, Overte uses a special encoding
|
||||
// Looking at the packet: ff 01 00 06 43 23...
|
||||
// 0xFF might be a marker, or this might be encoded differently
|
||||
// Let's try reading it as a signed int32
|
||||
if (offset + 4 > len) return;
|
||||
int32_t numNodesRaw = static_cast<int32_t>(ntohl(*reinterpret_cast<const uint32_t*>(data + offset)));
|
||||
|
||||
std::cout << "[OverteClient] Number of assignment clients (raw): 0x" << std::hex << numNodesRaw << std::dec
|
||||
<< " (" << numNodesRaw << ")" << std::endl;
|
||||
|
||||
// If the high byte is 0xFF, this might be a QList with custom size encoding
|
||||
// For now, let's skip the node list and just note we're connected
|
||||
uint32_t numNodes = (numNodesRaw < 0 || numNodesRaw > 100) ? 0 : static_cast<uint32_t>(numNodesRaw);
|
||||
offset += 4;
|
||||
|
||||
std::cout << "[OverteClient] Parsed node count: " << numNodes << std::endl;
|
||||
|
||||
if (numNodesRaw < 0 || numNodesRaw > 100) {
|
||||
std::cout << "[OverteClient] Warning: Unusual node count encoding, skipping node list parsing" << std::endl;
|
||||
// Dump remaining bytes for analysis
|
||||
std::cout << "[OverteClient] Remaining bytes: ";
|
||||
for (size_t i = offset - 4; i < std::min(offset + 20, len); i++) {
|
||||
printf("%02x ", (unsigned char)data[i]);
|
||||
}
|
||||
std::cout << std::endl;
|
||||
// Read additional timing/metadata fields (from Overte's DomainServer::sendDomainListToNode)
|
||||
// These fields were added after the authenticated flag
|
||||
if (offset + 8 > len) {
|
||||
std::cout << "[OverteClient] Packet too short for timing fields" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < numNodes && offset < len; ++i) {
|
||||
// Read NodeType
|
||||
if (offset + 1 > len) break;
|
||||
unsigned char nodeType = static_cast<unsigned char>(data[offset++]);
|
||||
// lastDomainCheckinTimestamp (uint64)
|
||||
uint64_t lastCheckinTimestamp;
|
||||
std::memcpy(&lastCheckinTimestamp, data + offset, 8);
|
||||
lastCheckinTimestamp = be64toh(lastCheckinTimestamp);
|
||||
offset += 8;
|
||||
|
||||
if (offset + 8 > len) return;
|
||||
// currentTimestamp (uint64)
|
||||
uint64_t currentTimestamp;
|
||||
std::memcpy(¤tTimestamp, data + offset, 8);
|
||||
currentTimestamp = be64toh(currentTimestamp);
|
||||
offset += 8;
|
||||
|
||||
if (offset + 8 > len) return;
|
||||
// processingTime (uint64)
|
||||
uint64_t processingTime;
|
||||
std::memcpy(&processingTime, data + offset, 8);
|
||||
processingTime = be64toh(processingTime);
|
||||
offset += 8;
|
||||
|
||||
if (offset + 1 > len) return;
|
||||
// newConnection (bool)
|
||||
bool newConnection = data[offset++];
|
||||
|
||||
std::cout << "[OverteClient] New connection: " << (newConnection ? "yes" : "no") << std::endl;
|
||||
|
||||
// Now mark as connected since we got a valid DomainList
|
||||
m_domainConnected = true;
|
||||
|
||||
// Clear previous assignment client list
|
||||
m_assignmentClients.clear();
|
||||
m_entityServerPort = 0;
|
||||
|
||||
// Parse assignment client nodes from the packet
|
||||
// Each node is serialized using QDataStream format (see Node.cpp operator<<)
|
||||
// Format per node:
|
||||
// - NodeType (qint8/char)
|
||||
// - UUID (16 bytes)
|
||||
// - PublicSocket.type (quint8)
|
||||
// - PublicSocket (QHostAddress [1 byte protocol + 4 bytes IPv4] + quint16 port)
|
||||
// - LocalSocket.type (quint8)
|
||||
// - LocalSocket (QHostAddress + quint16 port)
|
||||
// - Permissions (quint32)
|
||||
// - isReplicated (bool)
|
||||
// - localID (quint16)
|
||||
// - connectionSecretUUID (16 bytes) - added by DomainList packet
|
||||
|
||||
std::cout << "[OverteClient] Parsing assignment clients..." << std::endl;
|
||||
|
||||
while (offset < len) {
|
||||
AssignmentClient ac;
|
||||
|
||||
// Skip UUID (16 bytes)
|
||||
// Read NodeType (qint8)
|
||||
if (offset + 1 > len) break;
|
||||
ac.type = static_cast<uint8_t>(data[offset++]);
|
||||
|
||||
// Read UUID (16 bytes)
|
||||
if (offset + 16 > len) break;
|
||||
std::memcpy(ac.uuid.data(), data + offset, 16);
|
||||
offset += 16;
|
||||
|
||||
// Read public socket address
|
||||
if (offset + sizeof(sockaddr_in) > len) break;
|
||||
// Read PublicSocket.type (quint8)
|
||||
if (offset + 1 > len) break;
|
||||
uint8_t publicSocketType = static_cast<uint8_t>(data[offset++]);
|
||||
|
||||
sockaddr_in publicAddr;
|
||||
std::memcpy(&publicAddr, data + offset, sizeof(sockaddr_in));
|
||||
offset += sizeof(sockaddr_in);
|
||||
// Read PublicSocket.address (QHostAddress)
|
||||
if (offset + 1 > len) break;
|
||||
uint8_t addressProtocol = static_cast<uint8_t>(data[offset++]);
|
||||
|
||||
// Skip local socket (same size)
|
||||
if (offset + sizeof(sockaddr_in) > len) break;
|
||||
offset += sizeof(sockaddr_in);
|
||||
if (addressProtocol == 1) { // IPv4
|
||||
if (offset + 4 > len) break;
|
||||
uint32_t ipv4Addr;
|
||||
std::memcpy(&ipv4Addr, data + offset, 4);
|
||||
ipv4Addr = ntohl(ipv4Addr);
|
||||
offset += 4;
|
||||
|
||||
// Read PublicSocket.port (quint16)
|
||||
if (offset + 2 > len) break;
|
||||
uint16_t publicPort = ntohs(*reinterpret_cast<const uint16_t*>(data + offset));
|
||||
offset += 2;
|
||||
|
||||
// Store address
|
||||
sockaddr_in* addr = reinterpret_cast<sockaddr_in*>(&ac.address);
|
||||
addr->sin_family = AF_INET;
|
||||
addr->sin_addr.s_addr = htonl(ipv4Addr);
|
||||
addr->sin_port = htons(publicPort);
|
||||
ac.addressLen = sizeof(sockaddr_in);
|
||||
ac.port = publicPort;
|
||||
|
||||
} else {
|
||||
std::cout << "[OverteClient] Unsupported address protocol: " << (int)addressProtocol << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
// NodeType values from Overte:
|
||||
// 0 = DomainServer, 1 = EntityServer, 2 = Agent, 3 = AudioMixer,
|
||||
// 4 = AvatarMixer, 5 = AssetServer, 6 = MessagesMixer, 7 = EntityScriptServer
|
||||
const unsigned char NODE_TYPE_ENTITY_SERVER = 1;
|
||||
const unsigned char NODE_TYPE_AVATAR_MIXER = 4;
|
||||
const unsigned char NODE_TYPE_AUDIO_MIXER = 3;
|
||||
// Read LocalSocket.type (quint8)
|
||||
if (offset + 1 > len) break;
|
||||
uint8_t localSocketType = static_cast<uint8_t>(data[offset++]);
|
||||
(void)localSocketType; // unused for now
|
||||
|
||||
char addrStr[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &publicAddr.sin_addr, addrStr, sizeof(addrStr));
|
||||
int port = ntohs(publicAddr.sin_port);
|
||||
// Read LocalSocket.address (QHostAddress)
|
||||
if (offset + 1 > len) break;
|
||||
uint8_t localAddressProtocol = static_cast<uint8_t>(data[offset++]);
|
||||
|
||||
if (localAddressProtocol == 1) { // IPv4
|
||||
if (offset + 4 > len) break;
|
||||
offset += 4; // Skip local IP
|
||||
|
||||
// Read LocalSocket.port (quint16)
|
||||
if (offset + 2 > len) break;
|
||||
offset += 2; // Skip local port
|
||||
} else {
|
||||
std::cout << "[OverteClient] Unsupported local address protocol: " << (int)localAddressProtocol << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
// Read Permissions (quint32)
|
||||
if (offset + 4 > len) break;
|
||||
offset += 4; // Skip permissions
|
||||
|
||||
// Read isReplicated (bool)
|
||||
if (offset + 1 > len) break;
|
||||
offset++; // Skip isReplicated
|
||||
|
||||
// Read localID (quint16)
|
||||
if (offset + 2 > len) break;
|
||||
offset += 2; // Skip localID
|
||||
|
||||
// Read connectionSecretUUID (16 bytes) - this is added by DomainList packet
|
||||
if (offset + 16 > len) break;
|
||||
offset += 16; // Skip connectionSecretUUID
|
||||
|
||||
// Store this assignment client
|
||||
m_assignmentClients.push_back(ac);
|
||||
|
||||
// NodeType mapping (from Overte NodeType.h):
|
||||
// 'D' (0x44) = DomainServer
|
||||
// 'o' (0x6F) = EntityServer
|
||||
// 'I' (0x49) = Agent
|
||||
// 'M' (0x4D) = AudioMixer
|
||||
// 'W' (0x57) = AvatarMixer
|
||||
// 'A' (0x41) = AssetServer
|
||||
// 'm' (0x6D) = MessagesMixer
|
||||
// 'S' (0x53) = EntityScriptServer
|
||||
|
||||
const char* nodeTypeName = "Unknown";
|
||||
switch (nodeType) {
|
||||
case 0: nodeTypeName = "DomainServer"; break;
|
||||
case NODE_TYPE_ENTITY_SERVER: nodeTypeName = "EntityServer"; break;
|
||||
case 2: nodeTypeName = "Agent"; break;
|
||||
case NODE_TYPE_AUDIO_MIXER: nodeTypeName = "AudioMixer"; break;
|
||||
case NODE_TYPE_AVATAR_MIXER: nodeTypeName = "AvatarMixer"; break;
|
||||
case 5: nodeTypeName = "AssetServer"; break;
|
||||
case 6: nodeTypeName = "MessagesMixer"; break;
|
||||
case 7: nodeTypeName = "EntityScriptServer"; break;
|
||||
switch (ac.type) {
|
||||
case 'D': nodeTypeName = "DomainServer"; break;
|
||||
case 'o': nodeTypeName = "EntityServer"; break;
|
||||
case 'I': nodeTypeName = "Agent"; break;
|
||||
case 'M': nodeTypeName = "AudioMixer"; break;
|
||||
case 'W': nodeTypeName = "AvatarMixer"; break;
|
||||
case 'A': nodeTypeName = "AssetServer"; break;
|
||||
case 'm': nodeTypeName = "MessagesMixer"; break;
|
||||
case 'S': nodeTypeName = "EntityScriptServer"; break;
|
||||
}
|
||||
|
||||
std::cout << "[OverteClient] Assignment: " << nodeTypeName
|
||||
<< " at " << addrStr << ":" << port << std::endl;
|
||||
char addrStr[INET_ADDRSTRLEN];
|
||||
sockaddr_in* addr = reinterpret_cast<sockaddr_in*>(&ac.address);
|
||||
inet_ntop(AF_INET, &addr->sin_addr, addrStr, sizeof(addrStr));
|
||||
|
||||
if (nodeType == NODE_TYPE_ENTITY_SERVER) {
|
||||
// Update EntityServer connection to use discovered address
|
||||
std::cout << "[OverteClient] Connecting to EntityServer at " << addrStr << ":" << port << std::endl;
|
||||
std::cout << "[OverteClient] Assignment client: " << nodeTypeName
|
||||
<< " at " << addrStr << ":" << ac.port << std::endl;
|
||||
|
||||
// If this is the EntityServer, store its address for EntityQuery
|
||||
if (ac.type == 'o') { // EntityServer
|
||||
m_entityServerAddr = ac.address;
|
||||
m_entityServerAddrLen = ac.addressLen;
|
||||
m_entityServerPort = ac.port;
|
||||
|
||||
// Update target address for EntityServer
|
||||
sockaddr_in* entityAddr = reinterpret_cast<sockaddr_in*>(&m_entityAddr);
|
||||
entityAddr->sin_family = AF_INET;
|
||||
entityAddr->sin_port = publicAddr.sin_port;
|
||||
entityAddr->sin_addr = publicAddr.sin_addr;
|
||||
m_entityAddrLen = sizeof(sockaddr_in);
|
||||
|
||||
m_entityServerReady = true;
|
||||
|
||||
// Send EntityQuery to request all entities
|
||||
sendEntityQuery();
|
||||
std::cout << "[OverteClient] Entity server found at " << addrStr << ":" << ac.port << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "[OverteClient] Parsed " << m_assignmentClients.size() << " assignment clients" << std::endl;
|
||||
|
||||
// Now send EntityQuery to the EntityServer (if we found one)
|
||||
if (m_entityServerPort != 0) {
|
||||
std::cout << "[OverteClient] Domain connected! Sending entity query to entity-server..." << std::endl;
|
||||
sendEntityQuery();
|
||||
} else {
|
||||
std::cout << "[OverteClient] Warning: No EntityServer found in assignment client list" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void OverteClient::handleDomainConnectionDenied(const char* data, size_t len) {
|
||||
|
||||
Reference in New Issue
Block a user