Merge branch 'DRTVWR-591-maint-X' of https://github.com/secondlife/viewer

# Conflicts:
#	indra/llui/lltransutil.cpp
#	indra/llwindow/llwindowmacosx.cpp
#	indra/llwindow/llwindowmacosx.h
#	indra/newview/app_settings/settings.xml
#	indra/newview/llavatarpropertiesprocessor.cpp
#	indra/newview/llavatarpropertiesprocessor.h
#	indra/newview/llinspectavatar.cpp
#	indra/newview/llpanelprofile.cpp
#	indra/newview/llpanelprofile.h
#	indra/newview/llviewerjoystick.cpp
master
Ansariel 2023-10-26 20:58:56 +02:00
commit 63f30264ff
32 changed files with 1417 additions and 891 deletions

View File

@ -241,6 +241,7 @@ Ansariel Hiller
SL-18432
SL-19140
SL-4126
SL-20524
Aralara Rajal
Arare Chantilly
CHUIBUG-191

View File

@ -44,7 +44,7 @@ bool LLTransUtil::parseStrings(const std::string& xml_filename, const std::set<s
bool success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root, LLDir::ALL_SKINS);
if (!success)
{
gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN);
//gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN);
LL_ERRS() << "Couldn't load string table " << xml_filename << ". Please reinstall viewer from https://www.firestormviewer.org/choose-your-platform/ and contact https://www.firestormviewer.org/support if issue persists after reinstall." << LL_ENDL;
return false;
}

View File

@ -197,7 +197,13 @@ public:
// windows only DirectInput8 for joysticks
virtual void* getDirectInput8() { return NULL; };
virtual bool getInputDevices(U32 device_type_filter, void * devices_callback, void* userdata) { return false; };
virtual bool getInputDevices(U32 device_type_filter,
std::function<bool(std::string&, LLSD&, void*)> osx_callback,
void* win_callback,
void* userdata)
{
return false;
};
virtual S32 getRefreshRate() { return mRefreshRate; }
protected:

View File

@ -27,6 +27,7 @@
#include <AppKit/AppKit.h>
#include <Cocoa/Cocoa.h>
#include <errno.h>
#include "llopenglview-objc.h"
#include "llwindowmacosx-objc.h"
#include "llappdelegate-objc.h"

View File

@ -43,6 +43,13 @@
#include <CoreServices/CoreServices.h>
#include <CoreGraphics/CGDisplayConfiguration.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOMessage.h>
#include <IOKit/hid/IOHIDUsageTables.h>
#include <IOKit/hid/IOHIDLib.h>
#include <IOKit/usb/IOUSBLib.h>
extern BOOL gDebugWindowProc;
BOOL gHiDPISupport = TRUE;
@ -221,13 +228,16 @@ bool callKeyUp(NSKeyEventRef event, unsigned short key, unsigned int mask)
bool callKeyDown(NSKeyEventRef event, unsigned short key, unsigned int mask, wchar_t character)
{
if((key == gKeyboard->inverseTranslateKey('Z')) && (character == 'y'))
//if (mask!=MASK_NONE)
{
key = gKeyboard->inverseTranslateKey('Y');
}
else if ((key == gKeyboard->inverseTranslateKey('Y')) && (character == 'z'))
{
key = gKeyboard->inverseTranslateKey('Z');
if((key == gKeyboard->inverseTranslateKey('Z')) && (character == 'y'))
{
key = gKeyboard->inverseTranslateKey('Y');
}
else if ((key == gKeyboard->inverseTranslateKey('Y')) && (character == 'z'))
{
key = gKeyboard->inverseTranslateKey('Z');
}
}
mRawKeyEvent = event;
@ -1838,6 +1848,488 @@ void LLWindowMacOSX::spawnWebBrowser(const std::string& escaped_url, bool async)
}
}
// String should match ndof, so string mapping code was copied as is
static char mapChar( char c )
{
unsigned char uc = ( unsigned char ) c;
switch( uc )
{
case '/': return '-'; // use dash instead of slash
case 0x7F: return ' ';
case 0x80: return 'A';
case 0x81: return 'A';
case 0x82: return 'C';
case 0x83: return 'E';
case 0x84: return 'N';
case 0x85: return 'O';
case 0x86: return 'U';
case 0x87: return 'a';
case 0x88: return 'a';
case 0x89: return 'a';
case 0x8A: return 'a';
case 0x8B: return 'a';
case 0x8C: return 'a';
case 0x8D: return 'c';
case 0x8E: return 'e';
case 0x8F: return 'e';
case 0x90: return ' ';
case 0x91: return ' '; // ? '
case 0x92: return ' '; // ? '
case 0x93: return ' '; // ? "
case 0x94: return ' '; // ? "
case 0x95: return ' ';
case 0x96: return ' ';
case 0x97: return ' ';
case 0x98: return ' ';
case 0x99: return ' ';
case 0x9A: return ' ';
case 0x9B: return 0x27;
case 0x9C: return 0x22;
case 0x9D: return ' ';
case 0x9E: return ' ';
case 0x9F: return ' ';
case 0xA0: return ' ';
case 0xA1: return ' ';
case 0xA2: return ' ';
case 0xA3: return ' ';
case 0xA4: return ' ';
case 0xA5: return ' ';
case 0xA6: return ' ';
case 0xA7: return ' ';
case 0xA8: return ' ';
case 0xA9: return ' ';
case 0xAA: return ' ';
case 0xAB: return ' ';
case 0xAC: return ' ';
case 0xAD: return ' ';
case 0xAE: return ' ';
case 0xAF: return ' ';
case 0xB0: return ' ';
case 0xB1: return ' ';
case 0xB2: return ' ';
case 0xB3: return ' ';
case 0xB4: return ' ';
case 0xB5: return ' ';
case 0xB6: return ' ';
case 0xB7: return ' ';
case 0xB8: return ' ';
case 0xB9: return ' ';
case 0xBA: return ' ';
case 0xBB: return ' ';
case 0xBC: return ' ';
case 0xBD: return ' ';
case 0xBE: return ' ';
case 0xBF: return ' ';
case 0xC0: return ' ';
case 0xC1: return ' ';
case 0xC2: return ' ';
case 0xC3: return ' ';
case 0xC4: return ' ';
case 0xC5: return ' ';
case 0xC6: return ' ';
case 0xC7: return ' ';
case 0xC8: return ' ';
case 0xC9: return ' ';
case 0xCA: return ' ';
case 0xCB: return 'A';
case 0xCC: return 'A';
case 0xCD: return 'O';
case 0xCE: return ' ';
case 0xCF: return ' ';
case 0xD0: return '-';
case 0xD1: return '-';
case 0xD2: return 0x22;
case 0xD3: return 0x22;
case 0xD4: return 0x27;
case 0xD5: return 0x27;
case 0xD6: return '-'; // use dash instead of slash
case 0xD7: return ' ';
case 0xD8: return 'y';
case 0xD9: return 'Y';
case 0xDA: return '-'; // use dash instead of slash
case 0xDB: return ' ';
case 0xDC: return '<';
case 0xDD: return '>';
case 0xDE: return ' ';
case 0xDF: return ' ';
case 0xE0: return ' ';
case 0xE1: return ' ';
case 0xE2: return ',';
case 0xE3: return ',';
case 0xE4: return ' ';
case 0xE5: return 'A';
case 0xE6: return 'E';
case 0xE7: return 'A';
case 0xE8: return 'E';
case 0xE9: return 'E';
case 0xEA: return 'I';
case 0xEB: return 'I';
case 0xEC: return 'I';
case 0xED: return 'I';
case 0xEE: return 'O';
case 0xEF: return 'O';
case 0xF0: return ' ';
case 0xF1: return 'O';
case 0xF2: return 'U';
case 0xF3: return 'U';
case 0xF4: return 'U';
case 0xF5: return '|';
case 0xF6: return ' ';
case 0xF7: return ' ';
case 0xF8: return ' ';
case 0xF9: return ' ';
case 0xFA: return '.';
case 0xFB: return ' ';
case 0xFC: return ' ';
case 0xFD: return 0x22;
case 0xFE: return ' ';
case 0xFF: return ' ';
}
return c;
}
// String should match ndof for manufacturer based search to work
static void sanitizeString( char* inCStr )
{
char* charIt = inCStr;
while ( *charIt )
{
*charIt = mapChar( *charIt );
charIt++;
}
}
struct HidDevice
{
long mAxis;
long mLocalID;
char mProduct[256];
char mManufacturer[256];
long mUsage;
long mUsagePage;
};
static void populate_device_info( io_object_t io_obj_p, CFDictionaryRef device_dic, HidDevice* devicep )
{
CFMutableDictionaryRef io_properties = nil;
io_registry_entry_t entry1;
io_registry_entry_t entry2;
kern_return_t rc;
// Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
// get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
// try to get parent1
rc = IORegistryEntryGetParentEntry( io_obj_p, kIOServicePlane, &entry1 );
if ( KERN_SUCCESS == rc )
{
rc = IORegistryEntryGetParentEntry( entry1, kIOServicePlane, &entry2 );
IOObjectRelease( entry1 );
if ( KERN_SUCCESS == rc )
{
rc = IORegistryEntryCreateCFProperties( entry2, &io_properties, kCFAllocatorDefault, kNilOptions );
// either way, release parent2
IOObjectRelease( entry2 );
}
}
if ( KERN_SUCCESS == rc )
{
// IORegistryEntryCreateCFProperties() succeeded
if ( io_properties != nil )
{
CFTypeRef dict_element = 0;
// get device info
// try hid dictionary first, if fail then go to usb dictionary
dict_element = CFDictionaryGetValue( device_dic, CFSTR(kIOHIDProductKey) );
if ( !dict_element )
{
dict_element = CFDictionaryGetValue( io_properties, CFSTR( "USB Product Name" ) );
}
if ( dict_element )
{
bool res = CFStringGetCString((CFStringRef)dict_element, devicep->mProduct, 256, kCFStringEncodingUTF8);
sanitizeString(devicep->mProduct);
if ( !res )
{
LL_WARNS("Joystick") << "Failed to populate mProduct" << LL_ENDL;
}
}
dict_element = CFDictionaryGetValue( device_dic, CFSTR( kIOHIDManufacturerKey ) );
if ( !dict_element )
{
dict_element = CFDictionaryGetValue( io_properties, CFSTR( "USB Vendor Name" ) );
}
if ( dict_element )
{
bool res = CFStringGetCString( (CFStringRef)dict_element, devicep->mManufacturer, 256, kCFStringEncodingUTF8 );
sanitizeString(devicep->mManufacturer);
if ( !res )
{
LL_WARNS("Joystick") << "Failed to populate mManufacturer" << LL_ENDL;
}
}
dict_element = CFDictionaryGetValue( device_dic, CFSTR( kIOHIDLocationIDKey ) );
if ( !dict_element )
{
dict_element = CFDictionaryGetValue( io_properties, CFSTR( "locationID" ) );
}
if ( dict_element )
{
bool res = CFNumberGetValue( (CFNumberRef)dict_element, kCFNumberLongType, &devicep->mLocalID );
if ( !res )
{
LL_WARNS("Joystick") << "Failed to populate mLocalID" << LL_ENDL;
}
}
dict_element = CFDictionaryGetValue( device_dic, CFSTR( kIOHIDPrimaryUsagePageKey ) );
if ( dict_element )
{
bool res = CFNumberGetValue( (CFNumberRef)dict_element, kCFNumberLongType, &devicep->mUsagePage );
if ( !res )
{
LL_WARNS("Joystick") << "Failed to populate mUsagePage" << LL_ENDL;
}
dict_element = CFDictionaryGetValue( device_dic, CFSTR( kIOHIDPrimaryUsageKey ) );
if ( dict_element )
{
if ( !CFNumberGetValue( (CFNumberRef)dict_element, kCFNumberLongType, &devicep->mUsage ) )
{
LL_WARNS("Joystick") << "Failed to populate mUsage" << LL_ENDL;
}
}
}
//Add axis, because ndof lib checks sutability by axises as well as other elements
devicep->mAxis = 0;
CFTypeRef hid_elements = CFDictionaryGetValue( device_dic, CFSTR( kIOHIDElementKey ) );
if ( hid_elements && CFGetTypeID( hid_elements ) == CFArrayGetTypeID( ) )
{
long count = CFArrayGetCount( (CFArrayRef) hid_elements );
for (int i = 0; i < count; ++i)
{
CFTypeRef element = CFArrayGetValueAtIndex((CFArrayRef) hid_elements, i);
if (element && CFGetTypeID( element ) == CFDictionaryGetTypeID( ))
{
long type = 0, usage_page = 0, usage = 0;
CFTypeRef ref_value = CFDictionaryGetValue( (CFDictionaryRef) element, CFSTR( kIOHIDElementTypeKey ) );
if ( ref_value )
{
CFNumberGetValue( (CFNumberRef)ref_value, kCFNumberLongType, &type );
}
ref_value = CFDictionaryGetValue( (CFDictionaryRef) element, CFSTR( kIOHIDElementUsagePageKey ) );
if ( ref_value )
{
CFNumberGetValue( (CFNumberRef)ref_value, kCFNumberLongType, &usage_page );
}
ref_value = CFDictionaryGetValue( (CFDictionaryRef) element, CFSTR( kIOHIDElementUsageKey ) );
if ( ref_value )
{
CFNumberGetValue( (CFNumberRef)ref_value, kCFNumberLongType, &usage );
}
if ( type != 0
&& type != kIOHIDElementTypeCollection
&& usage_page == kHIDPage_GenericDesktop)
{
switch( usage )
{
case kHIDUsage_GD_X:
case kHIDUsage_GD_Y:
case kHIDUsage_GD_Z:
case kHIDUsage_GD_Rx:
case kHIDUsage_GD_Ry:
case kHIDUsage_GD_Rz:
devicep->mAxis++;
break;
default:
break;
}
}
}
}
}
CFRelease(io_properties);
}
else
{
LL_WARNS("Joystick") << "Failed to populate fields" << LL_ENDL;
}
}
}
HidDevice populate_device( io_object_t io_obj )
{
void* interfacep = nullptr;
HidDevice device;
memset( &device, 0, sizeof( HidDevice ) );
CFMutableDictionaryRef device_dic = 0;
kern_return_t result = IORegistryEntryCreateCFProperties( io_obj, &device_dic, kCFAllocatorDefault, kNilOptions );
if ( KERN_SUCCESS == result
&& device_dic )
{
IOReturn io_result = kIOReturnSuccess;
HRESULT query_result = S_OK;
SInt32 the_score = 0;
IOCFPlugInInterface **the_interface = NULL;
io_result = IOCreatePlugInInterfaceForService( io_obj, kIOHIDDeviceUserClientTypeID,
kIOCFPlugInInterfaceID, &the_interface, &the_score );
if ( io_result == kIOReturnSuccess )
{
query_result = ( *the_interface )->QueryInterface( the_interface, CFUUIDGetUUIDBytes( kIOHIDDeviceInterfaceID ), ( LPVOID * ) & ( interfacep ) );
if ( query_result != S_OK )
{
LL_WARNS("Joystick") << "QueryInterface failed" << LL_ENDL;
}
IODestroyPlugInInterface( the_interface );
}
else
{
LL_WARNS("Joystick") << "IOCreatePlugInInterfaceForService failed" << LL_ENDL;
}
if ( interfacep )
{
result = ( *( IOHIDDeviceInterface** )interfacep )->open( interfacep, 0 );
if ( result != kIOReturnSuccess)
{
LL_WARNS("Joystick") << "open failed" << LL_ENDL;
}
}
// extract needed fields
populate_device_info( io_obj, device_dic, &device );
// Release interface
if ( interfacep )
{
( *( IOHIDDeviceInterface** ) interfacep )->close( interfacep );
( *( IOHIDDeviceInterface** ) interfacep )->Release( interfacep );
interfacep = NULL;
}
CFRelease( device_dic );
}
else
{
LL_WARNS("Joystick") << "populate_device failed" << LL_ENDL;
}
return device;
}
static void get_devices(std::list<HidDevice> &list_of_devices,
io_iterator_t inIODeviceIterator)
{
IOReturn result = kIOReturnSuccess; // assume success( optimist! )
io_object_t io_obj = 0;
while ( 0 != (io_obj = IOIteratorNext( inIODeviceIterator ) ) )
{
HidDevice device = populate_device( io_obj );
if (debugLoggingEnabled("Joystick"))
{
list_of_devices.push_back(device);
LL_DEBUGS("Joystick") << "Device axises: " << (S32)device.mAxis
<< "Device HIDUsepage: " << (S32)device.mUsagePage
<< "Device HIDUsage: " << (S32)device.mUsage
<< LL_ENDL;
}
else
{
// Should match ndof
if (device.mAxis >= 3
|| (device.mUsagePage == kHIDPage_GenericDesktop
&& (device.mUsage == kHIDUsage_GD_MultiAxisController
|| device.mUsage == kHIDUsage_GD_GamePad
|| device.mUsage == kHIDUsage_GD_Joystick))
|| (device.mUsagePage == kHIDPage_Game
&& device.mUsage == kHIDUsage_Game_3DGameController)
|| strstr(device.mManufacturer, "3Dconnexion"))
{
list_of_devices.push_back(device);
}
}
// release the device object, it is no longer needed
result = IOObjectRelease( io_obj );
if ( KERN_SUCCESS != result )
{
LL_WARNS("Joystick") << "IOObjectRelease failed" << LL_ENDL;
}
}
}
bool LLWindowMacOSX::getInputDevices(U32 device_type_filter,
std::function<bool(std::string&, LLSD&, void*)> osx_callback,
void* win_callback,
void* userdata)
{
bool return_value = false;
CFMutableDictionaryRef device_dict_ref;
IOReturn result = kIOReturnSuccess; // assume success( optimist! )
// Set up matching dictionary to search the I/O Registry for HID devices we are interested in. Dictionary reference is NULL if error.
// A dictionary to match devices to?
device_dict_ref = IOServiceMatching( kIOHIDDeviceKey );
// BUG FIX! one reference is consumed by IOServiceGetMatchingServices
CFRetain( device_dict_ref );
io_iterator_t io_iter = 0;
// create an IO object iterator
result = IOServiceGetMatchingServices( kIOMasterPortDefault, device_dict_ref, &io_iter );
if ( kIOReturnSuccess != result )
{
LL_WARNS("Joystick") << "IOServiceGetMatchingServices failed" << LL_ENDL;
}
if ( io_iter )
{
// add all existing devices
std::list<HidDevice> device_list;
get_devices(device_list, io_iter);
std::list<HidDevice>::iterator iter;
for (iter = device_list.begin(); iter != device_list.end(); ++iter)
{
std::string label(iter->mProduct);
LLSD data;
data["manufacturer"] = std::string(iter->mManufacturer);
data["product"] = label;
if (osx_callback(label, data, userdata))
{
break; //found device
}
}
return_value = true;
}
CFRelease( device_dict_ref );
return return_value;
}
void LLWindowMacOSX::openFile(const std::string& file_name )
{
LL_INFOS() << "Opening file " << file_name << LL_ENDL;

View File

@ -114,6 +114,11 @@ public:
F32 getSystemUISize() override;
void openFile(const std::string& file_name) override;
void setTitle(const std::string& title) override;
bool getInputDevices(U32 device_type_filter,
std::function<bool(std::string&, LLSD&, void*)> osx_callback,
void* win_callback,
void* userdata) override;
static std::vector<std::string> getDisplaysResolutionList();

View File

@ -4612,7 +4612,10 @@ void* LLWindowWin32::getDirectInput8()
return &gDirectInput8;
}
bool LLWindowWin32::getInputDevices(U32 device_type_filter, void * di8_devices_callback, void* userdata)
bool LLWindowWin32::getInputDevices(U32 device_type_filter,
std::function<bool(std::string&, LLSD&, void*)> osx_callback,
void * di8_devices_callback,
void* userdata)
{
if (gDirectInput8 != NULL)
{

View File

@ -130,7 +130,10 @@ public:
static void setDPIAwareness();
/*virtual*/ void* getDirectInput8();
/*virtual*/ bool getInputDevices(U32 device_type_filter, void * di8_devices_callback, void* userdata);
/*virtual*/ bool getInputDevices(U32 device_type_filter,
std::function<bool(std::string&, LLSD&, void*)> osx_callback,
void* win_callback,
void* userdata);
U32 getRawWParam() { return mRawWParam; }

View File

@ -859,7 +859,7 @@ bool LLXMLNode::getLayeredXMLNode(LLXMLNodePtr& root,
if (!LLXMLNode::parseFile(filename, root, NULL))
{
LL_WARNS() << "Problem reading UI description file: " << filename << LL_ENDL;
LL_WARNS() << "Problem reading UI description file: " << filename << " " << errno << LL_ENDL;
return false;
}

View File

@ -6517,7 +6517,7 @@
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>String</string>
<string>LLSD</string>
<key>Value</key>
<string />
</map>

View File

@ -35,6 +35,8 @@
#include "llviewerregion.h"
#include "rlvhandler.h"
static constexpr char* CAPNAME = "AgentProfile";
FSRadarEntry::FSRadarEntry(const LLUUID& avid)
: mID(avid),
mName(LLTrans::getString("AvatarNameWaiting")),
@ -62,16 +64,21 @@ FSRadarEntry::FSRadarEntry(const LLUUID& avid)
// NOTE: typically we request these once on creation to avoid excess traffic/processing.
//This means updates to these properties won't typically be seen while target is in nearby range.
LLAvatarPropertiesProcessor* processor = LLAvatarPropertiesProcessor::getInstance();
processor->addObserver(mID, this);
processor->sendAvatarNotesRequest(mID);
if (auto region = gAgent.getRegion(); region)
{
if (region->capabilitiesReceived())
{
const bool use_cap = LLGridManager::instance().isInSecondLife() ? true : region->isCapabilityAvailable("AgentProfile");
processor->sendAvatarPropertiesRequest(mID, use_cap);
if (LLGridManager::instance().isInSecondLife() || region->isCapabilityAvailable(CAPNAME))
{
processor->sendAvatarPropertiesRequest(mID);
}
else
{
processor->sendAvatarLegacyPropertiesRequest(mID);
processor->sendAvatarNotesRequest(mID);
}
}
else
{
@ -82,8 +89,15 @@ FSRadarEntry::FSRadarEntry(const LLUUID& avid)
mRegionCapabilitiesReceivedCallbackConnection.disconnect();
}
gAgent.removeRegionChangedCallback(mRegionChangedCallbackConnection);
const bool use_cap = LLGridManager::instance().isInSecondLife() ? true : (reg && reg->isCapabilityAvailable("AgentProfile"));
LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(mID, use_cap);
if (LLGridManager::instance().isInSecondLife() || (reg && reg->isCapabilityAvailable(CAPNAME)))
{
LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(mID);
}
else
{
LLAvatarPropertiesProcessor::getInstance()->sendAvatarLegacyPropertiesRequest(mID);
LLAvatarPropertiesProcessor::getInstance()->sendAvatarNotesRequest(mID);
}
};
mRegionChangedCallbackConnection = gAgent.addRegionChangedCallback([this, capsReceivedCb]()
@ -98,8 +112,15 @@ FSRadarEntry::FSRadarEntry(const LLUUID& avid)
if (newregion->capabilitiesReceived())
{
gAgent.removeRegionChangedCallback(mRegionChangedCallbackConnection);
const bool use_cap = LLGridManager::instance().isInSecondLife() ? true : newregion->isCapabilityAvailable("AgentProfile");
LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(mID, use_cap);
if (LLGridManager::instance().isInSecondLife() || newregion->isCapabilityAvailable(CAPNAME))
{
LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(mID);
}
else
{
LLAvatarPropertiesProcessor::getInstance()->sendAvatarLegacyPropertiesRequest(mID);
LLAvatarPropertiesProcessor::getInstance()->sendAvatarNotesRequest(mID);
}
}
else
{
@ -165,7 +186,7 @@ void FSRadarEntry::processProperties(void* data, EAvatarProcessorType type)
{
if (data)
{
if (type == APT_PROPERTIES || type == APT_PROPERTIES_LEGACY)
if (type == APT_PROPERTIES)
{
LLAvatarData* avatar_data = static_cast<LLAvatarData*>(data);
if (avatar_data && avatar_data->agent_id == gAgentID && avatar_data->avatar_id == mID)
@ -176,6 +197,17 @@ void FSRadarEntry::processProperties(void* data, EAvatarProcessorType type)
else
mAge = ((LLDate::now().secondsSinceEpoch() - (avatar_data->born_on).secondsSinceEpoch()) / 86400);
checkAge();
setNotes(avatar_data->notes);
}
}
else if (type == APT_PROPERTIES_LEGACY)
{
LLAvatarData* avatar_data = static_cast<LLAvatarData*>(data);
if (avatar_data && avatar_data->agent_id == gAgentID && avatar_data->avatar_id == mID)
{
mStatus = avatar_data->flags;
mAge = ((LLDate::now().secondsSinceEpoch() - (avatar_data->born_on).secondsSinceEpoch()) / 86400);
checkAge();
}
}
else if (type == APT_NOTES)

View File

@ -50,10 +50,16 @@ public:
void sendAgentPicksRequest()
{
LLAvatarPropertiesProcessor::getInstance()->sendAvatarPicksRequest(gAgent.getID());
// <FS> OpenSim
//LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(gAgent.getID());
if (!gAgent.getRegionCapability("AgentProfile").empty())
LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(gAgent.getID());
else
LLAvatarPropertiesProcessor::getInstance()->sendAvatarPicksRequest(gAgent.getID());
// </FS>
}
typedef boost::function<void(LLAvatarPicks*)> server_respond_callback_t;
typedef boost::function<void(LLAvatarData*)> server_respond_callback_t;
void setServerRespondCallback(const server_respond_callback_t& cb)
{
@ -62,10 +68,10 @@ public:
virtual void processProperties(void* data, EAvatarProcessorType type)
{
if(APT_PICKS == type)
if(APT_PROPERTIES == type)
{
LLAvatarPicks* picks = static_cast<LLAvatarPicks*>(data);
if(picks && gAgent.getID() == picks->target_id)
LLAvatarData* picks = static_cast<LLAvatarData*>(data);
if(picks && gAgent.getID() == picks->avatar_id)
{
if(mServerRespondCallback)
{
@ -73,6 +79,21 @@ public:
}
}
}
// <FS> OpenSim
else if (APT_PICKS == type)
{
LLAvatarPicks* picks = static_cast<LLAvatarPicks*>(data);
if (picks && gAgent.getID() == picks->target_id)
{
if (mServerRespondCallback)
{
LLAvatarData avatardata;
avatardata.picks_list = picks->picks_list;
mServerRespondCallback(&avatardata);
}
}
}
// </FS>
}
private:
@ -118,7 +139,7 @@ bool LLAgentPicksInfo::isPickLimitReached()
return getNumberOfPicks() >= LLAgentBenefitsMgr::current().getPicksLimit();
}
void LLAgentPicksInfo::onServerRespond(LLAvatarPicks* picks)
void LLAgentPicksInfo::onServerRespond(LLAvatarData* picks)
{
if(!picks)
{

View File

@ -29,7 +29,7 @@
#include "llsingleton.h"
struct LLAvatarPicks;
struct LLAvatarData;
/**
* Class that provides information about Agent Picks
@ -74,7 +74,7 @@ public:
void decrementNumberOfPicks() { --mNumberOfPicks; }
void onServerRespond(LLAvatarPicks* picks);
void onServerRespond(LLAvatarData* picks);
private:

View File

@ -245,7 +245,7 @@ void LLAvatarIconCtrl::setValue(const LLSD& value)
// messages. People API already hits the user table.
LLIconCtrl::setValue(mDefaultIconName, LLViewerFetchedTexture::BOOST_UI);
app->addObserver(mAvatarId, this);
app->sendAvatarPropertiesRequest(mAvatarId);
app->sendAvatarLegacyPropertiesRequest(mAvatarId);
}
}
}
@ -296,7 +296,7 @@ void LLAvatarIconCtrl::processProperties(void* data, EAvatarProcessorType type)
{
if (APT_PROPERTIES_LEGACY == type)
{
LLAvatarData* avatar_data = static_cast<LLAvatarData*>(data);
LLAvatarLegacyData* avatar_data = static_cast<LLAvatarLegacyData*>(data);
if (avatar_data)
{
if (avatar_data->avatar_id != mAvatarId)

View File

@ -69,7 +69,7 @@ void LLAvatarPropertiesProcessor::addObserver(const LLUUID& avatar_id, LLAvatarP
// IAN BUG this should update the observer's UUID if this is a dupe - sent to PE
if (it == end)
{
mObservers.insert(pair(avatar_id, observer));
mObservers.emplace(avatar_id, observer);
}
}
@ -117,21 +117,19 @@ void LLAvatarPropertiesProcessor::sendRequest(const LLUUID& avatar_id, EAvatarPr
}
// Try to send HTTP request if cap_url is available
if (type == APT_PROPERTIES || type == APT_PICKS || type == APT_GROUPS || type == APT_NOTES)
if (type == APT_PROPERTIES)
{
std::string cap_url(gAgent.getRegionCapability("AgentProfile"));
std::string cap_url = gAgent.getRegionCapability("AgentProfile");
if (!cap_url.empty())
{
initAgentProfileCapRequest(avatar_id, cap_url, type);
return;
}
// Don't sent UDP request for APT_PROPERTIES
if (type == APT_PROPERTIES)
else
{
LL_WARNS() << "No cap_url for APT_PROPERTIES, request is not sent" << LL_ENDL;
return;
// Don't sent UDP request for APT_PROPERTIES
LL_WARNS() << "No cap_url for APT_PROPERTIES, request for " << avatar_id << " is not sent" << LL_ENDL;
}
return;
}
// Send UDP request
@ -158,8 +156,7 @@ void LLAvatarPropertiesProcessor::sendGenericRequest(const LLUUID& avatar_id, EA
// indicate we're going to make a request
addPendingRequest(avatar_id, type);
std::vector<std::string> strings;
strings.push_back(avatar_id.asString());
std::vector<std::string> strings{ avatar_id.asString() };
send_generic_message(method, strings);
}
@ -171,8 +168,8 @@ void LLAvatarPropertiesProcessor::sendAvatarPropertiesRequestMessage(const LLUUI
msg->newMessageFast(_PREHASH_AvatarPropertiesRequest);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
msg->addUUIDFast(_PREHASH_AgentID, gAgentID);
msg->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
msg->addUUIDFast(_PREHASH_AvatarID, avatar_id);
gAgent.sendReliableMessage();
}
@ -183,24 +180,14 @@ void LLAvatarPropertiesProcessor::initAgentProfileCapRequest(const LLUUID& avata
LLCoros::instance().launch("requestAgentUserInfoCoro",
[cap_url, avatar_id, type]() { requestAvatarPropertiesCoro(cap_url, avatar_id, type); });
}
void LLAvatarPropertiesProcessor::sendAvatarPropertiesRequest(const LLUUID& avatar_id, bool use_cap)
void LLAvatarPropertiesProcessor::sendAvatarPropertiesRequest(const LLUUID& avatar_id)
{
sendRequest(avatar_id, use_cap ? APT_PROPERTIES : APT_PROPERTIES_LEGACY, "AvatarPropertiesRequest");
sendRequest(avatar_id, APT_PROPERTIES, "AvatarPropertiesRequest");
}
void LLAvatarPropertiesProcessor::sendAvatarPicksRequest(const LLUUID& avatar_id)
void LLAvatarPropertiesProcessor::sendAvatarLegacyPropertiesRequest(const LLUUID& avatar_id)
{
sendGenericRequest(avatar_id, APT_PICKS, "avatarpicksrequest");
}
void LLAvatarPropertiesProcessor::sendAvatarNotesRequest(const LLUUID& avatar_id)
{
sendGenericRequest(avatar_id, APT_NOTES, "avatarnotesrequest");
}
void LLAvatarPropertiesProcessor::sendAvatarGroupsRequest(const LLUUID& avatar_id)
{
sendGenericRequest(avatar_id, APT_GROUPS, "avatargroupsrequest");
sendRequest(avatar_id, APT_PROPERTIES_LEGACY, "AvatarPropertiesRequest");
}
void LLAvatarPropertiesProcessor::sendAvatarTexturesRequest(const LLUUID& avatar_id)
@ -276,19 +263,21 @@ std::string LLAvatarPropertiesProcessor::accountType(const LLAvatarData* avatar_
std::string LLAvatarPropertiesProcessor::paymentInfo(const LLAvatarData* avatar_data)
{
// Special accounts like M Linden don't have payment info revealed.
if (!avatar_data->caption_text.empty()) return "";
if (!avatar_data->caption_text.empty())
return "";
// Linden employees don't have payment info revealed
const S32 LINDEN_EMPLOYEE_INDEX = 3;
if (avatar_data->caption_index == LINDEN_EMPLOYEE_INDEX) return "";
constexpr S32 LINDEN_EMPLOYEE_INDEX = 3;
if (avatar_data->caption_index == LINDEN_EMPLOYEE_INDEX)
return "";
BOOL transacted = (avatar_data->flags & AVATAR_TRANSACTED);
BOOL identified = (avatar_data->flags & AVATAR_IDENTIFIED);
bool transacted = (avatar_data->flags & AVATAR_TRANSACTED);
bool identified = (avatar_data->flags & AVATAR_IDENTIFIED);
// Not currently getting set in dataserver/lldataavatar.cpp for privacy considerations
//BOOL age_verified = (avatar_data->flags & AVATAR_AGEVERIFIED);
const char* payment_text;
if(transacted)
if (transacted)
{
payment_text = "PaymentInfoUsed";
}
@ -307,17 +296,19 @@ std::string LLAvatarPropertiesProcessor::paymentInfo(const LLAvatarData* avatar_
bool LLAvatarPropertiesProcessor::hasPaymentInfoOnFile(const LLAvatarData* avatar_data)
{
// Special accounts like M Linden don't have payment info revealed.
if (!avatar_data->caption_text.empty()) return true;
if (!avatar_data->caption_text.empty())
return true;
// Linden employees don't have payment info revealed
const S32 LINDEN_EMPLOYEE_INDEX = 3;
if (avatar_data->caption_index == LINDEN_EMPLOYEE_INDEX) return true;
constexpr S32 LINDEN_EMPLOYEE_INDEX = 3;
if (avatar_data->caption_index == LINDEN_EMPLOYEE_INDEX)
return true;
return ((avatar_data->flags & AVATAR_TRANSACTED) || (avatar_data->flags & AVATAR_IDENTIFIED));
}
// static
void LLAvatarPropertiesProcessor::requestAvatarPropertiesCoro(std::string cap_url, LLUUID agent_id, EAvatarProcessorType type)
void LLAvatarPropertiesProcessor::requestAvatarPropertiesCoro(std::string cap_url, LLUUID avatar_id, EAvatarProcessorType type)
{
LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
@ -328,132 +319,108 @@ void LLAvatarPropertiesProcessor::requestAvatarPropertiesCoro(std::string cap_ur
LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
httpOpts->setFollowRedirects(true);
std::string finalUrl = cap_url + "/" + agent_id.asString();
std::string finalUrl = cap_url + "/" + avatar_id.asString();
LLSD result = httpAdapter->getAndSuspend(httpRequest, finalUrl, httpOpts, httpHeaders);
// Response is being processed, no longer pending is required
getInstance()->removePendingRequest(agent_id, type);
getInstance()->removePendingRequest(avatar_id, type);
LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
if (!status
|| !result.has("id")
|| agent_id != result["id"].asUUID())
|| avatar_id != result["id"].asUUID())
{
LL_WARNS("AvatarProperties") << "Failed to get agent information for id " << agent_id
LL_WARNS("AvatarProperties") << "Failed to get agent information for id " << avatar_id
<< (!status ? " (no HTTP status)" : !result.has("id") ? " (no result.id)" :
std::string(" (result.id=") + result["id"].asUUID().asString() + ")")
<< LL_ENDL;
return;
}
if (type == APT_PROPERTIES)
LLAvatarData avatar_data;
std::string birth_date;
avatar_data.agent_id = gAgentID;
avatar_data.avatar_id = avatar_id;
avatar_data.image_id = result["sl_image_id"].asUUID();
avatar_data.fl_image_id = result["fl_image_id"].asUUID();
avatar_data.partner_id = result["partner_id"].asUUID();
avatar_data.about_text = result["sl_about_text"].asString();
avatar_data.fl_about_text = result["fl_about_text"].asString();
avatar_data.born_on = result["member_since"].asDate();
// TODO: SL-20163 Remove the "has" check when SRV-684 is done
// and the field "hide_age" is included to the http response
avatar_data.hide_age = !result.has("hide_age") || result["hide_age"].asBoolean();
avatar_data.profile_url = getProfileURL(avatar_id.asString());
avatar_data.customer_type = result["customer_type"].asString();
avatar_data.notes = result["notes"].asString();
avatar_data.flags = 0;
// <FS:Zi> FIRE-32184: Online/Offline status not working for non-friends
// if (result["online"].asBoolean())
if (result["online"].isUndefined())
{
LLAvatarData avatar_data;
std::string birth_date;
// <FS:Ansariel> Fix protocol bug
//avatar_data.agent_id = agent_id;
avatar_data.agent_id = gAgentID;
// </FS:Ansariel>
avatar_data.avatar_id = agent_id;
avatar_data.image_id = result["sl_image_id"].asUUID();
avatar_data.fl_image_id = result["fl_image_id"].asUUID();
avatar_data.partner_id = result["partner_id"].asUUID();
avatar_data.about_text = result["sl_about_text"].asString();
avatar_data.fl_about_text = result["fl_about_text"].asString();
avatar_data.born_on = result["member_since"].asDate();
// TODO: SL-20163 Remove the "has" check when SRV-684 is done
// and the field "hide_age" is included to the http response
avatar_data.hide_age = !result.has("hide_age") || result["hide_age"].asBoolean();
avatar_data.profile_url = getProfileURL(agent_id.asString());
avatar_data.customer_type = result["customer_type"].asString();
avatar_data.flags = 0;
if (result["online"].asBoolean())
{
avatar_data.flags |= AVATAR_ONLINE;
}
if (result["allow_publish"].asBoolean())
{
avatar_data.flags |= AVATAR_ALLOW_PUBLISH;
}
if (result["identified"].asBoolean())
{
avatar_data.flags |= AVATAR_IDENTIFIED;
}
if (result["transacted"].asBoolean())
{
avatar_data.flags |= AVATAR_TRANSACTED;
}
avatar_data.caption_index = 0;
if (result.has("charter_member")) // won't be present if "caption" is set
{
avatar_data.caption_index = result["charter_member"].asInteger();
}
else if (result.has("caption"))
{
avatar_data.caption_text = result["caption"].asString();
}
getInstance()->notifyObservers(agent_id, &avatar_data, type);
avatar_data.flags |= AVATAR_ONLINE_UNDEFINED;
}
else if (type == APT_PICKS)
else if (result["online"].asBoolean())
// </FS:Zi>
{
LLAvatarPicks avatar_picks;
avatar_picks.agent_id = agent_id; // Not in use?
avatar_picks.target_id = agent_id;
LLSD picks_array = result["picks"];
for (LLSD::array_const_iterator it = picks_array.beginArray(); it != picks_array.endArray(); ++it)
{
const LLSD& pick_data = *it;
avatar_picks.picks_list.emplace_back(pick_data["id"].asUUID(), pick_data["name"].asString());
}
getInstance()->notifyObservers(agent_id, &avatar_picks, type);
avatar_data.flags |= AVATAR_ONLINE;
}
else if (type == APT_GROUPS)
if (result["allow_publish"].asBoolean())
{
LLAvatarGroups avatar_groups;
avatar_groups.agent_id = agent_id; // Not in use?
avatar_groups.avatar_id = agent_id; // target_id
LLSD groups_array = result["groups"];
for (LLSD::array_const_iterator it = groups_array.beginArray(); it != groups_array.endArray(); ++it)
{
const LLSD& group_info = *it;
LLAvatarGroups::LLGroupData group_data;
group_data.group_powers = 0; // Not in use?
group_data.group_title = group_info["name"].asString(); // Missing data, not in use?
group_data.group_id = group_info["id"].asUUID();
group_data.group_name = group_info["name"].asString();
group_data.group_insignia_id = group_info["image_id"].asUUID();
avatar_groups.group_list.push_back(group_data);
}
getInstance()->notifyObservers(agent_id, &avatar_groups, type);
avatar_data.flags |= AVATAR_ALLOW_PUBLISH;
}
else if (type == APT_NOTES)
if (result["identified"].asBoolean())
{
LLAvatarNotes avatar_notes;
avatar_notes.agent_id = agent_id;
avatar_notes.target_id = agent_id;
avatar_notes.notes = result["notes"].asString();
getInstance()->notifyObservers(agent_id, &avatar_notes, type);
avatar_data.flags |= AVATAR_IDENTIFIED;
}
if (result["transacted"].asBoolean())
{
avatar_data.flags |= AVATAR_TRANSACTED;
}
avatar_data.caption_index = 0;
if (result.has("charter_member")) // won't be present if "caption" is set
{
avatar_data.caption_index = result["charter_member"].asInteger();
}
else if (result.has("caption"))
{
avatar_data.caption_text = result["caption"].asString();
}
// Groups
LLSD groups_array = result["groups"];
for (LLSD::array_const_iterator it = groups_array.beginArray(); it != groups_array.endArray(); ++it)
{
const LLSD& group_info = *it;
LLAvatarData::LLGroupData group_data;
group_data.group_powers = 0; // Not in use?
group_data.group_title = group_info["name"].asString(); // Missing data, not in use?
group_data.group_id = group_info["id"].asUUID();
group_data.group_name = group_info["name"].asString();
group_data.group_insignia_id = group_info["image_id"].asUUID();
avatar_data.group_list.push_back(group_data);
}
// Picks
LLSD picks_array = result["picks"];
for (LLSD::array_const_iterator it = picks_array.beginArray(); it != picks_array.endArray(); ++it)
{
const LLSD& pick_data = *it;
avatar_data.picks_list.emplace_back(pick_data["id"].asUUID(), pick_data["name"].asString());
}
getInstance()->notifyObservers(avatar_id, &avatar_data, type);
}
void LLAvatarPropertiesProcessor::processAvatarPropertiesReply(LLMessageSystem* msg, void**)
void LLAvatarPropertiesProcessor::processAvatarLegacyPropertiesReply(LLMessageSystem* msg, void**)
{
LLAvatarData avatar_data;
std::string birth_date;
@ -472,7 +439,7 @@ void LLAvatarPropertiesProcessor::processAvatarPropertiesReply(LLMessageSystem*
LLDateUtil::dateFromPDTString(avatar_data.born_on, birth_date);
// Since field 'hide_age' is not supported by msg system we'd better hide the age here
// <FS:Ansariel> OpenSim
//avatar_data.hide_age = TRUE;
//avatar_data.hide_age = true;
avatar_data.hide_age = LLGridManager::instance().isInSecondLife();
// </FS:Ansariel>
avatar_data.caption_index = 0;
@ -493,33 +460,6 @@ void LLAvatarPropertiesProcessor::processAvatarPropertiesReply(LLMessageSystem*
self->notifyObservers(avatar_data.avatar_id, &avatar_data, APT_PROPERTIES_LEGACY);
}
void LLAvatarPropertiesProcessor::processAvatarInterestsReply(LLMessageSystem* msg, void**)
{
/*
AvatarInterestsReply is automatically sent by the server in response to the
AvatarPropertiesRequest sent when the panel is opened (in addition to the AvatarPropertiesReply message).
If the interests panel is no longer part of the design (?) we should just register the message
to a handler function that does nothing.
That will suppress the warnings and be compatible with old server versions.
WARNING: LLTemplateMessageReader::decodeData: Message from 216.82.37.237:13000 with no handler function received: AvatarInterestsReply
*/
LLInterestsData interests_data;
msg->getUUIDFast( _PREHASH_AgentData, _PREHASH_AgentID, interests_data.agent_id );
msg->getUUIDFast( _PREHASH_AgentData, _PREHASH_AvatarID, interests_data.avatar_id );
msg->getU32Fast( _PREHASH_PropertiesData, _PREHASH_WantToMask, interests_data.want_to_mask );
msg->getStringFast( _PREHASH_PropertiesData, _PREHASH_WantToText, interests_data.want_to_text );
msg->getU32Fast( _PREHASH_PropertiesData, _PREHASH_SkillsMask, interests_data.skills_mask );
msg->getStringFast( _PREHASH_PropertiesData, _PREHASH_SkillsText, interests_data.skills_text );
msg->getString( _PREHASH_PropertiesData, _PREHASH_LanguagesText, interests_data.languages_text );
LLAvatarPropertiesProcessor* self = getInstance();
// Request processed, no longer pending
self->removePendingRequest(interests_data.avatar_id, APT_INTERESTS_INFO);
self->notifyObservers(interests_data.avatar_id, &interests_data, APT_INTERESTS_INFO);
}
void LLAvatarPropertiesProcessor::processAvatarClassifiedsReply(LLMessageSystem* msg, void**)
{
LLAvatarClassifieds classifieds;
@ -536,7 +476,7 @@ void LLAvatarPropertiesProcessor::processAvatarClassifiedsReply(LLMessageSystem*
msg->getUUID(_PREHASH_Data, _PREHASH_ClassifiedID, data.classified_id, n);
msg->getString(_PREHASH_Data, _PREHASH_Name, data.name, n);
classifieds.classifieds_list.push_back(data);
classifieds.classifieds_list.emplace_back(data);
}
LLAvatarPropertiesProcessor* self = getInstance();
@ -577,6 +517,241 @@ void LLAvatarPropertiesProcessor::processClassifiedInfoReply(LLMessageSystem* ms
// </FS:CR>
}
void LLAvatarPropertiesProcessor::processPickInfoReply(LLMessageSystem* msg, void**)
{
LLPickData pick_data;
// Extract the agent id and verify the message is for this
// client.
msg->getUUID(_PREHASH_AgentData, _PREHASH_AgentID, pick_data.agent_id );
msg->getUUID(_PREHASH_Data, _PREHASH_PickID, pick_data.pick_id);
msg->getUUID(_PREHASH_Data, _PREHASH_CreatorID, pick_data.creator_id);
// ** top_pick should be deleted, not being used anymore - angela
msg->getBOOL(_PREHASH_Data, _PREHASH_TopPick, pick_data.top_pick);
msg->getUUID(_PREHASH_Data, _PREHASH_ParcelID, pick_data.parcel_id);
msg->getString(_PREHASH_Data, _PREHASH_Name, pick_data.name);
msg->getString(_PREHASH_Data, _PREHASH_Desc, pick_data.desc);
msg->getUUID(_PREHASH_Data, _PREHASH_SnapshotID, pick_data.snapshot_id);
msg->getString(_PREHASH_Data, _PREHASH_User, pick_data.user_name);
msg->getString(_PREHASH_Data, _PREHASH_OriginalName, pick_data.original_name);
msg->getString(_PREHASH_Data, _PREHASH_SimName, pick_data.sim_name);
msg->getVector3d(_PREHASH_Data, _PREHASH_PosGlobal, pick_data.pos_global);
msg->getS32(_PREHASH_Data, _PREHASH_SortOrder, pick_data.sort_order);
msg->getBOOL(_PREHASH_Data, _PREHASH_Enabled, pick_data.enabled);
LLAvatarPropertiesProcessor* self = getInstance();
// don't need to remove pending request as we don't track pick info
self->notifyObservers(pick_data.creator_id, &pick_data, APT_PICK_INFO);
}
void LLAvatarPropertiesProcessor::notifyObservers(const LLUUID& id, void* data, EAvatarProcessorType type)
{
// Copy the map (because observers may delete themselves when updated?)
LLAvatarPropertiesProcessor::observer_multimap_t observers = mObservers;
for (const auto& [agent_id, observer] : observers)
{
// only notify observers for the same agent, or if the observer
// didn't know the agent ID and passed a NULL id.
if (agent_id == id || agent_id.isNull())
{
observer->processProperties(data, type);
}
}
}
void LLAvatarPropertiesProcessor::sendFriendRights(const LLUUID& avatar_id, S32 rights)
{
if(!avatar_id.isNull())
{
LLMessageSystem* msg = gMessageSystem;
// setup message header
msg->newMessageFast(_PREHASH_GrantUserRights);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUID(_PREHASH_AgentID, gAgentID);
msg->addUUID(_PREHASH_SessionID, gAgentSessionID);
msg->nextBlockFast(_PREHASH_Rights);
msg->addUUID(_PREHASH_AgentRelated, avatar_id);
msg->addS32(_PREHASH_RelatedRights, rights);
gAgent.sendReliableMessage();
}
}
void LLAvatarPropertiesProcessor::sendPickDelete( const LLUUID& pick_id )
{
LLMessageSystem* msg = gMessageSystem;
msg->newMessage(_PREHASH_PickDelete);
msg->nextBlock(_PREHASH_AgentData);
msg->addUUID(_PREHASH_AgentID, gAgentID);
msg->addUUID(_PREHASH_SessionID, gAgentSessionID);
msg->nextBlock(_PREHASH_Data);
msg->addUUID(_PREHASH_PickID, pick_id);
gAgent.sendReliableMessage();
LLAgentPicksInfo::getInstance()->requestNumberOfPicks();
LLAgentPicksInfo::getInstance()->decrementNumberOfPicks();
}
void LLAvatarPropertiesProcessor::sendClassifiedDelete(const LLUUID& classified_id)
{
LLMessageSystem* msg = gMessageSystem;
msg->newMessage(_PREHASH_ClassifiedDelete);
msg->nextBlock(_PREHASH_AgentData);
msg->addUUID(_PREHASH_AgentID, gAgentID);
msg->addUUID(_PREHASH_SessionID, gAgentSessionID);
msg->nextBlock(_PREHASH_Data);
msg->addUUID(_PREHASH_ClassifiedID, classified_id);
gAgent.sendReliableMessage();
}
void LLAvatarPropertiesProcessor::sendPickInfoUpdate(const LLPickData* new_pick)
{
if (!new_pick)
return;
LLMessageSystem* msg = gMessageSystem;
msg->newMessage(_PREHASH_PickInfoUpdate);
msg->nextBlock(_PREHASH_AgentData);
msg->addUUID(_PREHASH_AgentID, gAgentID);
msg->addUUID(_PREHASH_SessionID, gAgentSessionID);
msg->nextBlock(_PREHASH_Data);
msg->addUUID(_PREHASH_PickID, new_pick->pick_id);
msg->addUUID(_PREHASH_CreatorID, new_pick->creator_id);
//legacy var need to be deleted
msg->addBOOL(_PREHASH_TopPick, FALSE);
// fills in on simulator if null
msg->addUUID(_PREHASH_ParcelID, new_pick->parcel_id);
msg->addString(_PREHASH_Name, new_pick->name);
msg->addString(_PREHASH_Desc, new_pick->desc);
msg->addUUID(_PREHASH_SnapshotID, new_pick->snapshot_id);
msg->addVector3d(_PREHASH_PosGlobal, new_pick->pos_global);
// Only top picks have a sort order
msg->addS32(_PREHASH_SortOrder, 0);
msg->addBOOL(_PREHASH_Enabled, new_pick->enabled);
gAgent.sendReliableMessage();
LLAgentPicksInfo::getInstance()->requestNumberOfPicks();
}
void LLAvatarPropertiesProcessor::sendClassifiedInfoUpdate(const LLAvatarClassifiedInfo* c_data)
{
if(!c_data)
{
return;
}
LLMessageSystem* msg = gMessageSystem;
msg->newMessage(_PREHASH_ClassifiedInfoUpdate);
msg->nextBlock(_PREHASH_AgentData);
msg->addUUID(_PREHASH_AgentID, gAgentID);
msg->addUUID(_PREHASH_SessionID, gAgentSessionID);
msg->nextBlock(_PREHASH_Data);
msg->addUUID(_PREHASH_ClassifiedID, c_data->classified_id);
msg->addU32(_PREHASH_Category, c_data->category);
msg->addString(_PREHASH_Name, c_data->name);
msg->addString(_PREHASH_Desc, c_data->description);
msg->addUUID(_PREHASH_ParcelID, c_data->parcel_id);
msg->addU32(_PREHASH_ParentEstate, 0);
msg->addUUID(_PREHASH_SnapshotID, c_data->snapshot_id);
msg->addVector3d(_PREHASH_PosGlobal, c_data->pos_global);
msg->addU8(_PREHASH_ClassifiedFlags, c_data->flags);
msg->addS32(_PREHASH_PriceForListing, c_data->price_for_listing);
gAgent.sendReliableMessage();
}
void LLAvatarPropertiesProcessor::sendPickInfoRequest(const LLUUID& creator_id, const LLUUID& pick_id)
{
// Must ask for a pick based on the creator id because
// the pick database is distributed to the inventory cluster. JC
std::vector<std::string> request_params{ creator_id.asString(), pick_id.asString() };
send_generic_message("pickinforequest", request_params);
}
void LLAvatarPropertiesProcessor::sendClassifiedInfoRequest(const LLUUID& classified_id)
{
LLMessageSystem* msg = gMessageSystem;
msg->newMessage(_PREHASH_ClassifiedInfoRequest);
msg->nextBlock(_PREHASH_AgentData);
msg->addUUID(_PREHASH_AgentID, gAgentID);
msg->addUUID(_PREHASH_SessionID, gAgentSessionID);
msg->nextBlock(_PREHASH_Data);
msg->addUUID(_PREHASH_ClassifiedID, classified_id);
gAgent.sendReliableMessage();
}
bool LLAvatarPropertiesProcessor::isPendingRequest(const LLUUID& avatar_id, EAvatarProcessorType type)
{
timestamp_map_t::key_type key = std::make_pair(avatar_id, type);
timestamp_map_t::iterator it = mRequestTimestamps.find(key);
// Is this a new request?
if (it == mRequestTimestamps.end()) return false;
// We found a request, check if it has timed out
U32 now = time(nullptr);
const U32 REQUEST_EXPIRE_SECS = 5;
U32 expires = it->second + REQUEST_EXPIRE_SECS;
// Request is still pending if it hasn't expired yet
// *NOTE: Expired requests will accumulate in this map, but they are rare,
// the data is small, and they will be updated if the same data is
// re-requested
return (now < expires);
}
void LLAvatarPropertiesProcessor::addPendingRequest(const LLUUID& avatar_id, EAvatarProcessorType type)
{
timestamp_map_t::key_type key = std::make_pair(avatar_id, type);
U32 now = time(nullptr);
// Add or update existing (expired) request
mRequestTimestamps[ key ] = now;
}
void LLAvatarPropertiesProcessor::removePendingRequest(const LLUUID& avatar_id, EAvatarProcessorType type)
{
timestamp_map_t::key_type key = std::make_pair(avatar_id, type);
mRequestTimestamps.erase(key);
}
// <FS> OpenSim
void LLAvatarPropertiesProcessor::sendAvatarPicksRequest(const LLUUID& avatar_id)
{
sendGenericRequest(avatar_id, APT_PICKS, "avatarpicksrequest");
}
void LLAvatarPropertiesProcessor::sendAvatarNotesRequest(const LLUUID& avatar_id)
{
sendGenericRequest(avatar_id, APT_NOTES, "avatarnotesrequest");
}
void LLAvatarPropertiesProcessor::sendAvatarGroupsRequest(const LLUUID& avatar_id)
{
sendGenericRequest(avatar_id, APT_GROUPS, "avatargroupsrequest");
}
void LLAvatarPropertiesProcessor::processAvatarNotesReply(LLMessageSystem* msg, void**)
{
@ -615,36 +790,6 @@ void LLAvatarPropertiesProcessor::processAvatarPicksReply(LLMessageSystem* msg,
self->notifyObservers(avatar_picks.target_id,&avatar_picks,APT_PICKS);
}
void LLAvatarPropertiesProcessor::processPickInfoReply(LLMessageSystem* msg, void**)
{
LLPickData pick_data;
// Extract the agent id and verify the message is for this
// client.
msg->getUUID(_PREHASH_AgentData, _PREHASH_AgentID, pick_data.agent_id );
msg->getUUID(_PREHASH_Data, _PREHASH_PickID, pick_data.pick_id);
msg->getUUID(_PREHASH_Data, _PREHASH_CreatorID, pick_data.creator_id);
// ** top_pick should be deleted, not being used anymore - angela
msg->getBOOL(_PREHASH_Data, _PREHASH_TopPick, pick_data.top_pick);
msg->getUUID(_PREHASH_Data, _PREHASH_ParcelID, pick_data.parcel_id);
msg->getString(_PREHASH_Data, _PREHASH_Name, pick_data.name);
msg->getString(_PREHASH_Data, _PREHASH_Desc, pick_data.desc);
msg->getUUID(_PREHASH_Data, _PREHASH_SnapshotID, pick_data.snapshot_id);
msg->getString(_PREHASH_Data, _PREHASH_User, pick_data.user_name);
msg->getString(_PREHASH_Data, _PREHASH_OriginalName, pick_data.original_name);
msg->getString(_PREHASH_Data, _PREHASH_SimName, pick_data.sim_name);
msg->getVector3d(_PREHASH_Data, _PREHASH_PosGlobal, pick_data.pos_global);
msg->getS32(_PREHASH_Data, _PREHASH_SortOrder, pick_data.sort_order);
msg->getBOOL(_PREHASH_Data, _PREHASH_Enabled, pick_data.enabled);
LLAvatarPropertiesProcessor* self = getInstance();
// don't need to remove pending request as we don't track pick info
self->notifyObservers(pick_data.creator_id, &pick_data, APT_PICK_INFO);
}
void LLAvatarPropertiesProcessor::processAvatarGroupsReply(LLMessageSystem* msg, void**)
{
LLAvatarGroups avatar_groups;
@ -670,44 +815,6 @@ void LLAvatarPropertiesProcessor::processAvatarGroupsReply(LLMessageSystem* msg,
self->notifyObservers(avatar_groups.avatar_id,&avatar_groups,APT_GROUPS);
}
void LLAvatarPropertiesProcessor::notifyObservers(const LLUUID& id, void* data, EAvatarProcessorType type)
{
// Copy the map (because observers may delete themselves when updated?)
LLAvatarPropertiesProcessor::observer_multimap_t observers = mObservers;
observer_multimap_t::iterator oi = observers.begin();
observer_multimap_t::iterator end = observers.end();
for (; oi != end; ++oi)
{
// only notify observers for the same agent, or if the observer
// didn't know the agent ID and passed a NULL id.
const LLUUID &agent_id = oi->first;
if (agent_id == id || agent_id.isNull())
{
oi->second->processProperties(data, type);
}
}
}
void LLAvatarPropertiesProcessor::sendFriendRights(const LLUUID& avatar_id, S32 rights)
{
if(!avatar_id.isNull())
{
LLMessageSystem* msg = gMessageSystem;
// setup message header
msg->newMessageFast(_PREHASH_GrantUserRights);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUID(_PREHASH_AgentID, gAgent.getID());
msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID());
msg->nextBlockFast(_PREHASH_Rights);
msg->addUUID(_PREHASH_AgentRelated, avatar_id);
msg->addS32(_PREHASH_RelatedRights, rights);
gAgent.sendReliableMessage();
}
}
void LLAvatarPropertiesProcessor::sendNotes(const LLUUID& avatar_id, const std::string notes)
{
@ -729,181 +836,4 @@ void LLAvatarPropertiesProcessor::sendNotes(const LLUUID& avatar_id, const std::
}
}
void LLAvatarPropertiesProcessor::sendPickDelete( const LLUUID& pick_id )
{
LLMessageSystem* msg = gMessageSystem;
msg->newMessage(_PREHASH_PickDelete);
msg->nextBlock(_PREHASH_AgentData);
msg->addUUID(_PREHASH_AgentID, gAgent.getID());
msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID());
msg->nextBlock(_PREHASH_Data);
msg->addUUID(_PREHASH_PickID, pick_id);
gAgent.sendReliableMessage();
LLAgentPicksInfo::getInstance()->requestNumberOfPicks();
LLAgentPicksInfo::getInstance()->decrementNumberOfPicks();
}
void LLAvatarPropertiesProcessor::sendClassifiedDelete(const LLUUID& classified_id)
{
LLMessageSystem* msg = gMessageSystem;
msg->newMessage(_PREHASH_ClassifiedDelete);
msg->nextBlock(_PREHASH_AgentData);
msg->addUUID(_PREHASH_AgentID, gAgent.getID());
msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID());
msg->nextBlock(_PREHASH_Data);
msg->addUUID(_PREHASH_ClassifiedID, classified_id);
gAgent.sendReliableMessage();
}
void LLAvatarPropertiesProcessor::sendInterestsInfoUpdate(const LLInterestsData* interests_data)
{
if(!interests_data)
{
return;
}
LLMessageSystem* msg = gMessageSystem;
msg->newMessage(_PREHASH_AvatarInterestsUpdate);
msg->nextBlockFast( _PREHASH_AgentData);
msg->addUUIDFast( _PREHASH_AgentID, gAgent.getID() );
msg->addUUIDFast( _PREHASH_SessionID, gAgent.getSessionID() );
msg->nextBlockFast( _PREHASH_PropertiesData);
msg->addU32Fast( _PREHASH_WantToMask, interests_data->want_to_mask);
msg->addStringFast( _PREHASH_WantToText, interests_data->want_to_text);
msg->addU32Fast( _PREHASH_SkillsMask, interests_data->skills_mask);
msg->addStringFast( _PREHASH_SkillsText, interests_data->skills_text);
msg->addString( _PREHASH_LanguagesText, interests_data->languages_text);
gAgent.sendReliableMessage();
}
void LLAvatarPropertiesProcessor::sendPickInfoUpdate(const LLPickData* new_pick)
{
if (!new_pick) return;
LLMessageSystem* msg = gMessageSystem;
msg->newMessage(_PREHASH_PickInfoUpdate);
msg->nextBlock(_PREHASH_AgentData);
msg->addUUID(_PREHASH_AgentID, gAgent.getID());
msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID());
msg->nextBlock(_PREHASH_Data);
msg->addUUID(_PREHASH_PickID, new_pick->pick_id);
msg->addUUID(_PREHASH_CreatorID, new_pick->creator_id);
//legacy var need to be deleted
msg->addBOOL(_PREHASH_TopPick, FALSE);
// fills in on simulator if null
msg->addUUID(_PREHASH_ParcelID, new_pick->parcel_id);
msg->addString(_PREHASH_Name, new_pick->name);
msg->addString(_PREHASH_Desc, new_pick->desc);
msg->addUUID(_PREHASH_SnapshotID, new_pick->snapshot_id);
msg->addVector3d(_PREHASH_PosGlobal, new_pick->pos_global);
// Only top picks have a sort order
msg->addS32(_PREHASH_SortOrder, 0);
msg->addBOOL(_PREHASH_Enabled, new_pick->enabled);
gAgent.sendReliableMessage();
LLAgentPicksInfo::getInstance()->requestNumberOfPicks();
}
void LLAvatarPropertiesProcessor::sendClassifiedInfoUpdate(const LLAvatarClassifiedInfo* c_data)
{
if(!c_data)
{
return;
}
LLMessageSystem* msg = gMessageSystem;
msg->newMessage(_PREHASH_ClassifiedInfoUpdate);
msg->nextBlock(_PREHASH_AgentData);
msg->addUUID(_PREHASH_AgentID, gAgent.getID());
msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID());
msg->nextBlock(_PREHASH_Data);
msg->addUUID(_PREHASH_ClassifiedID, c_data->classified_id);
msg->addU32(_PREHASH_Category, c_data->category);
msg->addString(_PREHASH_Name, c_data->name);
msg->addString(_PREHASH_Desc, c_data->description);
msg->addUUID(_PREHASH_ParcelID, c_data->parcel_id);
msg->addU32(_PREHASH_ParentEstate, 0);
msg->addUUID(_PREHASH_SnapshotID, c_data->snapshot_id);
msg->addVector3d(_PREHASH_PosGlobal, c_data->pos_global);
msg->addU8(_PREHASH_ClassifiedFlags, c_data->flags);
msg->addS32(_PREHASH_PriceForListing, c_data->price_for_listing);
gAgent.sendReliableMessage();
}
void LLAvatarPropertiesProcessor::sendPickInfoRequest(const LLUUID& creator_id, const LLUUID& pick_id)
{
// Must ask for a pick based on the creator id because
// the pick database is distributed to the inventory cluster. JC
std::vector<std::string> request_params;
request_params.push_back(creator_id.asString() );
request_params.push_back(pick_id.asString() );
send_generic_message("pickinforequest", request_params);
}
void LLAvatarPropertiesProcessor::sendClassifiedInfoRequest(const LLUUID& classified_id)
{
LLMessageSystem* msg = gMessageSystem;
msg->newMessage(_PREHASH_ClassifiedInfoRequest);
msg->nextBlock(_PREHASH_AgentData);
msg->addUUID(_PREHASH_AgentID, gAgent.getID());
msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID());
msg->nextBlock(_PREHASH_Data);
msg->addUUID(_PREHASH_ClassifiedID, classified_id);
gAgent.sendReliableMessage();
}
bool LLAvatarPropertiesProcessor::isPendingRequest(const LLUUID& avatar_id, EAvatarProcessorType type)
{
timestamp_map_t::key_type key = std::make_pair(avatar_id, type);
timestamp_map_t::iterator it = mRequestTimestamps.find(key);
// Is this a new request?
if (it == mRequestTimestamps.end()) return false;
// We found a request, check if it has timed out
U32 now = time(NULL);
const U32 REQUEST_EXPIRE_SECS = 5;
U32 expires = it->second + REQUEST_EXPIRE_SECS;
// Request is still pending if it hasn't expired yet
// *NOTE: Expired requests will accumulate in this map, but they are rare,
// the data is small, and they will be updated if the same data is
// re-requested
return (now < expires);
}
void LLAvatarPropertiesProcessor::addPendingRequest(const LLUUID& avatar_id, EAvatarProcessorType type)
{
timestamp_map_t::key_type key = std::make_pair(avatar_id, type);
U32 now = time(NULL);
// Add or update existing (expired) request
mRequestTimestamps[ key ] = now;
}
void LLAvatarPropertiesProcessor::removePendingRequest(const LLUUID& avatar_id, EAvatarProcessorType type)
{
timestamp_map_t::key_type key = std::make_pair(avatar_id, type);
mRequestTimestamps.erase(key);
}
// </FS>

View File

@ -40,6 +40,7 @@ const U32 AVATAR_IDENTIFIED = 0x1 << 2; // whether avatar has provided paymen
const U32 AVATAR_TRANSACTED = 0x1 << 3; // whether avatar has actively used payment info
const U32 AVATAR_ONLINE = 0x1 << 4; // the online status of this avatar, if known.
const U32 AVATAR_AGEVERIFIED = 0x1 << 5; // whether avatar has been age-verified
const U32 AVATAR_ONLINE_UNDEFINED = 0x1 << 31; // <FS:Zi> FIRE-32184: Online/Offline status not working for non-friends
/*
*TODO Vadim: This needs some refactoring:
@ -50,56 +51,77 @@ class LLMessageSystem;
enum EAvatarProcessorType
{
APT_PROPERTIES_LEGACY, // APT_PROPERTIES via udp request
APT_PROPERTIES_LEGACY, // APT_PROPERTIES via udp request (Truncates data!!!)
APT_PROPERTIES, // APT_PROPERTIES via http request
// <FS> OpenSim
APT_NOTES,
APT_GROUPS,
APT_PICKS,
// </FS>
APT_PICK_INFO,
APT_TEXTURES,
APT_INTERESTS_INFO,
APT_CLASSIFIEDS,
APT_CLASSIFIED_INFO
};
struct LLInterestsData
// legacy data is supposed to match AvatarPropertiesReply,
// but it is obsolete, fields like about_text will truncate
// data, if you need them, use AgenProfile cap.
// Todo: remove it once once icon ids get moved elsewhere,
// since AgentProfile is too large for bulk icon requests
struct LLAvatarLegacyData
{
LLUUID agent_id;
LLUUID avatar_id; //target id
U32 want_to_mask;
std::string want_to_text;
U32 skills_mask;
std::string skills_text;
std::string languages_text;
LLUUID agent_id;
LLUUID avatar_id; //target id
LLUUID image_id;
LLUUID fl_image_id;
LLUUID partner_id;
std::string about_text;
std::string fl_about_text;
LLDate born_on;
std::string profile_url;
U8 caption_index;
std::string caption_text;
std::string customer_type;
U32 flags;
};
struct LLAvatarData
{
LLUUID agent_id;
LLUUID avatar_id; //target id
LLUUID image_id;
LLUUID fl_image_id;
LLUUID partner_id;
std::string about_text;
std::string fl_about_text;
LLDate born_on;
std::string profile_url;
U8 caption_index;
std::string caption_text;
std::string customer_type;
U32 flags;
bool hide_age;
LLUUID agent_id;
LLUUID avatar_id; //target id
LLUUID image_id;
LLUUID fl_image_id;
LLUUID partner_id;
std::string about_text;
std::string fl_about_text;
LLDate born_on;
std::string profile_url;
U8 caption_index;
std::string caption_text;
std::string customer_type;
U32 flags;
bool hide_age;
std::string notes;
struct LLGroupData;
typedef std::list<LLGroupData> group_list_t;
group_list_t group_list;
typedef std::pair<LLUUID, std::string> pick_data_t;
typedef std::list< pick_data_t> picks_list_t;
picks_list_t picks_list;
BOOL allow_publish; // <FS:Ansariel> UDP profiles
};
struct LLAvatarPicks
struct LLAvatarData::LLGroupData
{
LLUUID agent_id;
LLUUID target_id; //target id
typedef std::pair<LLUUID,std::string> pick_data_t;
typedef std::list< pick_data_t> picks_list_t;
picks_list_t picks_list;
U64 group_powers;
BOOL accept_notices;
std::string group_title;
LLUUID group_id;
std::string group_name;
LLUUID group_insignia_id;
};
struct LLPickData
@ -123,7 +145,17 @@ struct LLPickData
//used only in write (update) requests
LLUUID session_id;
};
// <FS> OpenSim
struct LLAvatarPicks
{
LLUUID agent_id;
LLUUID target_id; //target id
typedef std::pair<LLUUID,std::string> pick_data_t;
typedef std::list< pick_data_t> picks_list_t;
picks_list_t picks_list;
};
struct LLAvatarNotes
@ -154,6 +186,7 @@ struct LLAvatarGroups
LLUUID group_insignia_id;
};
};
// </FS>
struct LLAvatarClassifieds
{
@ -212,12 +245,16 @@ public:
// Request various types of avatar data. Duplicate requests will be
// suppressed while waiting for a response from the network.
void sendAvatarPropertiesRequest(const LLUUID& avatar_id, bool use_cap = false);
void sendAvatarPropertiesRequest(const LLUUID& avatar_id);
void sendAvatarLegacyPropertiesRequest(const LLUUID& avatar_id);
void sendAvatarTexturesRequest(const LLUUID& avatar_id);
void sendAvatarClassifiedsRequest(const LLUUID& avatar_id);
// <FS> OpenSim
void sendAvatarPicksRequest(const LLUUID& avatar_id);
void sendAvatarNotesRequest(const LLUUID& avatar_id);
void sendAvatarGroupsRequest(const LLUUID& avatar_id);
void sendAvatarTexturesRequest(const LLUUID& avatar_id);
void sendAvatarClassifiedsRequest(const LLUUID& avatar_id);
// </FS>
// Duplicate pick info requests are not suppressed.
void sendPickInfoRequest(const LLUUID& creator_id, const LLUUID& pick_id);
@ -232,14 +269,12 @@ public:
void sendFriendRights(const LLUUID& avatar_id, S32 rights);
// <FS> OpenSim
void sendNotes(const LLUUID& avatar_id, const std::string notes);
void sendPickDelete(const LLUUID& pick_id);
void sendClassifiedDelete(const LLUUID& classified_id);
void sendInterestsInfoUpdate(const LLInterestsData* interests_data);
// Returns translated, human readable string for account type, such
// as "Resident" or "Linden Employee". Used for profiles, inspectors.
static std::string accountType(const LLAvatarData* avatar_data);
@ -251,22 +286,20 @@ public:
static bool hasPaymentInfoOnFile(const LLAvatarData* avatar_data);
static void requestAvatarPropertiesCoro(std::string cap_url, LLUUID agent_id, EAvatarProcessorType type);
static void requestAvatarPropertiesCoro(std::string cap_url, LLUUID avatar_id, EAvatarProcessorType type);
static void processAvatarPropertiesReply(LLMessageSystem* msg, void**);
static void processAvatarInterestsReply(LLMessageSystem* msg, void**);
// Processing of UDP variant of properties, truncates certain fields!
static void processAvatarLegacyPropertiesReply(LLMessageSystem* msg, void**);
static void processAvatarClassifiedsReply(LLMessageSystem* msg, void**);
static void processClassifiedInfoReply(LLMessageSystem* msg, void**);
// <FS> OpenSim
static void processAvatarGroupsReply(LLMessageSystem* msg, void**);
static void processAvatarNotesReply(LLMessageSystem* msg, void**);
static void processAvatarPicksReply(LLMessageSystem* msg, void**);
// </FS>
static void processPickInfoReply(LLMessageSystem* msg, void**);
protected:

View File

@ -656,6 +656,9 @@ std::unique_ptr<std::vector<std::string>> LLFilePicker::navOpenFilterProc(ELoadF
switch(filter)
{
case FFLOAD_ALL:
case FFLOAD_EXE:
allowedv->push_back("app");
allowedv->push_back("exe");
allowedv->push_back("wav");
allowedv->push_back("bvh");
allowedv->push_back("anim");
@ -679,9 +682,6 @@ std::unique_ptr<std::vector<std::string>> LLFilePicker::navOpenFilterProc(ELoadF
allowedv->push_back("tpic");
allowedv->push_back("png");
break;
case FFLOAD_EXE:
allowedv->push_back("app");
allowedv->push_back("exe");
break;
case FFLOAD_WAV:
allowedv->push_back("wav");

View File

@ -404,7 +404,13 @@ LLFetchAvatarPaymentInfo::LLFetchAvatarPaymentInfo(bool has_target, const std::s
processor->addObserver(mAvatarID, this);
// send a request (duplicates will be suppressed inside the avatar
// properties processor)
processor->sendAvatarPropertiesRequest(mAvatarID);
// <FS> OpenSim
//processor->sendAvatarPropertiesRequest(mAvatarID);
if (!gAgent.getRegionCapability("AgentProfile").empty())
processor->sendAvatarPropertiesRequest(mAvatarID);
else
processor->sendAvatarLegacyPropertiesRequest(mAvatarID);
// </FS>
}
LLFetchAvatarPaymentInfo::~LLFetchAvatarPaymentInfo()
@ -414,7 +420,7 @@ LLFetchAvatarPaymentInfo::~LLFetchAvatarPaymentInfo()
void LLFetchAvatarPaymentInfo::processProperties(void* data, EAvatarProcessorType type)
{
if (data && type == APT_PROPERTIES_LEGACY)
if (data && (type == APT_PROPERTIES || type == APT_PROPERTIES_LEGACY))
{
LLAvatarData* avatar_data = static_cast<LLAvatarData*>(data);
LLFloaterBuyCurrency::handleBuyCurrency(LLAvatarPropertiesProcessor::hasPaymentInfoOnFile(avatar_data), mHasTarget, mName, mPrice);

View File

@ -327,6 +327,13 @@ void LLFloaterJoystick::refresh()
initFromSettings();
}
bool LLFloaterJoystick::addDeviceCallback(std::string &name, LLSD& value, void* userdata)
{
LLFloaterJoystick * floater = (LLFloaterJoystick*)userdata;
floater->mJoysticksCombo->add(name, value, ADD_BOTTOM, 1);
return false; // keep searching
}
void LLFloaterJoystick::addDevice(std::string &name, LLSD& value)
{
mJoysticksCombo->add(name, value, ADD_BOTTOM, 1);
@ -341,19 +348,21 @@ void LLFloaterJoystick::refreshListOfDevices()
mHasDeviceList = false;
void* win_calback = nullptr;
// di8_devices_callback callback is immediate and happens in scope of getInputDevices()
#if LL_WINDOWS && !LL_MESA_HEADLESS
// space navigator is marked as DI8DEVCLASS_GAMECTRL in ndof lib
U32 device_type = DI8DEVCLASS_GAMECTRL;
void* callback = &di8_list_devices_callback;
#else
// MAC doesn't support device search yet
// On MAC there is an ndof_idsearch and it is possible to specify product
// and manufacturer in NDOF_Device for ndof_init_first to pick specific one
win_calback = di8_list_devices_callback;
#elif LL_DARWIN
U32 device_type = 0;
#else
// On MAC it is possible to specify product
// and manufacturer in NDOF_Device for
// ndof_init_first to pick specific device
U32 device_type = 0;
void* callback = NULL;
#endif
if (gViewerWindow->getWindow()->getInputDevices(device_type, callback, this))
if (gViewerWindow->getWindow()->getInputDevices(device_type, addDeviceCallback, win_calback, this))
{
mHasDeviceList = true;
}
@ -495,10 +504,11 @@ void LLFloaterJoystick::onCommitJoystickEnabled(LLUICtrl*, void *joy_panel)
joystick->toggleFlycam();
}
}
std::string device_id = LLViewerJoystick::getInstance()->getDeviceUUIDString();
gSavedSettings.setString("JoystickDeviceUUID", device_id);
LL_DEBUGS("Joystick") << "Selected " << device_id << " as joystick." << LL_ENDL;
LLViewerJoystick::getInstance()->saveDeviceIdToSettings();
std::string device_string = LLViewerJoystick::getInstance()->getDeviceUUIDString();
LL_DEBUGS("Joystick") << "Selected " << device_string << " as joystick." << LL_ENDL;
self->refreshListOfDevices();

View File

@ -50,6 +50,7 @@ public:
virtual void draw();
static void setSNDefaults();
static bool addDeviceCallback(std::string &name, LLSD& value, void* userdata);
void addDevice(std::string &name, LLSD& value);
protected:

View File

@ -557,7 +557,7 @@ void LLFloaterPreference::processProperties( void* pData, EAvatarProcessorType t
{
if ( APT_PROPERTIES_LEGACY == type )
{
const LLAvatarData* pAvatarData = static_cast<const LLAvatarData*>( pData );
const LLAvatarLegacyData* pAvatarData = static_cast<const LLAvatarLegacyData*>( pData );
if (pAvatarData && (gAgent.getID() == pAvatarData->avatar_id) && (pAvatarData->avatar_id != LLUUID::null))
{
mAllowPublish = (bool)(pAvatarData->flags & AVATAR_ALLOW_PUBLISH);
@ -1108,7 +1108,7 @@ void LLFloaterPreference::onOpen(const LLSD& key)
(gAgent.isMature() || gAgent.isGodlike());
LLComboBox* maturity_combo = getChild<LLComboBox>("maturity_desired_combobox");
LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest( gAgent.getID() );
LLAvatarPropertiesProcessor::getInstance()->sendAvatarLegacyPropertiesRequest( gAgent.getID() );
if (can_choose_maturity)
{
// if they're not adult or a god, they shouldn't see the adult selection, so delete it

View File

@ -196,9 +196,11 @@ public:
// send a request (duplicates will be suppressed inside the avatar
// properties processor)
// <FS:Ansariel> OpenSim
//processor->sendAvatarPropertiesRequest(mAvatarID, true);
const bool use_cap = LLGridManager::instance().isInSecondLife() ? true : !gAgent.getRegionCapability("AgentProfile").empty();
processor->sendAvatarPropertiesRequest(mAvatarID, use_cap);
//processor->sendAvatarPropertiesRequest(mAvatarID);
if (LLGridManager::instance().isInSecondLife() || !gAgent.getRegionCapability("AgentProfile").empty())
processor->sendAvatarPropertiesRequest(mAvatarID);
else
processor->sendAvatarLegacyPropertiesRequest(mAvatarID);
// </FS:Ansariel>
}

View File

@ -210,3 +210,16 @@ void LLPanelProfilePropertiesProcessorTab::setAvatarId(const LLUUID & avatar_id)
LLAvatarPropertiesProcessor::getInstance()->addObserver(getAvatarId(), this);
}
}
void LLPanelProfilePropertiesProcessorTab::updateData()
{
LLUUID avatar_id = getAvatarId();
// <FS> OpenSim
//if (!getStarted() && avatar_id.notNull())
if (!getStarted() && avatar_id.notNull() && !gAgent.getRegionCapability("AgentProfile").empty())
// </FS>
{
setIsLoading();
LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(getAvatarId());
}
}

View File

@ -97,12 +97,12 @@ public:
/**
* Sends update data request to server.
*/
virtual void updateData() {};
virtual void updateData(){};
/**
* Clears panel data if viewing avatar info for first time and sends update data request.
*/
virtual void onOpen(const LLSD& key);
virtual void onOpen(const LLSD& key) override;
/**
* Clears all data received from server.
@ -160,12 +160,14 @@ public:
LLPanelProfilePropertiesProcessorTab();
~LLPanelProfilePropertiesProcessorTab();
/*virtual*/ void setAvatarId(const LLUUID& avatar_id);
void setAvatarId(const LLUUID& avatar_id) override;
void updateData() override;
/**
* Processes data received from server via LLAvatarPropertiesObserver.
*/
virtual void processProperties(void* data, EAvatarProcessorType type) = 0;
virtual void processProperties(void* data, EAvatarProcessorType type) override = 0;
};
#endif // LL_LLPANELAVATAR_H

View File

@ -104,6 +104,11 @@ LLPanelClassifiedInfo::LLPanelClassifiedInfo()
LLPanelClassifiedInfo::~LLPanelClassifiedInfo()
{
sAllPanels.remove(this);
if (getAvatarId().notNull())
{
LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(), this);
}
}
BOOL LLPanelClassifiedInfo::postBuild()

View File

@ -103,192 +103,8 @@ static const std::string PANEL_PROFILE_VIEW = "panel_profile_view";
static const std::string PROFILE_PROPERTIES_CAP = "AgentProfile";
static const std::string PROFILE_IMAGE_UPLOAD_CAP = "UploadAgentProfileImage";
// <FS:Zi> FIRE-32184: Online/Offline status not working for non-friends
const U32 AVATAR_ONLINE_UNDEFINED = 0x1 << 31;
//////////////////////////////////////////////////////////////////////////
void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id)
{
LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("request_avatar_properties_coro", httpPolicy));
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
LLCore::HttpHeaders::ptr_t httpHeaders;
LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
httpOpts->setFollowRedirects(true);
std::string finalUrl = cap_url + "/" + agent_id.asString();
LLSD result = httpAdapter->getAndSuspend(httpRequest, finalUrl, httpOpts, httpHeaders);
LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
LL_DEBUGS("AvatarProperties") << "Agent id: " << agent_id << " Result: " << httpResults << LL_ENDL;
if (!status
|| !result.has("id")
|| agent_id != result["id"].asUUID())
{
LL_WARNS("AvatarProperties") << "Failed to get agent information for id " << agent_id << LL_ENDL;
return;
}
LLFloater* floater_profile = LLFloaterReg::findInstance("profile", LLSD().with("id", agent_id));
if (!floater_profile)
{
// floater is dead, so panels are dead as well
return;
}
LLPanel *panel = floater_profile->findChild<LLPanel>(PANEL_PROFILE_VIEW, TRUE);
LLPanelProfile *panel_profile = dynamic_cast<LLPanelProfile*>(panel);
if (!panel_profile)
{
LL_WARNS() << PANEL_PROFILE_VIEW << " not found" << LL_ENDL;
return;
}
// Avatar Data
LLAvatarData *avatar_data = &panel_profile->mAvatarData;
std::string birth_date;
avatar_data->agent_id = agent_id;
avatar_data->avatar_id = agent_id;
avatar_data->image_id = result["sl_image_id"].asUUID();
avatar_data->fl_image_id = result["fl_image_id"].asUUID();
avatar_data->partner_id = result["partner_id"].asUUID();
avatar_data->about_text = result["sl_about_text"].asString();
avatar_data->fl_about_text = result["fl_about_text"].asString();
avatar_data->born_on = result["member_since"].asDate();
avatar_data->profile_url = getProfileURL(agent_id.asString());
avatar_data->customer_type = result["customer_type"].asString();
avatar_data->flags = 0;
// <FS:Zi> FIRE-32184: Online/Offline status not working for non-friends
// if (result["online"].asBoolean())
if (result["online"].isUndefined())
{
avatar_data->flags |= AVATAR_ONLINE_UNDEFINED;
}
else if (result["online"].asBoolean())
// </FS:Zi>
{
avatar_data->flags |= AVATAR_ONLINE;
}
if (result["allow_publish"].asBoolean())
{
avatar_data->flags |= AVATAR_ALLOW_PUBLISH;
}
if (result["identified"].asBoolean())
{
avatar_data->flags |= AVATAR_IDENTIFIED;
}
if (result["transacted"].asBoolean())
{
avatar_data->flags |= AVATAR_TRANSACTED;
}
avatar_data->caption_index = 0;
if (result.has("charter_member")) // won't be present if "caption" is set
{
avatar_data->caption_index = result["charter_member"].asInteger();
}
else if (result.has("caption"))
{
avatar_data->caption_text = result["caption"].asString();
}
// TODO: SL-20163 Remove the "has" check when SRV-684 is done
// and the field "hide_age" is included to the http response
avatar_data->hide_age = result.has("hide_age") ?
result["hide_age"].asBoolean() : // Server option value provided by resident
!panel_profile->getSelfProfile(); // Fallback temporary value (to be removed)
panel = floater_profile->findChild<LLPanel>(PANEL_SECONDLIFE, TRUE);
LLPanelProfileSecondLife *panel_sl = dynamic_cast<LLPanelProfileSecondLife*>(panel);
if (panel_sl)
{
panel_sl->processProfileProperties(avatar_data);
}
panel = floater_profile->findChild<LLPanel>(PANEL_WEB, TRUE);
LLPanelProfileWeb *panel_web = dynamic_cast<LLPanelProfileWeb*>(panel);
if (panel_web)
{
panel_web->setLoaded();
}
panel = floater_profile->findChild<LLPanel>(PANEL_FIRSTLIFE, TRUE);
LLPanelProfileFirstLife *panel_first = dynamic_cast<LLPanelProfileFirstLife*>(panel);
if (panel_first)
{
panel_first->processProperties(avatar_data);
}
// Picks
LLSD picks_array = result["picks"];
LLAvatarPicks avatar_picks;
avatar_picks.agent_id = agent_id; // Not in use?
avatar_picks.target_id = agent_id;
for (LLSD::array_const_iterator it = picks_array.beginArray(); it != picks_array.endArray(); ++it)
{
const LLSD& pick_data = *it;
avatar_picks.picks_list.emplace_back(pick_data["id"].asUUID(), pick_data["name"].asString());
}
panel = floater_profile->findChild<LLPanel>(PANEL_PICKS, TRUE);
LLPanelProfilePicks *panel_picks = dynamic_cast<LLPanelProfilePicks*>(panel);
if (panel_picks)
{
// Refresh pick limit before processing
LLAgentPicksInfo::getInstance()->onServerRespond(&avatar_picks);
panel_picks->processProperties(&avatar_picks);
}
// Groups
LLSD groups_array = result["groups"];
LLAvatarGroups avatar_groups;
avatar_groups.agent_id = agent_id; // Not in use?
avatar_groups.avatar_id = agent_id; // target_id
for (LLSD::array_const_iterator it = groups_array.beginArray(); it != groups_array.endArray(); ++it)
{
const LLSD& group_info = *it;
LLAvatarGroups::LLGroupData group_data;
group_data.group_powers = 0; // Not in use?
group_data.group_title = group_info["name"].asString(); // Missing data, not in use?
group_data.group_id = group_info["id"].asUUID();
group_data.group_name = group_info["name"].asString();
group_data.group_insignia_id = group_info["image_id"].asUUID();
avatar_groups.group_list.push_back(group_data);
}
if (panel_sl)
{
panel_sl->processGroupProperties(&avatar_groups);
}
// Notes
LLAvatarNotes avatar_notes;
avatar_notes.agent_id = agent_id;
avatar_notes.target_id = agent_id;
avatar_notes.notes = result["notes"].asString();
panel = floater_profile->findChild<LLPanel>(PANEL_NOTES, TRUE);
LLPanelProfileNotes *panel_notes = dynamic_cast<LLPanelProfileNotes*>(panel);
if (panel_notes)
{
panel_notes->processProperties(&avatar_notes);
}
}
LLUUID post_profile_image(std::string cap_url, const LLSD &first_data, std::string path_to_image, LLHandle<LLPanel> *handle)
{
LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
@ -885,7 +701,7 @@ void LLFloaterProfilePermissions::onCancel()
// LLPanelProfileSecondLife
LLPanelProfileSecondLife::LLPanelProfileSecondLife()
: LLPanelProfilePropertiesProcessorTab() // <FS:Beq/> alter ancestry to re-enable UDP
: LLPanelProfilePropertiesProcessorTab()
, mAvatarNameCacheConnection()
, mHasUnsavedDescriptionChanges(false)
, mWaitingForImageUpload(false)
@ -1004,11 +820,11 @@ BOOL LLPanelProfileSecondLife::postBuild()
}
// <FS:Zi> FIRE-32184: Online/Offline status not working for non-friends
void LLPanelProfileSecondLife::onAvatarProperties(const LLAvatarData* d)
void LLPanelProfileSecondLife::onAvatarProperties(const LLAvatarData* data)
{
// only update the "unknown" status if they are showing as online, otherwise
// we still don't know their true status
if (d->agent_id == gAgentID && d->flags & AVATAR_ONLINE)
if (data->agent_id == gAgentID && data->flags & AVATAR_ONLINE)
{
processOnlineStatus(false, true, true);
}
@ -1102,34 +918,26 @@ void LLPanelProfileSecondLife::onOpen(const LLSD& key)
mAvatarNameCacheConnection = LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLPanelProfileSecondLife::onAvatarNameCache, this, _1, _2));
}
// <FS:Beq> restore UDP profiles for opensim that does not support the cap
void LLPanelProfileSecondLife::updateData()
{
LLUUID avatar_id = getAvatarId();
if (!getStarted() && avatar_id.notNull())
{
setIsLoading();
std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
if (!cap_url.empty())
{
LLCoros::instance().launch("requestAgentUserInfoCoro",
[cap_url, avatar_id]() { request_avatar_properties_coro(cap_url, avatar_id); });
}
else
{
// <FS:Beq> restore UDP profiles for opensim that does not support the cap
#ifdef OPENSIM
if (LLGridManager::instance().isInOpenSim() && !(getSelfProfile() /* TODO(Beq):No longer neeed? && !getEmbedded()*/))
{
LLAvatarPropertiesProcessor::getInstance()->sendAvatarGroupsRequest(avatar_id);
}
else
#endif
// </FS:Beq>
LL_WARNS() << "Failed to update profile data, no cap found" << LL_ENDL;
if (LLGridManager::instance().isInOpenSim() && gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP).empty())
{
LLUUID avatar_id = getAvatarId();
if (!getStarted() && avatar_id.notNull() && gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP).empty() && !getSelfProfile())
{
setIsLoading();
LLAvatarPropertiesProcessor::getInstance()->sendAvatarGroupsRequest(avatar_id);
}
}
else
#endif
{
LLPanelProfilePropertiesProcessorTab::updateData();
}
}
// </FS:Beq>
void LLPanelProfileSecondLife::refreshName()
{
@ -1153,34 +961,6 @@ void LLPanelProfileSecondLife::apply(LLAvatarData* data)
}
#endif
}
void LLPanelProfileSecondLife::processProperties(void* data, EAvatarProcessorType type)
{
// discard UDP replies for profile data if profile capability is available
// otherwise we will truncate profile descriptions to the old UDP limits
if (!gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP).empty())
{
return;
}
if (APT_PROPERTIES_LEGACY == type)
{
const LLAvatarData* avatar_data = static_cast<const LLAvatarData*>(data);
if(avatar_data && getAvatarId() == avatar_data->avatar_id)
{
processProfileProperties(avatar_data);
setLoaded();
}
}
else if (APT_GROUPS == type)
{
LLAvatarGroups* avatar_groups = static_cast<LLAvatarGroups*>(data);
if(avatar_groups && getAvatarId() == avatar_groups->avatar_id)
{
processGroupProperties(avatar_groups);
}
}
}
// </FS:Beq>
void LLPanelProfileSecondLife::resetData()
@ -1251,6 +1031,44 @@ void LLPanelProfileSecondLife::resetData()
// </FS:Ansariel>
}
void LLPanelProfileSecondLife::processProperties(void* data, EAvatarProcessorType type)
{
if (APT_PROPERTIES == type)
{
LLAvatarData* avatar_data = static_cast<LLAvatarData*>(data);
if (avatar_data && getAvatarId() == avatar_data->avatar_id)
{
processProfileProperties(avatar_data);
}
}
// <FS:Beq> Restore UDP profiles
// discard UDP replies for profile data if profile capability is available
// otherwise we will truncate profile descriptions to the old UDP limits
if (!gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP).empty())
{
return;
}
if (APT_PROPERTIES_LEGACY == type)
{
const LLAvatarData* avatar_data = static_cast<const LLAvatarData*>(data);
if(avatar_data && getAvatarId() == avatar_data->avatar_id)
{
processProfileProperties(avatar_data);
}
}
else if (APT_GROUPS == type)
{
LLAvatarGroups* avatar_groups = static_cast<LLAvatarGroups*>(data);
if(avatar_groups && getAvatarId() == avatar_groups->avatar_id)
{
processGroupProperties(avatar_groups);
}
}
// </FS:Beq>
}
void LLPanelProfileSecondLife::processProfileProperties(const LLAvatarData* avatar_data)
{
const LLRelationship* relationship = LLAvatarTracker::instance().getBuddyInfo(getAvatarId());
@ -1278,6 +1096,19 @@ void LLPanelProfileSecondLife::processProfileProperties(const LLAvatarData* avat
fillAccountStatus(avatar_data);
if (!gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP).empty()) { // <FS>
LLAvatarData::group_list_t::const_iterator it = avatar_data->group_list.begin();
const LLAvatarData::group_list_t::const_iterator it_end = avatar_data->group_list.end();
for (; it_end != it; ++it)
{
LLAvatarData::LLGroupData group_data = *it;
mGroups[group_data.group_name] = group_data.group_id;
}
mGroupList->setGroups(mGroups);
} // </FS>
// <FS:Beq> Restore UDP profiles
#ifdef OPENSIM
if (LLGridManager::instance().isInOpenSim())
@ -1307,6 +1138,7 @@ void LLPanelProfileSecondLife::processProfileProperties(const LLAvatarData* avat
updateButtons();
}
// <FS> OpenSim
void LLPanelProfileSecondLife::processGroupProperties(const LLAvatarGroups* avatar_groups)
{
LLAvatarGroups::group_list_t::const_iterator it = avatar_groups->group_list.begin();
@ -1320,6 +1152,7 @@ void LLPanelProfileSecondLife::processGroupProperties(const LLAvatarGroups* avat
mGroupList->setGroups(mGroups);
}
// </FS>
void LLPanelProfileSecondLife::openGroupProfile()
{
@ -1690,14 +1523,15 @@ void LLPanelProfileSecondLife::fillAgeData(const LLAvatarData* avatar_data)
// <FS:Ansariel> Fix LL UI/UX design accident
//// Date from server comes already converted to stl timezone,
//// so display it as an UTC + 0
//std::string name_and_date = getString(avatar_data->hide_age ? "date_format_short" : "date_format_full");
//bool hide_age = avatar_data->hide_age && !getSelfProfile();
//std::string name_and_date = getString(hide_age ? "date_format_short" : "date_format_full");
//LLSD args_name;
//args_name["datetime"] = (S32)avatar_data->born_on.secondsSinceEpoch();
//LLStringUtil::format(name_and_date, args_name);
//getChild<LLUICtrl>("sl_birth_date")->setValue(name_and_date);
//LLUICtrl* userAgeCtrl = getChild<LLUICtrl>("user_age");
//if (avatar_data->hide_age)
//if (hide_age)
//{
// userAgeCtrl->setVisible(FALSE);
//}
@ -1831,7 +1665,7 @@ void LLPanelProfileSecondLife::setAvatarId(const LLUUID& avatar_id)
LLAvatarTracker::instance().removeParticularFriendObserver(getAvatarId(), this);
}
LLPanelProfilePropertiesProcessorTab::setAvatarId(avatar_id); // <FS:Beq/> Change ancestry to restore UDP profiles
LLPanelProfilePropertiesProcessorTab::setAvatarId(avatar_id);
if (LLAvatarActions::isFriend(getAvatarId()))
{
@ -1874,7 +1708,7 @@ void LLPanelProfileSecondLife::processOnlineStatus(bool is_friend, bool show_onl
mPropertiesObserver.mPanelProfile = this;
mPropertiesObserver.mRequester = gAgentID;
LLAvatarPropertiesProcessor::getInstance()->addObserver(getAvatarId(), &mPropertiesObserver);
LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(getAvatarId());
LLAvatarPropertiesProcessor::getInstance()->sendAvatarLegacyPropertiesRequest(getAvatarId());
return;
}
@ -2771,6 +2605,8 @@ void LLPanelProfileWeb::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent e
LLStringUtil::format_map_t args;
args["[TIME]"] = llformat("%.2f", mPerformanceTimer.getElapsedTimeF32());
childSetValue("status_text", LLSD( getString("LoadTime", args)) );
setLoaded();
}
break;
@ -2786,7 +2622,7 @@ void LLPanelProfileWeb::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent e
//////////////////////////////////////////////////////////////////////////
LLPanelProfileFirstLife::LLPanelProfileFirstLife()
: LLPanelProfilePropertiesProcessorTab() // <FS:Beq/> alter ancestry to re-enable UDP
: LLPanelProfilePropertiesProcessorTab()
, mHasUnsavedChanges(false)
{
}
@ -3058,16 +2894,8 @@ void LLPanelProfileFirstLife::onDiscardDescriptionChanges()
setDescriptionText(mCurrentDescription);
}
// <FS:Beq> Restore UDP profiles
void LLPanelProfileFirstLife::processProperties(void * data, EAvatarProcessorType type)
{
// discard UDP replies for profile data if profile capability is available
// otherwise we will truncate profile first life descriptions to the old UDP limits
if (!gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP).empty())
{
return;
}
if (APT_PROPERTIES == type)
{
const LLAvatarData* avatar_data = static_cast<const LLAvatarData*>(data);
@ -3076,8 +2904,17 @@ void LLPanelProfileFirstLife::processProperties(void * data, EAvatarProcessorTyp
processProperties(avatar_data);
}
}
// <FS:Beq> Restore UDP profiles
else if (gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP).empty() && APT_PROPERTIES_LEGACY == type)
{
const LLAvatarData* avatar_data = static_cast<const LLAvatarData*>(data);
if (avatar_data && getAvatarId() == avatar_data->avatar_id)
{
processProperties(avatar_data);
}
}
// </FS:Beq>
}
// </FS:Beq>
void LLPanelProfileFirstLife::processProperties(const LLAvatarData* avatar_data)
{
@ -3152,7 +2989,7 @@ void LLPanelProfileFirstLife::setLoaded()
//////////////////////////////////////////////////////////////////////////
LLPanelProfileNotes::LLPanelProfileNotes()
: LLPanelProfilePropertiesProcessorTab() // <FS:Beq/> alter ancestry to re-enable UDP
: LLPanelProfilePropertiesProcessorTab()
, mHasUnsavedChanges(false)
{
@ -3162,29 +2999,26 @@ LLPanelProfileNotes::~LLPanelProfileNotes()
{
}
// <FS:Beq> Restore UDP profiles
void LLPanelProfileNotes::updateData()
{
LLUUID avatar_id = getAvatarId();
if (!getStarted() && avatar_id.notNull())
{
setIsLoading();
std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
if (!cap_url.empty())
{
LLCoros::instance().launch("requestAgentUserInfoCoro",
[cap_url, avatar_id]() { request_avatar_properties_coro(cap_url, avatar_id); });
}
// <FS:Beq> Restore UDO profiles
#ifdef OPENSIM
else if(LLGridManager::instance().isInOpenSim())
if (LLGridManager::instance().isInOpenSim() && gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP).empty())
{
LLUUID avatar_id = getAvatarId();
if (!getStarted() && avatar_id.notNull() && gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP).empty() && !getSelfProfile())
{
setIsLoading();
LLAvatarPropertiesProcessor::getInstance()->sendAvatarNotesRequest(avatar_id);
}
}
else
#endif
// </FS:Beq>
{
LLPanelProfilePropertiesProcessorTab::updateData();
}
}
// </FS:Beq>
void LLPanelProfileNotes::commitUnsavedChanges()
{
@ -3267,32 +3101,37 @@ void LLPanelProfileNotes::onDiscardNotesChanges()
setNotesText(mCurrentNotes);
}
void LLPanelProfileNotes::processProperties(LLAvatarNotes* avatar_notes)
void LLPanelProfileNotes::processProperties(void* data, EAvatarProcessorType type)
{
setNotesText(avatar_notes->notes);
if (APT_PROPERTIES == type)
{
LLAvatarData* avatar_data = static_cast<LLAvatarData*>(data);
if (avatar_data && getAvatarId() == avatar_data->avatar_id)
{
processProperties(avatar_data);
}
}
// <FS:Beq> Restore UDP profiles
else if (gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP).empty() && APT_NOTES == type)
{
LLAvatarNotes* avatar_notes = static_cast<LLAvatarNotes*>(data);
if (avatar_notes && getAvatarId() == avatar_notes->target_id)
{
LLAvatarData avatardata;
avatardata.notes = avatar_notes->notes;
processProperties(&avatardata);
}
}
// </FS:Beq>
}
void LLPanelProfileNotes::processProperties(const LLAvatarData* avatar_data)
{
setNotesText(avatar_data->notes);
mNotesEditor->setEnabled(TRUE);
setLoaded();
}
// <FS:Beq> Restore UDP profiles
void LLPanelProfileNotes::processProperties(void * data, EAvatarProcessorType type)
{
// discard UDP replies for profile data if profile capability is available
// otherwise we will truncate profile notes to the old UDP limits
if (!gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP).empty())
{
return;
}
if (APT_NOTES == type)
{
LLAvatarNotes* avatar_notes = static_cast<LLAvatarNotes*>(data);
if (avatar_notes && getAvatarId() == avatar_notes->target_id)
{
processProperties(avatar_notes);
}
}
}
// </FS:Beq>
void LLPanelProfileNotes::resetData()
{
@ -3300,14 +3139,6 @@ void LLPanelProfileNotes::resetData()
setNotesText(std::string());
}
void LLPanelProfileNotes::setAvatarId(const LLUUID& avatar_id)
{
if (avatar_id.notNull())
{
LLPanelProfilePropertiesProcessorTab::setAvatarId(avatar_id); // <FS:Beq/> alter ancestry to re-enable UDP
}
}
//////////////////////////////////////////////////////////////////////////
// LLPanelProfile
@ -3396,17 +3227,17 @@ void LLPanelProfile::updateData()
mPanelFirstlife->setIsLoading();
mPanelNotes->setIsLoading();
} // <FS:Beq/> restore udp profiles
std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
if (!cap_url.empty())
{
LLCoros::instance().launch("requestAgentUserInfoCoro",
[cap_url, avatar_id]() { request_avatar_properties_coro(cap_url, avatar_id); });
}
// <FS:Beq> Restore UDP profiles
//LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(getAvatarId());
if (!gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP).empty())
{
LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(getAvatarId());
}
#ifdef OPENSIM
else if (LLGridManager::instance().isInOpenSim())
{
LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(avatar_id);
LLAvatarPropertiesProcessor::getInstance()->sendAvatarLegacyPropertiesRequest(avatar_id);
}
#endif
// </FS:Beq>

View File

@ -112,8 +112,7 @@ public:
* Sends update data request to server.
*/
void apply(LLAvatarData* data);
void processProperties(void* data, EAvatarProcessorType type) override;
void updateData() override;
void updateData() override; // <FS> OpenSim
void refreshName();
void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name);
@ -124,10 +123,10 @@ public:
bool hasUnsavedChanges() override;
void commitUnsavedChanges() override;
friend void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id);
void processProperties(void* data, EAvatarProcessorType type) override;
// <FS:Zi> FIRE-32184: Online/Offline status not working for non-friends
void onAvatarProperties(const LLAvatarData* d);
void onAvatarProperties(const LLAvatarData* data);
protected:
/**
@ -138,6 +137,7 @@ protected:
/**
* Processes group related data received from server.
*/
// <FS> OpenSim
void processGroupProperties(const LLAvatarGroups* avatar_groups);
/**
@ -307,8 +307,6 @@ public:
void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name);
friend void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id);
protected:
void onCommitLoad(LLUICtrl* ctrl);
@ -337,8 +335,8 @@ public:
BOOL postBuild() override;
void processProperties(void* data, EAvatarProcessorType type) override;
void processProperties(const LLAvatarData* avatar_data);
void processProperties(void * data, EAvatarProcessorType type) override;
void apply(LLAvatarData* data);
void resetData() override;
@ -348,8 +346,6 @@ public:
bool hasUnsavedChanges() override { return mHasUnsavedChanges; }
void commitUnsavedChanges() override;
friend void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id);
protected:
void setLoaded() override;
@ -391,16 +387,15 @@ public:
LLPanelProfileNotes();
/*virtual*/ ~LLPanelProfileNotes();
void setAvatarId(const LLUUID& avatar_id) override;
void onOpen(const LLSD& key) override;
BOOL postBuild() override;
void processProperties(LLAvatarNotes* avatar_notes);
void processProperties(void * data, EAvatarProcessorType type) override;
void processProperties(void* data, EAvatarProcessorType type) override;
void processProperties(const LLAvatarData* avatar_data);
void resetData() override;
// <FS> OpenSim
void updateData() override;
bool hasUnsavedChanges() override { return mHasUnsavedChanges; }
@ -452,8 +447,6 @@ public:
LLAvatarData getAvatarData() { return mAvatarData; };
void setAvatarData(const LLAvatarData* avatar_data){ mAvatarData = *avatar_data; };
friend void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id);
private:
void onTabChange();
@ -465,6 +458,7 @@ private:
LLPanelProfileNotes* mPanelNotes;
LLTabContainer* mTabContainer;
// <FS> OpenSim
// Todo: due to server taking minutes to update this needs a more long term storage
// to reuse recently saved values if user opens floater again
// Storage implementation depends onto how a cap will be implemented, if cap will be

View File

@ -307,17 +307,33 @@ void LLPanelProfilePicks::callbackDeletePick(const LLSD& notification, const LLS
void LLPanelProfilePicks::processProperties(void* data, EAvatarProcessorType type)
{
if (APT_PICKS == type)
if (APT_PROPERTIES == type)
{
LLAvatarData* avatar_picks = static_cast<LLAvatarData*>(data);
if (avatar_picks && getAvatarId() == avatar_picks->avatar_id)
{
if (getSelfProfile())
{
LLAgentPicksInfo::getInstance()->onServerRespond(avatar_picks);
}
processProperties(avatar_picks);
}
}
// <FS> OpenSim
else if (APT_PICKS == type)
{
LLAvatarPicks* avatar_picks = static_cast<LLAvatarPicks*>(data);
if (avatar_picks && getAvatarId() == avatar_picks->target_id)
{
processProperties(avatar_picks);
LLAvatarData avatardata;
avatardata.picks_list = avatar_picks->picks_list;
processProperties(&avatardata);
}
}
// </FS>
}
void LLPanelProfilePicks::processProperties(const LLAvatarPicks* avatar_picks)
void LLPanelProfilePicks::processProperties(const LLAvatarData* avatar_picks)
{
LLUUID selected_id = mPickToSelectOnLoad;
bool has_selection = false;
@ -335,7 +351,7 @@ void LLPanelProfilePicks::processProperties(const LLAvatarPicks* avatar_picks)
mTabContainer->deleteAllTabs();
LLAvatarPicks::picks_list_t::const_iterator it = avatar_picks->picks_list.begin();
LLAvatarData::picks_list_t::const_iterator it = avatar_picks->picks_list.begin();
for (; avatar_picks->picks_list.end() != it; ++it)
{
LLUUID pick_id = it->first;
@ -442,7 +458,13 @@ void LLPanelProfilePicks::updateData()
{
setIsLoading();
LLAvatarPropertiesProcessor::getInstance()->sendAvatarPicksRequest(avatar_id);
// <FS> OpenSim
//LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(avatar_id);
if (!gAgent.getRegionCapability("AgentProfile").empty())
LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(avatar_id);
else
LLAvatarPropertiesProcessor::getInstance()->sendAvatarPicksRequest(avatar_id);
// </FS>
}
if (!getIsLoaded())
{

View File

@ -60,7 +60,7 @@ public:
void selectPick(const LLUUID& pick_id);
void processProperties(void* data, EAvatarProcessorType type) override;
void processProperties(const LLAvatarPicks* avatar_picks);
void processProperties(const LLAvatarData* avatar_picks);
void resetData() override;
@ -79,8 +79,6 @@ public:
bool hasUnsavedChanges() override;
void commitUnsavedChanges() override;
friend void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id);
private:
void onClickNewBtn();
void onClickDelete();

View File

@ -3562,18 +3562,7 @@ void register_viewer_callbacks(LLMessageSystem* msg)
LLViewerParcelMgr::processParcelDwellReply);
msg->setHandlerFunc("AvatarPropertiesReply",
&LLAvatarPropertiesProcessor::processAvatarPropertiesReply);
msg->setHandlerFunc("AvatarInterestsReply",
&LLAvatarPropertiesProcessor::processAvatarInterestsReply);
msg->setHandlerFunc("AvatarGroupsReply",
&LLAvatarPropertiesProcessor::processAvatarGroupsReply);
// ratings deprecated
//msg->setHandlerFuncFast(_PREHASH_AvatarStatisticsReply,
// LLPanelAvatar::processAvatarStatisticsReply);
msg->setHandlerFunc("AvatarNotesReply",
&LLAvatarPropertiesProcessor::processAvatarNotesReply);
msg->setHandlerFunc("AvatarPicksReply",
&LLAvatarPropertiesProcessor::processAvatarPicksReply);
&LLAvatarPropertiesProcessor::processAvatarLegacyPropertiesReply);
msg->setHandlerFunc("AvatarClassifiedReply",
&LLAvatarPropertiesProcessor::processAvatarClassifiedsReply);

View File

@ -234,8 +234,18 @@ std::string string_from_guid(const GUID &guid)
return res;
}
#elif LL_DARWIN
bool macos_devices_callback(std::string &product_name, LLSD &data, void* userdata)
{
std::string product = data["product"].asString();
return LLViewerJoystick::getInstance()->initDevice(nullptr, product, data);
}
#endif
// -----------------------------------------------------------------------------
void LLViewerJoystick::updateEnabled(bool autoenable)
{
@ -384,25 +394,48 @@ void LLViewerJoystick::init(bool autoenable)
{
if (mNdofDev)
{
U32 device_type = 0;
void* win_callback = nullptr;
std::function<bool(std::string&, LLSD&, void*)> osx_callback;
// di8_devices_callback callback is immediate and happens in scope of getInputDevices()
#if LL_WINDOWS && !LL_MESA_HEADLESS
// space navigator is marked as DI8DEVCLASS_GAMECTRL in ndof lib
U32 device_type = DI8DEVCLASS_GAMECTRL;
void* callback = &di8_devices_callback;
#else
// MAC doesn't support device search yet
// On MAC there is an ndof_idsearch and it is possible to specify product
// and manufacturer in NDOF_Device for ndof_init_first to pick specific one
U32 device_type = 0;
void* callback = NULL;
#endif
if (!gViewerWindow->getWindow()->getInputDevices(device_type, callback, NULL))
device_type = DI8DEVCLASS_GAMECTRL;
win_callback = &di8_devices_callback;
#elif LL_DARWIN
osx_callback = macos_devices_callback;
if (mLastDeviceUUID.isMap())
{
LL_INFOS("Joystick") << "Failed to gather devices from window. Falling back to ndof's init" << LL_ENDL;
// Failed to gather devices from windows, init first suitable one
mLastDeviceUUID = LLSD();
void *preffered_device = NULL;
initDevice(preffered_device);
std::string manufacturer = mLastDeviceUUID["manufacturer"].asString();
std::string product = mLastDeviceUUID["product"].asString();
strncpy(mNdofDev->manufacturer, manufacturer.c_str(), sizeof(mNdofDev->manufacturer));
strncpy(mNdofDev->product, product.c_str(), sizeof(mNdofDev->product));
if (ndof_init_first(mNdofDev, nullptr))
{
mDriverState = JDS_INITIALIZING;
// Saved device no longer exist
// No device found
LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL;
}
else
{
mDriverState = JDS_INITIALIZED;
}
}
#endif
if (mDriverState != JDS_INITIALIZED)
{
if (!gViewerWindow->getWindow()->getInputDevices(device_type, osx_callback, win_callback, NULL))
{
LL_INFOS("Joystick") << "Failed to gather input devices. Falling back to ndof's init" << LL_ENDL;
// Failed to gather devices, init first suitable one
mLastDeviceUUID = LLSD();
void *preffered_device = NULL;
initDevice(preffered_device);
}
}
if (mDriverState == JDS_INITIALIZING)
@ -457,27 +490,49 @@ void LLViewerJoystick::initDevice(LLSD &guid)
{
#if LIB_NDOF
mLastDeviceUUID = guid;
U32 device_type = 0;
void* win_callback = nullptr;
std::function<bool(std::string&, LLSD&, void*)> osx_callback;
mDriverState = JDS_INITIALIZING;
#if LL_WINDOWS && !LL_MESA_HEADLESS
// space navigator is marked as DI8DEVCLASS_GAMECTRL in ndof lib
U32 device_type = DI8DEVCLASS_GAMECTRL;
void* callback = &di8_devices_callback;
#else
// MAC doesn't support device search yet
// On MAC there is an ndof_idsearch and it is possible to specify product
// and manufacturer in NDOF_Device for ndof_init_first to pick specific one
U32 device_type = 0;
void* callback = NULL;
device_type = DI8DEVCLASS_GAMECTRL;
win_callback = &di8_devices_callback;
#elif LL_DARWIN
osx_callback = macos_devices_callback;
if (mLastDeviceUUID.isMap())
{
std::string manufacturer = mLastDeviceUUID["manufacturer"].asString();
std::string product = mLastDeviceUUID["product"].asString();
strncpy(mNdofDev->manufacturer, manufacturer.c_str(), sizeof(mNdofDev->manufacturer));
strncpy(mNdofDev->product, product.c_str(), sizeof(mNdofDev->product));
if (ndof_init_first(mNdofDev, nullptr))
{
mDriverState = JDS_INITIALIZING;
// Saved device no longer exist
// Np other device present
LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL;
}
else
{
mDriverState = JDS_INITIALIZED;
}
}
#endif
mDriverState = JDS_INITIALIZING;
if (!gViewerWindow->getWindow()->getInputDevices(device_type, callback, NULL))
if (mDriverState != JDS_INITIALIZED)
{
LL_INFOS("Joystick") << "Failed to gather devices from window. Falling back to ndof's init" << LL_ENDL;
// Failed to gather devices from windows, init first suitable one
void *preffered_device = NULL;
mLastDeviceUUID = LLSD();
initDevice(preffered_device);
if (!gViewerWindow->getWindow()->getInputDevices(device_type, osx_callback, win_callback, NULL))
{
LL_INFOS("Joystick") << "Failed to gather input devices. Falling back to ndof's init" << LL_ENDL;
// Failed to gather devices from window, init first suitable one
void *preffered_device = NULL;
mLastDeviceUUID = LLSD();
initDevice(preffered_device);
}
}
if (mDriverState == JDS_INITIALIZING)
@ -488,11 +543,26 @@ void LLViewerJoystick::initDevice(LLSD &guid)
#endif
}
void LLViewerJoystick::initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/, std::string &name, LLSD &guid)
bool LLViewerJoystick::initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/, std::string &name, LLSD &guid)
{
#if LIB_NDOF
mLastDeviceUUID = guid;
#if LL_DARWIN
if (guid.isMap())
{
std::string manufacturer = mLastDeviceUUID["manufacturer"].asString();
std::string product = mLastDeviceUUID["product"].asString();
strncpy(mNdofDev->manufacturer, manufacturer.c_str(), sizeof(mNdofDev->manufacturer));
strncpy(mNdofDev->product, product.c_str(), sizeof(mNdofDev->product));
}
else
{
mNdofDev->product[0] = '\0';
mNdofDev->manufacturer[0] = '\0';
}
#else
#ifdef LL_LINUX
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-truncation"
@ -504,12 +574,15 @@ void LLViewerJoystick::initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8
mNdofDev->product[ sizeof(mNdofDev->product)-1 ] = 0;
mNdofDev->manufacturer[0] = '\0';
#endif
initDevice(preffered_device);
return initDevice(preffered_device);
#else
return false;
#endif
}
void LLViewerJoystick::initDevice(void * preffered_device /* LPDIRECTINPUTDEVICE8* */)
bool LLViewerJoystick::initDevice(void * preffered_device /* LPDIRECTINPUTDEVICE8* */)
{
#if LIB_NDOF
// Different joysticks will return different ranges of raw values.
@ -539,8 +612,10 @@ void LLViewerJoystick::initDevice(void * preffered_device /* LPDIRECTINPUTDEVICE
else
{
mDriverState = JDS_INITIALIZED;
return true;
}
#endif
return false;
}
// -----------------------------------------------------------------------------
@ -1378,6 +1453,8 @@ bool LLViewerJoystick::isDeviceUUIDSet()
#if LL_WINDOWS && !LL_MESA_HEADLESS
// for ease of comparison and to dial less with platform specific variables, we store id as LLSD binary
return mLastDeviceUUID.isBinary();
#elif LL_DARWIN
return mLastDeviceUUID.isMap();
#else
return false;
#endif
@ -1404,19 +1481,48 @@ std::string LLViewerJoystick::getDeviceUUIDString()
{
return std::string();
}
#elif LL_DARWIN
if (mLastDeviceUUID.isMap())
{
std::string manufacturer = mLastDeviceUUID["manufacturer"].asString();
std::string product = mLastDeviceUUID["product"].asString();
return manufacturer + ":" + product;
}
else
{
return std::string();
}
#else
return std::string();
// return mLastDeviceUUID;
#endif
}
void LLViewerJoystick::saveDeviceIdToSettings()
{
#if LL_WINDOWS && !LL_MESA_HEADLESS
// can't save as binary directly,
// someone editing the xml will corrupt it
// so convert to string first
std::string device_string = getDeviceUUIDString();
gSavedSettings.setLLSD("JoystickDeviceUUID", LLSD(device_string));
#else
LLSD device_id = getDeviceUUID();
gSavedSettings.setLLSD("JoystickDeviceUUID", device_id);
#endif
}
void LLViewerJoystick::loadDeviceIdFromSettings()
{
LLSD dev_id = gSavedSettings.getLLSD("JoystickDeviceUUID");
#if LL_WINDOWS && !LL_MESA_HEADLESS
// We can't save binary data to gSavedSettings, somebody editing the file will corrupt it,
// so _GUID data gets converted to string (we probably can convert it to LLUUID with memcpy)
// and here we need to convert it back to binary from string
std::string device_string = gSavedSettings.getString("JoystickDeviceUUID");
std::string device_string;
if (dev_id.isString())
{
device_string = dev_id.asString();
}
if (device_string.empty())
{
mLastDeviceUUID = LLSD();
@ -1430,10 +1536,22 @@ void LLViewerJoystick::loadDeviceIdFromSettings()
LLSD::Binary data; //just an std::vector
data.resize(size);
memcpy(&data[0], &guid /*POD _GUID*/, size);
// We store this data in LLSD since LLSD is versatile and will be able to handle both GUID2
// and any data MAC will need for device selection
// We store this data in LLSD since it can handle both GUID2 and long
mLastDeviceUUID = LLSD(data);
}
#elif LL_DARWIN
if (!dev_id.isMap())
{
mLastDeviceUUID = LLSD();
}
else
{
std::string manufacturer = mLastDeviceUUID["manufacturer"].asString();
std::string product = mLastDeviceUUID["product"].asString();
LL_DEBUGS("Joystick") << "Looking for device by manufacturer: " << manufacturer << " and product: " << product << LL_ENDL;
// We store this data in LLSD since it can handle both GUID2 and long
mLastDeviceUUID = dev_id;
}
#else
mLastDeviceUUID = LLSD();
//mLastDeviceUUID = gSavedSettings.getLLSD("JoystickDeviceUUID");

View File

@ -30,6 +30,9 @@
#include "stdtypes.h"
#if LIB_NDOF
#if LL_DARWIN
#define TARGET_OS_MAC 1
#endif
#include "ndofdev_external.h"
#else
#define NDOF_Device void
@ -60,8 +63,8 @@ class LLViewerJoystick : public LLSingleton<LLViewerJoystick>
public:
void init(bool autoenable);
void initDevice(LLSD &guid);
void initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/);
void initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/, std::string &name, LLSD &guid);
bool initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/);
bool initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/, std::string &name, LLSD &guid);
void terminate();
void updateStatus();
@ -84,6 +87,7 @@ public:
LLSD getDeviceUUID(); //unconverted, OS dependent value wrapped into LLSD, for comparison/search
std::string getDeviceUUIDString(); // converted readable value for settings
std::string getDescription();
void saveDeviceIdToSettings();
protected:
void updateEnabled(bool autoenable);
@ -115,7 +119,11 @@ private:
bool mCameraUpdated;
bool mOverrideCamera;
U32 mJoystickRun;
LLSD mLastDeviceUUID; // _GUID as U8 binary map, integer 1 for no device/ndof's device
// Windows: _GUID as U8 binary map
// MacOS: long as an U8 binary map
// Else: integer 1 for no device/ndof's default device
LLSD mLastDeviceUUID;
static F32 sLastDelta[7];
static F32 sDelta[7];