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;
|
std::cout << "[OverteClient] Authenticated: " << (authenticated ? "yes" : "no") << std::endl;
|
||||||
|
|
||||||
// Now mark as connected since we got a valid DomainList
|
// Read additional timing/metadata fields (from Overte's DomainServer::sendDomainListToNode)
|
||||||
m_domainConnected = true;
|
// These fields were added after the authenticated flag
|
||||||
|
if (offset + 8 > len) {
|
||||||
// Send EntityQuery to request entity data from the server
|
std::cout << "[OverteClient] Packet too short for timing fields" << std::endl;
|
||||||
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;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < numNodes && offset < len; ++i) {
|
// lastDomainCheckinTimestamp (uint64)
|
||||||
// Read NodeType
|
uint64_t lastCheckinTimestamp;
|
||||||
if (offset + 1 > len) break;
|
std::memcpy(&lastCheckinTimestamp, data + offset, 8);
|
||||||
unsigned char nodeType = static_cast<unsigned char>(data[offset++]);
|
lastCheckinTimestamp = be64toh(lastCheckinTimestamp);
|
||||||
|
offset += 8;
|
||||||
|
|
||||||
// Skip UUID (16 bytes)
|
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;
|
||||||
|
|
||||||
|
// 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;
|
if (offset + 16 > len) break;
|
||||||
|
std::memcpy(ac.uuid.data(), data + offset, 16);
|
||||||
offset += 16;
|
offset += 16;
|
||||||
|
|
||||||
// Read public socket address
|
// Read PublicSocket.type (quint8)
|
||||||
if (offset + sizeof(sockaddr_in) > len) break;
|
if (offset + 1 > len) break;
|
||||||
|
uint8_t publicSocketType = static_cast<uint8_t>(data[offset++]);
|
||||||
|
|
||||||
sockaddr_in publicAddr;
|
// Read PublicSocket.address (QHostAddress)
|
||||||
std::memcpy(&publicAddr, data + offset, sizeof(sockaddr_in));
|
if (offset + 1 > len) break;
|
||||||
offset += sizeof(sockaddr_in);
|
uint8_t addressProtocol = static_cast<uint8_t>(data[offset++]);
|
||||||
|
|
||||||
// Skip local socket (same size)
|
if (addressProtocol == 1) { // IPv4
|
||||||
if (offset + sizeof(sockaddr_in) > len) break;
|
if (offset + 4 > len) break;
|
||||||
offset += sizeof(sockaddr_in);
|
uint32_t ipv4Addr;
|
||||||
|
std::memcpy(&ipv4Addr, data + offset, 4);
|
||||||
|
ipv4Addr = ntohl(ipv4Addr);
|
||||||
|
offset += 4;
|
||||||
|
|
||||||
// NodeType values from Overte:
|
// Read PublicSocket.port (quint16)
|
||||||
// 0 = DomainServer, 1 = EntityServer, 2 = Agent, 3 = AudioMixer,
|
if (offset + 2 > len) break;
|
||||||
// 4 = AvatarMixer, 5 = AssetServer, 6 = MessagesMixer, 7 = EntityScriptServer
|
uint16_t publicPort = ntohs(*reinterpret_cast<const uint16_t*>(data + offset));
|
||||||
const unsigned char NODE_TYPE_ENTITY_SERVER = 1;
|
offset += 2;
|
||||||
const unsigned char NODE_TYPE_AVATAR_MIXER = 4;
|
|
||||||
const unsigned char NODE_TYPE_AUDIO_MIXER = 3;
|
|
||||||
|
|
||||||
char addrStr[INET_ADDRSTRLEN];
|
// Store address
|
||||||
inet_ntop(AF_INET, &publicAddr.sin_addr, addrStr, sizeof(addrStr));
|
sockaddr_in* addr = reinterpret_cast<sockaddr_in*>(&ac.address);
|
||||||
int port = ntohs(publicAddr.sin_port);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read LocalSocket.type (quint8)
|
||||||
|
if (offset + 1 > len) break;
|
||||||
|
uint8_t localSocketType = static_cast<uint8_t>(data[offset++]);
|
||||||
|
(void)localSocketType; // unused for now
|
||||||
|
|
||||||
|
// 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";
|
const char* nodeTypeName = "Unknown";
|
||||||
switch (nodeType) {
|
switch (ac.type) {
|
||||||
case 0: nodeTypeName = "DomainServer"; break;
|
case 'D': nodeTypeName = "DomainServer"; break;
|
||||||
case NODE_TYPE_ENTITY_SERVER: nodeTypeName = "EntityServer"; break;
|
case 'o': nodeTypeName = "EntityServer"; break;
|
||||||
case 2: nodeTypeName = "Agent"; break;
|
case 'I': nodeTypeName = "Agent"; break;
|
||||||
case NODE_TYPE_AUDIO_MIXER: nodeTypeName = "AudioMixer"; break;
|
case 'M': nodeTypeName = "AudioMixer"; break;
|
||||||
case NODE_TYPE_AVATAR_MIXER: nodeTypeName = "AvatarMixer"; break;
|
case 'W': nodeTypeName = "AvatarMixer"; break;
|
||||||
case 5: nodeTypeName = "AssetServer"; break;
|
case 'A': nodeTypeName = "AssetServer"; break;
|
||||||
case 6: nodeTypeName = "MessagesMixer"; break;
|
case 'm': nodeTypeName = "MessagesMixer"; break;
|
||||||
case 7: nodeTypeName = "EntityScriptServer"; break;
|
case 'S': nodeTypeName = "EntityScriptServer"; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "[OverteClient] Assignment: " << nodeTypeName
|
char addrStr[INET_ADDRSTRLEN];
|
||||||
<< " at " << addrStr << ":" << port << std::endl;
|
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) {
|
std::cout << "[OverteClient] Assignment client: " << nodeTypeName
|
||||||
// Update EntityServer connection to use discovered address
|
<< " at " << addrStr << ":" << ac.port << std::endl;
|
||||||
std::cout << "[OverteClient] Connecting to EntityServer at " << addrStr << ":" << port << std::endl;
|
|
||||||
|
|
||||||
// Update target address for EntityServer
|
// If this is the EntityServer, store its address for EntityQuery
|
||||||
sockaddr_in* entityAddr = reinterpret_cast<sockaddr_in*>(&m_entityAddr);
|
if (ac.type == 'o') { // EntityServer
|
||||||
entityAddr->sin_family = AF_INET;
|
m_entityServerAddr = ac.address;
|
||||||
entityAddr->sin_port = publicAddr.sin_port;
|
m_entityServerAddrLen = ac.addressLen;
|
||||||
entityAddr->sin_addr = publicAddr.sin_addr;
|
m_entityServerPort = ac.port;
|
||||||
m_entityAddrLen = sizeof(sockaddr_in);
|
|
||||||
|
|
||||||
m_entityServerReady = true;
|
std::cout << "[OverteClient] Entity server found at " << addrStr << ":" << ac.port << std::endl;
|
||||||
|
|
||||||
// Send EntityQuery to request all entities
|
|
||||||
sendEntityQuery();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
void OverteClient::handleDomainConnectionDenied(const char* data, size_t len) {
|
||||||
|
|||||||
Reference in New Issue
Block a user