From d2e80039df1712734a6e3b0d811d73f850345157 Mon Sep 17 00:00:00 2001 From: Rye Date: Sun, 2 Nov 2025 13:20:01 -0500 Subject: Fix building with clang on non-darwin platforms Signed-off-by: Rye --- indra/newview/llviewermessage.cpp | 4 ---- 1 file changed, 4 deletions(-) (limited to 'indra/newview/llviewermessage.cpp') diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index e9f6d7175e..efd5a4b8ad 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -3730,9 +3730,7 @@ void process_sound_trigger(LLMessageSystem *msg, void **) { if (!gAudiop) { -#if !LL_LINUX LL_WARNS("AudioEngine") << "LLAudioEngine instance doesn't exist!" << LL_ENDL; -#endif return; } @@ -3804,9 +3802,7 @@ void process_preload_sound(LLMessageSystem *msg, void **user_data) { if (!gAudiop) { -#if !LL_LINUX LL_WARNS("AudioEngine") << "LLAudioEngine instance doesn't exist!" << LL_ENDL; -#endif return; } -- cgit v1.3 From 842095059730c67367719d57d51c33fe6c87af74 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Wed, 19 Nov 2025 01:21:14 +0200 Subject: #4926 Replace tabs with spaces in script messages --- indra/newview/llviewermessage.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra/newview/llviewermessage.cpp') diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index e9f6d7175e..29f3bc62d5 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -2435,6 +2435,8 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) color.setVec(1.f,1.f,1.f,1.f); msg->getStringFast(_PREHASH_ChatData, _PREHASH_Message, mesg); + // Preserve tabs from scripts by expanding them to spaces before any sanitization/formatting. + LLStringUtil::replaceTabsWithSpaces(mesg, 4); bool ircstyle = false; -- cgit v1.3 From f99e2233a40e3a7b88e6c54aa89a1bc0bcd76456 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 27 May 2026 09:33:09 -0700 Subject: Log failed delivery of crucial vewer-->simulator connection UDP messages (#5568) --- indra/newview/llstartup.cpp | 157 ++++++++++++++++++++++++++++++-------- indra/newview/llviewermessage.cpp | 21 ++++- indra/newview/llviewerregion.cpp | 21 ++++- 3 files changed, 165 insertions(+), 34 deletions(-) (limited to 'indra/newview/llviewermessage.cpp') diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index b3ed65be82..6a85ca069a 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -24,6 +24,72 @@ * $/LicenseInfo$ */ +// +// LOGIN AND CONNECTION SEQUENCE OVERVIEW +// ====================================== +// The Viewer connects to the SL service in two phases: HTTP authentication +// followed by UDP "Circuit" establishment to the first Simulator. +// +// PHASE 1: HTTP LOGIN (see lllogin.cpp, process_login_success_response()) +// ----------------------------------------------------------------------- +// Viewer sends an XMLRPC HTTP POST to LoginServer containing: +// - Credentials (first name, last name, password) +// - Client version, channel, MAC address, machine ID +// - Start location preferences +// +// Login-server responds with critical connection data: +// - agent_id, session_id, secure_session_id (authentication tokens) +// - Circuit_code (used to establish UDP Circuit with Simulator) +// - sim_ip, sim_port (Simulator address to connect to) +// - seed_capability (base URL for HTTP capability requests) +// - region_x, region_y (region grid coordinates) +// +// PHASE 2: UDP CIRCUIT ESTABLISHMENT (see idle_startup() state machine below) +// --------------------------------------------------------------------------- +// After HTTP login succeeds, Viewer establishes a UDP Circuit with the +// simulator. This also happens whenever the Viewer connects to new Simulators +// in the same session. The following UDP messages are exchanged: +// +// 1. UseCircuitCode (Viewer -> Simulator) +// - Sent in STATE_WORLD_INIT +// - Contains: Circuit_code, session_id, agent_id +// - Establishes the UDP Circuit with the Simulator +// +// 2. RegionHandshake (Simulator -> Viewer) +// - Handled by process_region_handshake() in llworld.cpp +// - Contains: region name, terrain textures, water height, region flags +// - Viewer responds with RegionHandshakeReply +// +// 3. CompleteAgentMovement (Viewer -> Simulator) +// - Sent in STATE_AGENT_SEND via send_complete_agent_movement() +// - Signals the Viewer is ready to enter the world +// +// 4. AgentMovementComplete (Simulator -> Viewer) +// - Handled by process_agent_movement_complete() in llviewermessage.cpp +// - Contains: final agent position, look_at direction, region handle +// - Sets gAgentMovementCompleted = true +// - Agent is now fully connected to the region +// +// STARTUP STATE MACHINE +// --------------------- +// The connection sequence is managed by idle_startup() which progresses +// through these key states: +// +// STATE_LOGIN_WAIT - Waiting for HTTP login response +// STATE_LOGIN_PROCESS_RESPONSE - Processing login response data +// STATE_WORLD_INIT - Send UseCircuitCode, enable UDP Circuit +// STATE_WORLD_WAIT - Wait for Circuit acknowledgment +// STATE_AGENT_SEND - Send CompleteAgentMovement +// STATE_AGENT_WAIT - Wait for AgentMovementComplete +// STATE_INVENTORY_SEND - Agent connected, begin loading inventory +// +// HTTP CAPABILITIES +// ----------------- +// After UDP connection, Viewer fetches "capability" URLs from the +// seed_capability endpoint. These provide HTTP endpoints for various +// services (inventory, textures, etc.) that supplement the UDP protocol. +// + #include "llviewerprecompiledheaders.h" #include "llappviewer.h" @@ -277,7 +343,6 @@ void show_first_run_dialog(); bool first_run_dialog_callback(const LLSD& notification, const LLSD& response); void set_startup_status(const F32 frac, const std::string& string, const std::string& msg); bool login_alert_status(const LLSD& notification, const LLSD& response); -void use_circuit_callback(void**, S32 result); void register_viewer_callbacks(LLMessageSystem* msg); void asset_callback_nothing(const LLUUID&, LLAssetType::EType, void*, S32); bool callback_choose_gender(const LLSD& notification, const LLSD& response); @@ -1719,13 +1784,48 @@ bool idle_startup() gUseCircuitCallbackCalled = false; msg->enableCircuit(gFirstSim, true); - // now, use the circuit info to tell simulator about us! + + // UDP CONNECTION STEP 1: Send UseCircuitCode + // This is the first UDP message sent to Simulator after HTTP login. + // It establishes the UDP circuit using the circuit_code received from + // LoginServer. Simulator will respond with an ACK, then send + // RegionHandshake message with region details. LL_INFOS("AppInit") << "viewer: UserLoginLocationReply() Enabling " << gFirstSim << " with code " << msg->mOurCircuitCode << LL_ENDL; msg->newMessageFast(_PREHASH_UseCircuitCode); msg->nextBlockFast(_PREHASH_CircuitCode); msg->addU32Fast(_PREHASH_Code, msg->mOurCircuitCode); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->addUUIDFast(_PREHASH_ID, gAgent.getID()); + + // build a lambda to be used as callback on ACK or timeout + void (*use_circuit_callback)(void**, S32) = [](void**, S32 result) + { + // bail if we're quitting. + if(LLApp::isExiting()) return; + if( !gUseCircuitCallbackCalled ) + { + gUseCircuitCallbackCalled = true; + if (result != LL_ERR_NOERR) + { + // Make sure user knows something bad happened. JC + LL_WARNS("AppInit") << "Backing up to login screen!" << LL_ENDL; + if (gRememberPassword) + { + LLNotificationsUtil::add("LoginPacketNeverReceived", LLSD(), LLSD(), login_alert_status); + } + else + { + LLNotificationsUtil::add("LoginPacketNeverReceivedNoTP", LLSD(), LLSD(), login_alert_status); + } + reset_login(); + } + else + { + gGotUseCircuitCodeAck = true; + } + } + }; + msg->sendReliable( gFirstSim, gSavedSettings.getS32("UseCircuitCodeMaxRetries"), @@ -1743,6 +1843,9 @@ bool idle_startup() //--------------------------------------------------------------------- // World Wait //--------------------------------------------------------------------- + // UDP CONNECTION STEP 2: Wait for UseCircuitCode acknowledgment + // While waiting, Simulator also sends RegionHandshake (handled by + // process_region_handshake() in llworld.cpp) containing region info. if(STATE_WORLD_WAIT == LLStartUp::getStartupState()) { LL_DEBUGS("AppInit") << "Waiting for simulator ack...." << LL_ENDL; @@ -1758,13 +1861,16 @@ bool idle_startup() //--------------------------------------------------------------------- // Agent Send //--------------------------------------------------------------------- + // UDP CONNECTION STEP 3: Send CompleteAgentMovement + // After the circuit is established and RegionHandshake received, we signal + // to Simulator the Viewer is ready to enter the world. if (STATE_AGENT_SEND == LLStartUp::getStartupState()) { LL_DEBUGS("AppInit") << "Connecting to region..." << LL_ENDL; set_startup_status(0.60f, LLTrans::getString("LoginConnectingToRegion"), gAgent.mMOTD); do_startup_frame(); - // register with the message system so it knows we're - // expecting this message + // Register handler process_agent_movement_complete for AgentMovementComplete - + // the final UDP message confirming the agent is connected. LLMessageSystem* msg = gMessageSystem; msg->setHandlerFuncFast( _PREHASH_AgentMovementComplete, @@ -1807,12 +1913,17 @@ bool idle_startup() //--------------------------------------------------------------------- // Agent Wait //--------------------------------------------------------------------- + // UDP CONNECTION STEP 4: Wait for AgentMovementComplete + // Simulator responds with the agent's confirmed position and look_at + // direction. Once received, gAgentMovementCompleted is set true and the + // agent is fully connected to the region. if (STATE_AGENT_WAIT == LLStartUp::getStartupState()) { do_startup_frame(); if (gAgentMovementCompleted) { + // Connection complete - agent is now in-world LLStartUp::setStartupState( STATE_INVENTORY_SEND ); } do_startup_frame(); @@ -2811,34 +2922,6 @@ bool login_alert_status(const LLSD& notification, const LLSD& response) } -void use_circuit_callback(void**, S32 result) -{ - // bail if we're quitting. - if(LLApp::isExiting()) return; - if( !gUseCircuitCallbackCalled ) - { - gUseCircuitCallbackCalled = true; - if (result) - { - // Make sure user knows something bad happened. JC - LL_WARNS("AppInit") << "Backing up to login screen!" << LL_ENDL; - if (gRememberPassword) - { - LLNotificationsUtil::add("LoginPacketNeverReceived", LLSD(), LLSD(), login_alert_status); - } - else - { - LLNotificationsUtil::add("LoginPacketNeverReceivedNoTP", LLSD(), LLSD(), login_alert_status); - } - reset_login(); - } - else - { - gGotUseCircuitCodeAck = true; - } - } -} - void register_viewer_callbacks(LLMessageSystem* msg) { msg->setHandlerFuncFast(_PREHASH_LayerData, process_layer_data ); @@ -3731,6 +3814,14 @@ bool init_benefits(LLSD& response) return succ; } +// HTTP LOGIN RESPONSE PROCESSING +// Called after successful HTTP XMLRPC authentication. Extracts critical data +// from LoginServer response needed to establish the UDP connection: +// - agent_id, session_id, secure_session_id (authentication tokens) +// - circuit_code (used in UseCircuitCode UDP message) +// - sim_ip, sim_port (simulator address for UDP circuit) +// - seed_capability (URL for fetching HTTP capability endpoints) +// bool process_login_success_response() { LLSD response = LLLoginInstance::getInstance()->getResponse(); @@ -3853,6 +3944,8 @@ bool process_login_success_response() gAgentStartLocation.assign(text); } + // Extract UDP circuit parameters from login response. + // These are used in STATE_WORLD_INIT to establish the UDP circuit. text = response["circuit_code"].asString(); if(!text.empty()) { diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 812ba76551..1f1ff30b8f 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -418,7 +418,26 @@ void send_complete_agent_movement(const LLHost& sim_host) msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->addU32Fast(_PREHASH_CircuitCode, msg->mOurCircuitCode); - msg->sendReliable(sim_host); + + // build a lambda to be used as callback on ACK or timeout + void (*complete_agent_movement_callback)(void**, S32) = [](void**, S32 result) + { + if(LLApp::isExiting()) return; + if (result != LL_ERR_NOERR) + { + LL_WARNS("Messaging") << "CompleteAgentMovement failed with err=" << result << LL_ENDL; + } + }; + + // We use same retry strategy as UseCircuitCode because this is a crucial message + // that MUST arrive else we'll suffer a failed login/teleport/region-cross + msg->sendReliable( + sim_host, + gSavedSettings.getS32("UseCircuitCodeMaxRetries"), + false, + (F32Seconds)gSavedSettings.getF32("UseCircuitCodeTimeout"), + complete_agent_movement_callback, + NULL); } void process_logout_reply(LLMessageSystem* msg, void**) diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index dd02cca50a..5f73d8e901 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -3207,7 +3207,26 @@ void LLViewerRegion::unpackRegionHandshake() flags |= 0x00000002; //set the bit 1 to be 1 to tell sim the cache file is empty, no need to send cache probes. } msg->addU32("Flags", flags ); - msg->sendReliable(host); + + // build a lambda to be used as callback on ACK or timeout + void (*region_handshake_reply_callback)(void**, S32) = [](void**, S32 result) + { + if(LLApp::isExiting()) return; + if (result != LL_ERR_NOERR) + { + LL_WARNS("Messaging") << "RegionHandshakeReply failed with err=" << result << LL_ENDL; + } + }; + + // This is a crucial message for establishing a connection to a region + // (either the main region or a visible neighbor). + msg->sendReliable( + host, + gSavedSettings.getS32("UseCircuitCodeMaxRetries"), + false, + (F32Seconds)gSavedSettings.getF32("UseCircuitCodeTimeout"), + region_handshake_reply_callback, + NULL); mRegionTimer.reset(); //reset region timer. } -- cgit v1.3 From b906284977b120c4c554fd08b78fac62759b160a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 22 Jul 2026 09:01:47 -0700 Subject: Defer processing AvatarAppearance until after first ObjectUpdate (#6017) * make AvatarAppearance a low-priority message * remember agent_id of unexpected AvatarAppearance message for later --- indra/llmessage/llpacketbuffer.h | 7 ++++ indra/llmessage/message.cpp | 80 +++++++++++++++++++++++++++++++-------- indra/llmessage/message.h | 6 ++- indra/llprimitive/llprimitive.cpp | 8 +++- indra/llprimitive/llprimitive.h | 3 ++ indra/newview/llagent.cpp | 1 + indra/newview/llviewermessage.cpp | 9 +++-- indra/newview/llvoavatar.cpp | 18 ++++++++- indra/newview/llvoavatar.h | 21 +++++++++- 9 files changed, 128 insertions(+), 25 deletions(-) (limited to 'indra/newview/llviewermessage.cpp') diff --git a/indra/llmessage/llpacketbuffer.h b/indra/llmessage/llpacketbuffer.h index b0505e7afd..a08e6c22e5 100644 --- a/indra/llmessage/llpacketbuffer.h +++ b/indra/llmessage/llpacketbuffer.h @@ -49,11 +49,18 @@ public: void init(S32 hSocket); void init(const char* buffer, S32 data_size, const LLHost& host); + // Whether LLCircuitData::checkPacketInID() has already been run for this + // packet (done at socket-read time, before this packet was sorted into + // the high/low priority inbound queue). + bool getPacketIDChecked() const { return mPacketIDChecked; } + void setPacketIDChecked(bool checked) { mPacketIDChecked = checked; } + protected: char mData[NET_BUFFER_SIZE]; // packet data /* Flawfinder : ignore */ S32 mSize; // size of buffer in bytes LLHost mHost; // source/dest IP and port LLHost mReceivingIF; // source/dest IP and port + bool mPacketIDChecked = false; }; #endif diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index 52180b20f1..8d86856706 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -519,10 +519,11 @@ bool LLMessageSystem::checkMessages(LockMessageChecker&, S64 frame_count ) bool recv_resent = false; S32 num_acks = 0; S32 true_rcv_size = 0; + bool recv_packet_id_checked = false; U8* buffer = mTrueReceiveBuffer; - mTrueReceiveSize = receivePacketOrDrop((char *)mTrueReceiveBuffer); + mTrueReceiveSize = receivePacketOrDrop((char *)mTrueReceiveBuffer, recv_packet_id_checked); // If you want to dump all received packets into SecondLife.log, uncomment this //dumpPacketToLog(); @@ -686,7 +687,7 @@ bool LLMessageSystem::checkMessages(LockMessageChecker&, S64 frame_count ) if ( valid_packet ) { - logValidMsg(cdp, host, recv_reliable, recv_resent, num_acks>0 ); + logValidMsg(cdp, host, recv_reliable, recv_resent, num_acks>0, recv_packet_id_checked ); valid_packet = mTemplateMessageReader->readMessage(buffer, host); } @@ -866,19 +867,37 @@ bool LLMessageSystem::computeDrop() bool LLMessageSystem::isHighPriorityMessage(const LLPacketBuffer& pkt) const { S32 size = pkt.getSize(); - if (size < LL_PACKET_ID_SIZE + 1) + + // avoid stepping out of bounds + const S32 MIN_VALID_PACKET_SIZE = LL_PACKET_ID_SIZE + 4; + if (size < MIN_VALID_PACKET_SIZE) { return false; } - // We want to prioritize crucial messages use to establish viewer <--> simulator connection, + // We want to prioritize crucial messages used to establish viewer <--> simulator connection, // which are all low-frequency. A simple approximation is to just prioritize all non high- // frequency messages. // - // High frequency messages use a single byte for message_id whereas all low- and medium- - // frequency messages have 255 at the first byte of the message_id (which is after the - // LL_PACKET_ID_SIZE bytes of packet_id). - return *((const U8*)pkt.getData() + LL_PACKET_ID_SIZE) == 255; + // High frequency messages use a single byte for message_id whereas medium- and low- + // frequency messages have 255 at the first byte (which is after the LL_PACKET_ID_SIZE + // bytes of packet_id). + const U8* header = (const U8*)pkt.getData() + LL_PACKET_ID_SIZE; + if (header[0] != 255) + { + // high-frequency message + return false; + } + + // BUG: The low-frequency AvatarAppearance message will be ignored if its agent is unknown + // and the agent is created upon receipt of its first high-frequency ObjectUpdate message. + // This race condition will be exacerbated by our default prioritization strategy. + // + // WORKAROUND: All middle- and low-frequency messages are high-priority except AvatarAppearance + // + // AvatarAppearance is "Low 158" which means it is stored in four bytes: 0xff 0xff 0x00 0x9E + // the last two bytes represent 158 in a BigEndian U16 + return header[1] != 255 && header[2] != 0 && header[3] != 158; } void LLMessageSystem::dropPackets(U32 num_to_drop) @@ -891,8 +910,10 @@ void LLMessageSystem::setDropPercentage(F32 percent_to_drop) mDropPercentage = percent_to_drop; } -S32 LLMessageSystem::receivePacketOrDrop(char* datap) +S32 LLMessageSystem::receivePacketOrDrop(char* datap, bool& packet_id_already_checked) { + packet_id_already_checked = false; + if (getNumBufferedPackets() > 0) { LLHost invalid_host; @@ -905,6 +926,7 @@ S32 LLMessageSystem::receivePacketOrDrop(char* datap) S32 packet_size = pkt.getSize(); mLastSender = pkt.getHost(); mLastReceivingIF = pkt.getReceivingInterface(); + packet_id_already_checked = pkt.getPacketIDChecked(); if (packet_size > 0) { @@ -913,7 +935,8 @@ S32 LLMessageSystem::receivePacketOrDrop(char* datap) return packet_size; } - // Read directly from the socket. + // Read directly from the socket. checkPacketInID() has not run yet for + // this packet. bool drop = computeDrop(); S32 packet_size = 0; if (LLProxy::isSOCKSProxyEnabled()) @@ -1034,10 +1057,24 @@ S32 LLMessageSystem::bufferInboundPacket() } } - // ACK inbound reliable packet ASAP - if (cdp && (data[0] & LL_RELIABLE_FLAG)) + if (cdp) { - cdp->collectRAck(recv_packet_id); + // ACK inbound reliable packet ASAP + if ((data[0] & LL_RELIABLE_FLAG)) + { + cdp->collectRAck(recv_packet_id); + } + + // Check packet sequencing here, in true socket-arrival order, before + // this packet is sorted into the high/low priority inbound queue. + // Skip genuine duplicate resends, same as checkMessages()/logValidMsg() + // would do further downstream. + bool recv_resent = (data[0] & LL_RESENT_FLAG) != 0; + if (!recv_resent || !cdp->isDuplicateResend(recv_packet_id)) + { + cdp->checkPacketInID(recv_packet_id, recv_resent); + pkt.setPacketIDChecked(true); + } } if (isHighPriorityMessage(pkt)) @@ -1668,7 +1705,13 @@ void LLMessageSystem::logTrustedMsgFromUntrustedCircuit( const LLHost& host ) } } -void LLMessageSystem::logValidMsg(LLCircuitData *cdp, const LLHost& host, bool recv_reliable, bool recv_resent, bool recv_acks ) +void LLMessageSystem::logValidMsg( + LLCircuitData *cdp, + const LLHost& host, + bool recv_reliable, + bool recv_resent, + bool recv_acks, + bool skip_packet_id_check ) { if (mNumMessageCounts >= MAX_MESSAGE_COUNT_NUM) { @@ -1685,8 +1728,13 @@ void LLMessageSystem::logValidMsg(LLCircuitData *cdp, const LLHost& host, bool r if (cdp) { - // update circuit packet ID tracking (missing/out of order packets) - cdp->checkPacketInID( mCurrentRecvPacketID, recv_resent ); + if (!skip_packet_id_check) + { + // update circuit packet ID tracking (missing/out of order packets) + // Already done in bufferInboundPacket(), in true socket-arrival + // order, if this packet came off the high/low priority queues. + cdp->checkPacketInID( mCurrentRecvPacketID, recv_resent ); + } cdp->addBytesIn( (S32Bytes)mTrueReceiveSize ); } diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h index e81cfe16a9..e23a67c04a 100644 --- a/indra/llmessage/message.h +++ b/indra/llmessage/message.h @@ -910,7 +910,7 @@ private: void logMsgFromInvalidCircuit( const LLHost& sender, bool recv_reliable ); void logTrustedMsgFromUntrustedCircuit( const LLHost& sender ); - void logValidMsg(LLCircuitData *cdp, const LLHost& sender, bool recv_reliable, bool recv_resent, bool recv_acks ); + void logValidMsg(LLCircuitData *cdp, const LLHost& sender, bool recv_reliable, bool recv_resent, bool recv_acks, bool skip_packet_id_check ); class LLMessageCountInfo { @@ -967,8 +967,10 @@ private: // Receive one packet: pop from ring if buffered, else read from mSocket. // Sets mLastSender and mLastReceivingIF. + // Sets packet_id_already_checked to whether checkPacketInID() was already + // run for this packet back when it was buffered (see bufferInboundPacket()). // Returns packet_size, or 0 if no packet or packet was dropped. - S32 receivePacketOrDrop(char* datap); + S32 receivePacketOrDrop(char* datap, bool& packet_id_already_checked); // Read one raw packet from mSocket into inbound message queues // Returns packet_size (0 if no packet was available). diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index 7a638dd625..fd755e35dd 100644 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -1369,6 +1369,12 @@ bool LLPrimitive::packTEMessage(LLDataPacker &dp) const } S32 LLPrimitive::parseTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num, LLTEContents& tec) +{ + return parseTEMessage(mesgsys, block_name, block_num, tec, getNumTEs()); +} + +// static +S32 LLPrimitive::parseTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num, LLTEContents& tec, U8 face_count) { S32 retval = 0; // temp buffer for material ID processing @@ -1403,7 +1409,7 @@ S32 LLPrimitive::parseTEMessage(LLMessageSystem* mesgsys, char const* block_name tec.packed_buffer[tec.size] = 0x00; ++tec.size; - tec.face_count = llmin((U32)getNumTEs(),(U32)LLTEContents::MAX_TES); + tec.face_count = llmin((U32)face_count,(U32)LLTEContents::MAX_TES); U8 *cur_ptr = tec.packed_buffer; LL_DEBUGS("TEXTUREENTRY") << "Texture Entry with buffere sized: " << tec.size << LL_ENDL; diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h index c3e3e19ee9..21bf52610e 100644 --- a/indra/llprimitive/llprimitive.h +++ b/indra/llprimitive/llprimitive.h @@ -502,6 +502,9 @@ public: S32 unpackTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num); // Variable num of blocks S32 unpackTEMessage(LLDataPacker &dp); S32 parseTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num, LLTEContents& tec); + // Same as above, but usable before an instance exists to derive the face count from + // (e.g. decoding a message for an object that hasn't been created yet). + static S32 parseTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num, LLTEContents& tec, U8 face_count); S32 applyParsedTEMessage(LLTEContents& tec); #ifdef CHECK_FOR_FINITE diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 35d60e6595..2f0ad15d18 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -4151,6 +4151,7 @@ void LLAgent::handleTeleportFinished() } clearTeleportRequest(); mTeleportCanceled.reset(); + LLVOAvatar::resetEarlyAppearanceList(); if (mIsMaturityRatingChangingDuringTeleport) { // notify user that the maturity preference has been changed diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 1f1ff30b8f..e435ce08e3 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -4181,11 +4181,12 @@ void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data) if (avatarp) { avatarp->processAvatarAppearance( mesgsys ); + return; } - else - { - LL_WARNS("Messaging") << "avatar_appearance sent for unknown avatar " << uuid << LL_ENDL; - } + // The avatar object doesn't exist yet. + // We will re-request its appearance data after it is created. + LLVOAvatar::registerEarlyAppearance(uuid); + LL_WARNS("Messaging") << "AvatarAppearance received for avatar " << uuid << " before object created" << LL_ENDL; } void process_camera_constraint(LLMessageSystem *mesgsys, void **user_data) diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index cd71a2c660..89ee5ede2a 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -615,6 +615,8 @@ const LLUUID LLVOAvatar::sStepSounds[LL_MCODE_END] = SND_RUBBER_RUBBER }; +uuid_list_t LLVOAvatar::sEarlyAppearanceList; + S32 LLVOAvatar::sRenderName = RENDER_NAME_ALWAYS; S32 LLVOAvatar::sRenderGroupTitles = RENDER_GROUP_TITLE_ALWAYS; S32 LLVOAvatar::sNumVisibleChatBubbles = 0; @@ -779,6 +781,20 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mVisuallyMuteSetting = LLVOAvatar::VisualMuteSettings(LLRenderMuteList::getInstance()->getSavedVisualMuteSetting(getID())); sInstances.push_back(this); + + uuid_list_t::iterator it = sEarlyAppearanceList.find(id); + if (it != sEarlyAppearanceList.end()) + { + // Note: aside from LLVOAvatar::resetEarlyAppearanceList() (called on + // teleport), this is the only place where we remove from + // sEarlyAppearanceList, which means any agent who receives an + // AvatarAppearance message but is never actually instantiated will + // remain on the list until the next teleport. This is a resource leak + // but we expect it to be small enough per-session to not cause problems. + sEarlyAppearanceList.erase(it); + LL_INFOS("Avatar") << "Re-requesting AvatarAppearance for new avatar " << id << LL_ENDL; + LLAvatarPropertiesProcessor::getInstance()->sendAvatarTexturesRequest(getID()); + } } std::string LLVOAvatar::avString() const @@ -9937,7 +9953,7 @@ void LLVOAvatar::applyParsedAppearanceMessage(LLAppearanceMessageContents& conte if (visualParamWeightsAreDefault() && mRuthTimer.getElapsedTimeF32() > LOADING_TIMEOUT_SECONDS) { // re-request appearance, hoping that it comes back with a shape next time - LL_INFOS() << "Re-requesting AvatarAppearance for object: " << getID() << LL_ENDL; + LL_INFOS() << "Re-requesting AvatarAppearance for agent: " << getID() << LL_ENDL; LLAvatarPropertiesProcessor::getInstance()->sendAvatarTexturesRequest(getID()); mRuthTimer.reset(); } diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 3f33dc54b8..3457e7bb6a 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -949,9 +949,26 @@ protected: ** APPEARANCE **/ +public: + // Used when an AvatarAppearance UDP message is received before the + // corresponding avatar could be created. + static void registerEarlyAppearance(const LLUUID& av_id) + { + sEarlyAppearanceList.emplace(av_id); + } + + // Entries left behind by agents who never get instantiated (e.g. an + // AvatarAppearance message arrives for an avatar we never rez) are a + // small resource leak. Teleporting to another region invalidates the + // whole list, since it was only ever relevant to avatars in the region + // we're leaving, so clear it out at that point to bound the leak. + static void resetEarlyAppearanceList() + { + sEarlyAppearanceList.clear(); + } + LLPointer mLastProcessedAppearance; -public: void parseAppearanceMessage(LLMessageSystem* mesgsys, LLAppearanceMessageContents& msg); void processAvatarAppearance(LLMessageSystem* mesgsys); void applyParsedAppearanceMessage(LLAppearanceMessageContents& contents, bool slam_params); @@ -983,6 +1000,8 @@ private: bool mIsEditingAppearance; // flag for if we're actively in appearance editing mode bool mUseLocalAppearance; // flag for if we're using a local composite + static uuid_list_t sEarlyAppearanceList; + //-------------------------------------------------------------------- // Visibility //-------------------------------------------------------------------- -- cgit v1.3