diff --git a/README b/README index 8d470c1c9f..61c0983370 100644 --- a/README +++ b/README @@ -1,3 +1,17 @@ + + +This repo has moved: git clone git://dawn.adminart.net/lsl-repo.git + + + + + + + + + + + 00000000011111111112222222222333333333344444444445555555555666666666677777777778 12345678901234567890123456789012345678901234567890123456789012345678901234567890 ______ ___ __ _ _____ _ _ ______ _____ ___ _____ _ _ diff --git a/README.compile b/README.compile new file mode 100644 index 0000000000..97c44ff25d --- /dev/null +++ b/README.compile @@ -0,0 +1,36 @@ + +2015-09-18: + +failing webkit on Gentoo: apparently emerging dev-qt/qtopengl fixes it + +for reference: + +build-linux-x86_64-release/newview/packaged $ LD_LIBRARY_PATH="`pwd`/lib64:`pwd`/lib32:$LD_LIBRARY_PATH" ldd ../../plugins/webkit/libmedia_plugin_webkit.so | grep found + +... to see what's missing, and go from there + + +OLD: + +compiling on a minimal centos 6.5 installation from their DVD: + + +yum install cmake gcc gcc-c++ mesa-libGL-devel mesa-libGLU-devel +yum install libidn-devel libXrender-devel libXinerama-devel + +optionally: + +yum install prelink + + +edit SingularityViewer/indra/cmake/00-Common.cmake and comment out +the line "-flto=4" + +install fmod according to instructions on singularity website + +run + + sh ./mkupdate.sh + +... and it should compile. If prelink is not installed, the binary +will not be prelinked, and this is not required. diff --git a/README.gentoo b/README.gentoo new file mode 100644 index 0000000000..f7eb4636ce --- /dev/null +++ b/README.gentoo @@ -0,0 +1,14 @@ + +To compile on Gentoo: + + ++ Use python2.7, not 3.x. + ++ When you get undefined references to GOMP_.* and omp.* symbols, add + '-lgomp' (without the quotes) to: + + build-linux-x86_64-release/newview/CMakeFiles/secondlife-bin.dir/link.txt + build-linux-x86_64-release/llplugin/slplugin/CMakeFiles/SLPlugin.dir/link.txt + ++ You may get messages about 'libwebkit_pluin' failing. I don't know + how to fix that. diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 139a10b3c3..6a41d8bbbf 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -132,12 +132,51 @@ if (LINUX) -DAPPID=secondlife -D_REENTRANT -fexceptions + + -fdelete-dead-exceptions + -fno-math-errno -fno-strict-aliasing -fsigned-char -fvisibility=hidden - -g +# -g -pthread + -march=native +# + -maes + -mmmx + -mpopcnt + -msse + -msse2 + -msse3 + -msse4.1 + -msse4.2 + -mssse3 +# + -O3 + -fno-stack-protector + -fomit-frame-pointer + + -finline-functions +# -ffast-math + -funsafe-math-optimizations + -ffinite-math-only + -fno-signed-zeros + -fcx-limited-range + -funroll-loops + -frename-registers +# -ftracer + -fvariable-expansion-in-unroller + -freorder-blocks-and-partition + -fuse-linker-plugin + -flto=24 +# -I/usr/lib/gcc/x86_64-redhat-linux/4.8.1/ + + -Wno-strict-aliasing +# -ftree-loop-linear // don't + -ftree-loop-im + -ftree-parallelize-loops=24 + -funswitch-loops ) set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 ") @@ -197,10 +236,10 @@ if (LINUX) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") - if (NOT STANDALONE) - # this stops us requiring a really recent glibc at runtime - add_definitions(-fno-stack-protector) - endif (NOT STANDALONE) + # if (NOT STANDALONE) + # # this stops us requiring a really recent glibc at runtime + # add_definitions(-fno-stack-protector) + # endif (NOT STANDALONE) if (${ARCH} STREQUAL "x86_64") add_definitions(-DLINUX64=1 -pipe) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fomit-frame-pointer -ffast-math -funroll-loops") @@ -219,10 +258,10 @@ if (LINUX) set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}") endif (${ARCH} STREQUAL "x86_64") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - if (NOT STANDALONE) - # this stops us requiring a really recent glibc at runtime - add_definitions(-fno-stack-protector) - endif (NOT STANDALONE) + # if (NOT STANDALONE) + # # this stops us requiring a really recent glibc at runtime + # add_definitions(-fno-stack-protector) + # endif (NOT STANDALONE) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline -msse2") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline -msse2") @@ -232,10 +271,10 @@ if (LINUX) set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -msse2") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") - if (NOT STANDALONE) - # this stops us requiring a really recent glibc at runtime - add_definitions(-fno-stack-protector) - endif (NOT STANDALONE) + # if (NOT STANDALONE) + # # this stops us requiring a really recent glibc at runtime + # add_definitions(-fno-stack-protector) + # endif (NOT STANDALONE) if (NOT STANDALONE) set(MARCH_FLAG " -axsse4.1 -msse2") diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index ba81301e3c..2c98567f88 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -882,7 +882,7 @@ static U32 LLMemoryAdjustKBResult(U32 inKB) } #endif -U32 LLMemoryInfo::getPhysicalMemoryKB() const +U64 LLMemoryInfo::getPhysicalMemoryKB() const { #if LL_WINDOWS return LLMemoryAdjustKBResult(mStatsMap["Total Physical KB"].asInteger()); @@ -900,7 +900,7 @@ U32 LLMemoryInfo::getPhysicalMemoryKB() const #elif LL_LINUX U64 phys = 0; phys = (U64)(getpagesize()) * (U64)(get_phys_pages()); - return (U32)(phys >> 10); + return (U64)(phys >> 10); #elif LL_SOLARIS U64 phys = 0; @@ -913,20 +913,10 @@ U32 LLMemoryInfo::getPhysicalMemoryKB() const #endif } -U32 LLMemoryInfo::getPhysicalMemoryClamped() const +U64 LLMemoryInfo::getPhysicalMemoryClamped() const { - // Return the total physical memory in bytes, but clamp it - // to no more than U32_MAX - - U32 phys_kb = getPhysicalMemoryKB(); - if (phys_kb >= 4194304 /* 4GB in KB */) - { - return U32_MAX; - } - else - { - return phys_kb << 10; - } + U64 phys_kb = getPhysicalMemoryKB(); + return (phys_kb << 10); } //static diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h index 52b884e5af..7b2ee924bc 100644 --- a/indra/llcommon/llsys.h +++ b/indra/llcommon/llsys.h @@ -112,12 +112,13 @@ class LL_COMMON_API LLMemoryInfo LLMemoryInfo(); ///< Default constructor void stream(std::ostream& s) const; ///< output text info to s - U32 getPhysicalMemoryKB() const; ///< Memory size in KiloBytes + U64 getPhysicalMemoryKB() const; ///< Memory size in KiloBytes /*! Memory size in bytes, if total memory is >= 4GB then U32_MAX will ** be returned. */ - U32 getPhysicalMemoryClamped() const; ///< Memory size in clamped bytes + // no, do return actual memory, not clamped + U64 getPhysicalMemoryClamped() const; ///< Memory size in clamped bytes //get the available memory infomation in KiloBytes. static void getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_virtual_mem_kb); diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index 1d6a733ecf..fef80eeab4 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -281,7 +281,7 @@ U8* LLImageBase::allocateDataSize(S32 width, S32 height, S32 ncomponents, S32 si // LLImageRaw //--------------------------------------------------------------------------- -AIThreadSafeSimpleDC LLImageRaw::sGlobalRawMemory; +AIThreadSafeSimpleDC LLImageRaw::sGlobalRawMemory; S32 LLImageRaw::sRawImageCount = 0; S32 LLImageRaw::sRawImageCachedCount = 0; @@ -358,7 +358,7 @@ LLImageRaw::~LLImageRaw() U8* LLImageRaw::allocateData(S32 size) { U8* res = LLImageBase::allocateData(size); - *AIAccess(sGlobalRawMemory) += getDataSize(); + *AIAccess(sGlobalRawMemory) += getDataSize(); return res; } @@ -367,7 +367,7 @@ U8* LLImageRaw::reallocateData(S32 size) { S32 old_data_size = getDataSize(); U8* res = LLImageBase::reallocateData(size); - *AIAccess(sGlobalRawMemory) += getDataSize() - old_data_size; + *AIAccess(sGlobalRawMemory) += getDataSize() - old_data_size; return res; } @@ -375,7 +375,7 @@ U8* LLImageRaw::reallocateData(S32 size) void LLImageRaw::deleteData() { { - *AIAccess(sGlobalRawMemory) -= getDataSize(); + *AIAccess(sGlobalRawMemory) -= getDataSize(); } LLImageBase::deleteData(); } @@ -392,7 +392,7 @@ void LLImageRaw::setDataAndSize(U8 *data, S32 width, S32 height, S8 components) LLImageBase::setSize(width, height, components) ; LLImageBase::setDataAndSize(data, width * height * components) ; - *AIAccess(sGlobalRawMemory) += getDataSize(); + *AIAccess(sGlobalRawMemory) += getDataSize(); } BOOL LLImageRaw::resize(U16 width, U16 height, S8 components) diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index 1e94f49f44..2ceb1f59ab 100644 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -257,7 +257,7 @@ class LLImageRaw : public LLImageBase void setDataAndSize(U8 *data, S32 width, S32 height, S8 components) ; public: - static AIThreadSafeSimpleDC sGlobalRawMemory; + static AIThreadSafeSimpleDC sGlobalRawMemory; static S32 sRawImageCount; static S32 sRawImageCachedCount; diff --git a/indra/llinventory/llparcel.cpp b/indra/llinventory/llparcel.cpp index 02cc55e1e0..60bc2961c5 100644 --- a/indra/llinventory/llparcel.cpp +++ b/indra/llinventory/llparcel.cpp @@ -32,6 +32,7 @@ #include "llparcel.h" #include "llstreamtools.h" +#include "llcontrol.h" #include "llmath.h" #include "llsd.h" #include "llsdutil.h" @@ -455,74 +456,74 @@ S32 LLParcel::blockAccess(const LLUUID& agent_id, const LLUUID& group_id, const BOOL is_agent_transacted, const BOOL is_agent_ageverified) const { - // Test ban list - if (isAgentBanned(agent_id)) - { - return BA_BANNED; - } - - // Always allow owner on (unless he banned himself, useful for - // testing). We will also allow estate owners/managers in if they - // are not explicitly banned. - if (agent_id == mOwnerID) - { - return BA_ALLOWED; - } + // Test ban list + if (isAgentBanned(agent_id)) + { + return BA_BANNED; + } - // Special case when using pass list where group access is being restricted but not - // using access list. In this case group members are allowed only if they buy a pass. - // We return BA_NOT_IN_LIST if not in list - BOOL passWithGroup = getParcelFlag(PF_USE_PASS_LIST) && !getParcelFlag(PF_USE_ACCESS_LIST) - && getParcelFlag(PF_USE_ACCESS_GROUP) && !mGroupID.isNull() && group_id == mGroupID; + // Always allow owner on (unless he banned himself, useful for + // testing). We will also allow estate owners/managers in if they + // are not explicitly banned. + if (agent_id == mOwnerID) + { + return BA_ALLOWED; + } + + // Special case when using pass list where group access is being restricted but not + // using access list. In this case group members are allowed only if they buy a pass. + // We return BA_NOT_IN_LIST if not in list + BOOL passWithGroup = getParcelFlag(PF_USE_PASS_LIST) && !getParcelFlag(PF_USE_ACCESS_LIST) + && getParcelFlag(PF_USE_ACCESS_GROUP) && !mGroupID.isNull() && group_id == mGroupID; - // Test group list - if (getParcelFlag(PF_USE_ACCESS_GROUP) - && !mGroupID.isNull() - && group_id == mGroupID - && !passWithGroup) - { - return BA_ALLOWED; - } + // Test group list + if (getParcelFlag(PF_USE_ACCESS_GROUP) + && !mGroupID.isNull() + && group_id == mGroupID + && !passWithGroup) + { + return BA_ALLOWED; + } - // Test access list - if (getParcelFlag(PF_USE_ACCESS_LIST) || passWithGroup ) - { - if (mAccessList.find(agent_id) != mAccessList.end()) - { - return BA_ALLOWED; - } + // Test access list + if (getParcelFlag(PF_USE_ACCESS_LIST) || passWithGroup ) + { + if (mAccessList.find(agent_id) != mAccessList.end()) + { + return BA_ALLOWED; + } - return BA_NOT_ON_LIST; - } + return BA_NOT_ON_LIST; + } - // If we're not doing any other limitations, all users - // can enter, unless - if ( !getParcelFlag(PF_USE_ACCESS_GROUP) - && !getParcelFlag(PF_USE_ACCESS_LIST)) - { - //If the land is group owned, and you are in the group, bypass these checks - if(getIsGroupOwned() && group_id == mGroupID) - { - return BA_ALLOWED; - } + // If we're not doing any other limitations, all users + // can enter, unless + if ( !getParcelFlag(PF_USE_ACCESS_GROUP) + && !getParcelFlag(PF_USE_ACCESS_LIST)) + { + //If the land is group owned, and you are in the group, bypass these checks + if(getIsGroupOwned() && group_id == mGroupID) + { + return BA_ALLOWED; + } - // Test for "payment" access levels - // Anonymous - No Payment Info on File - if(getParcelFlag(PF_DENY_ANONYMOUS) && !is_agent_identified && !is_agent_transacted) - { - return BA_NO_ACCESS_LEVEL; - } - // AgeUnverified - Not Age Verified - if(getParcelFlag(PF_DENY_AGEUNVERIFIED) && !is_agent_ageverified) - { + // Test for "payment" access levels + // Anonymous - No Payment Info on File + if(getParcelFlag(PF_DENY_ANONYMOUS) && !is_agent_identified && !is_agent_transacted) + { + return BA_NO_ACCESS_LEVEL; + } + // AgeUnverified - Not Age Verified + if(getParcelFlag(PF_DENY_AGEUNVERIFIED) && !is_agent_ageverified) + { return BA_NOT_AGE_VERIFIED; - } + } - return BA_ALLOWED; - } + return BA_ALLOWED; + } - return BA_NOT_IN_GROUP; + return BA_NOT_IN_GROUP; } diff --git a/indra/llinventory/llparcelflags.h b/indra/llinventory/llparcelflags.h index 3430447276..541313c586 100644 --- a/indra/llinventory/llparcelflags.h +++ b/indra/llinventory/llparcelflags.h @@ -1,10 +1,10 @@ -/** +/** * @file llparcelflags.h * * $LicenseInfo:firstyear=2002&license=viewergpl$ - * + * * Copyright (c) 2002-2009, Linden Research, Inc. - * + * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 @@ -12,17 +12,17 @@ * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * + * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * + * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. - * + * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. @@ -68,7 +68,7 @@ const U32 PF_ALLOW_ALL_OBJECT_ENTRY = 1 << 27; // Allow all objects to enter a const U32 PF_ALLOW_GROUP_OBJECT_ENTRY = 1 << 28; // Only allow group (and owner) objects to enter the parcel const U32 PF_ALLOW_VOICE_CHAT = 1 << 29; // Allow residents to use voice chat on this parcel const U32 PF_USE_ESTATE_VOICE_CHAN = 1 << 30; -const U32 PF_DENY_AGEUNVERIFIED = 1 << 31; // Prevent residents who aren't age-verified +const U32 PF_DENY_AGEUNVERIFIED = 1 << 31; // Prevent residents who aren't age-verified // NOTE: At one point we have used all of the bits. // We have deprecated two of them in 1.19.0 which *could* be reused, // but only after we are certain there are no simstates using those bits. @@ -93,8 +93,8 @@ const U32 PF_DEFAULT = PF_ALLOW_FLY | PF_USE_BAN_LIST | PF_ALLOW_ALL_OBJECT_ENTRY | PF_ALLOW_GROUP_OBJECT_ENTRY - | PF_ALLOW_VOICE_CHAT - | PF_USE_ESTATE_VOICE_CHAN; + | PF_ALLOW_VOICE_CHAT + | PF_USE_ESTATE_VOICE_CHAN; // Access list flags const U32 AL_ACCESS = (1 << 0); diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h index ced674700b..94ae238be9 100644 --- a/indra/llmessage/message.h +++ b/indra/llmessage/message.h @@ -70,7 +70,7 @@ namespace const U32 MESSAGE_MAX_STRINGS_LENGTH = 64; const U32 MESSAGE_NUMBER_OF_HASH_BUCKETS = 8192; -const S32 MESSAGE_MAX_PER_FRAME = 400; +const S32 MESSAGE_MAX_PER_FRAME = 800; class LLMessageStringTable : public LLSingleton { diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 68ff5d6a07..048a95e9f7 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -50,8 +50,8 @@ U32 wpo2(U32 i); U32 LLImageGL::sUniqueCount = 0; U32 LLImageGL::sBindCount = 0; -S32 LLImageGL::sGlobalTextureMemoryInBytes = 0; -S32 LLImageGL::sBoundTextureMemoryInBytes = 0; +S64 LLImageGL::sGlobalTextureMemoryInBytes = 0; +S64 LLImageGL::sBoundTextureMemoryInBytes = 0; S32 LLImageGL::sCurBoundTextureMemory = 0; S32 LLImageGL::sCount = 0; diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index ecb5222deb..caccf5504c 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -228,8 +228,8 @@ class LLImageGL : public LLRefCount static F32 sLastFrameTime; // Global memory statistics - static S32 sGlobalTextureMemoryInBytes; // Tracks main memory texmem - static S32 sBoundTextureMemoryInBytes; // Tracks bound texmem for last completed frame + static S64 sGlobalTextureMemoryInBytes; // Tracks main memory texmem + static S64 sBoundTextureMemoryInBytes; // Tracks bound texmem for last completed frame static S32 sCurBoundTextureMemory; // Tracks bound texmem for current frame static U32 sBindCount; // Tracks number of texture binds for current frame static U32 sUniqueCount; // Tracks number of unique texture binds for current frame diff --git a/indra/llui/llalertdialog.cpp b/indra/llui/llalertdialog.cpp index 3b4931341e..6e0593586a 100644 --- a/indra/llui/llalertdialog.cpp +++ b/indra/llui/llalertdialog.cpp @@ -324,7 +324,7 @@ bool LLAlertDialog::show() startModal(); gFloaterView->adjustToFitScreen(this, FALSE); open(); /* Flawfinder: ignore */ - setFocus(TRUE); + setFocus(gSavedSettings.getBOOL("NotifyRecievesFocus")); if (mLineEditor) { mLineEditor->setFocus(TRUE); diff --git a/indra/llui/lldraghandle.cpp b/indra/llui/lldraghandle.cpp index 929548137e..d9d89a600c 100644 --- a/indra/llui/lldraghandle.cpp +++ b/indra/llui/lldraghandle.cpp @@ -51,8 +51,8 @@ const S32 LEADING_PAD = 5; const S32 TITLE_PAD = 8; const S32 BORDER_PAD = 1; -const S32 LEFT_PAD = BORDER_PAD + TITLE_PAD + LEADING_PAD; -const S32 RIGHT_PAD = BORDER_PAD + 32; // HACK: space for close btn and minimize btn +const S32 LEFT_PAD = BORDER_PAD + TITLE_PAD + LEADING_PAD + 40; +const S32 RIGHT_PAD = BORDER_PAD; // HACK: space for close btn and minimize btn S32 LLDragHandle::sSnapMargin = 5; diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index fbd2a646dc..d8fd75b317 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -705,6 +705,18 @@ void LLFloater::center() centerWithin(gFloaterView->getRect()); } + +void LLFloater::center_right() +{ + if(getHost()) + { + // hosted floaters can't move + return; + } + centerRight(gFloaterView->getRect()); +} + + void LLFloater::applyRectControl() { if (!getRectControl().empty()) @@ -864,6 +876,8 @@ void LLFloater::handleReshape(const LLRect& new_rect, bool by_user) floaterp->setShape(dependent_rect, by_user); } } + + updateButtons(); } else { @@ -1619,11 +1633,19 @@ void LLFloater::updateButtons() } else { + // btn_rect.setLeftTopAndSize( + // getRect().getWidth() - LLPANEL_BORDER_WIDTH - (LLFLOATER_CLOSE_BOX_SIZE + 1) * button_count, + // getRect().getHeight() - CLOSE_BOX_FROM_TOP, + // ll_round((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale), + // ll_round((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale)); + + // Rty: FIXME: btn_rect.setLeftTopAndSize( - getRect().getWidth() - LLPANEL_BORDER_WIDTH - (LLFLOATER_CLOSE_BOX_SIZE + 1) * button_count, + LLPANEL_BORDER_WIDTH + (LLFLOATER_CLOSE_BOX_SIZE - 1) * button_count, getRect().getHeight() - CLOSE_BOX_FROM_TOP, ll_round((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale), ll_round((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale)); + } mButtons[i]->setRect(btn_rect); @@ -1637,7 +1659,7 @@ void LLFloater::updateButtons() } } if (mDragHandle) - mDragHandle->setMaxTitleWidth(getRect().getWidth() - (button_count * (LLFLOATER_CLOSE_BOX_SIZE + 1))); + mDragHandle->setMaxTitleWidth(getRect().getWidth() - (button_count * (LLFLOATER_CLOSE_BOX_SIZE + 1)) - 8); } void LLFloater::buildButtons() @@ -1655,8 +1677,15 @@ void LLFloater::buildButtons() } else { + // btn_rect.setLeftTopAndSize( + // getRect().getWidth() - LLPANEL_BORDER_WIDTH - (LLFLOATER_CLOSE_BOX_SIZE + 1) * (i + 1), + // getRect().getHeight() - CLOSE_BOX_FROM_TOP, + // ll_round(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale), + // ll_round(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale)); + + // Rty: FIXME: btn_rect.setLeftTopAndSize( - getRect().getWidth() - LLPANEL_BORDER_WIDTH - (LLFLOATER_CLOSE_BOX_SIZE + 1) * (i + 1), + LLPANEL_BORDER_WIDTH + (LLFLOATER_CLOSE_BOX_SIZE - 1) * (i + 1), getRect().getHeight() - CLOSE_BOX_FROM_TOP, ll_round(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale), ll_round(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale)); diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 0aeb115016..dd7c845d7d 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -155,6 +155,9 @@ class LLFloater : public LLPanel // moves to center of gFloaterView void center(); + // moves to right center of gFloaterView + void center_right(); + // applies rectangle stored in mRectControl, if any void applyRectControl(); diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp index 1b8fe5ce21..919ee9eb91 100644 --- a/indra/llui/llscrollbar.cpp +++ b/indra/llui/llscrollbar.cpp @@ -425,7 +425,7 @@ BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask) BOOL LLScrollbar::handleScrollWheel(S32 x, S32 y, S32 clicks) { - BOOL handled = changeLine( clicks * mStepSize, TRUE ); + BOOL handled = changeLine( (clicks << 2) * mStepSize, TRUE ); return handled; } diff --git a/indra/llui/llscrolllistcell.cpp b/indra/llui/llscrolllistcell.cpp index 82484b0bbe..8c427cb0ea 100644 --- a/indra/llui/llscrolllistcell.cpp +++ b/indra/llui/llscrolllistcell.cpp @@ -174,7 +174,7 @@ U32 LLScrollListText::sCount = 0; LLScrollListText::LLScrollListText(const LLScrollListCell::Params& p) : LLScrollListCell(p), mText(p.value().asString()), - mFont(LLFontGL::getFontSansSerifSmall()), + mFont(LLFontGL::getFontSansSerif()), mColor(p.color), mUseColor(p.color.isProvided()), mFontStyle(LLFontGL::getStyleFromString(p.font_style)), @@ -187,18 +187,19 @@ LLScrollListText::LLScrollListText(const LLScrollListCell::Params& p) mTextWidth = getWidth(); - if (p.font.isProvided()) - { - if (const LLFontGL* font = LLResMgr::getInstance()->getRes(p.font)) // Common CAPITALIZED font name? - mFont = font; - else // Less common camelCase font? - { - LLFontDescriptor font_desc; - font_desc.setName(p.font); - if (const LLFontGL* font = LLFontGL::getFont(font_desc)) - mFont = font; - } - } + // FIXME: configurable + // if (p.font.isProvided()) + // { + // if (const LLFontGL* font = LLResMgr::getInstance()->getRes(p.font)) // Common CAPITALIZED font name? + // mFont = font; + // else // Less common camelCase font? + // { + // LLFontDescriptor font_desc; + // font_desc.setName(p.font); + // if (const LLFontGL* font = LLFontGL::getFont(font_desc)) + // mFont = font; + // } + // } // initialize rounded rect image if (!mRoundedRectImage) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 0412c302ea..2f2d5a97b9 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -67,6 +67,12 @@ #include "llmenugl.h" #include "../newview/lgghunspell_wrapper.h" +// [Ratany] +#include "llnotificationsutil.h" +#include "../newview/llexternaleditor.h" +// [/Ratany] + + // // Globals // @@ -80,8 +86,8 @@ const S32 UI_TEXTEDITOR_BUFFER_BLOCK_SIZE = 512; const S32 UI_TEXTEDITOR_BORDER = 1; const S32 UI_TEXTEDITOR_H_PAD = 4; const S32 UI_TEXTEDITOR_V_PAD_TOP = 4; -const S32 UI_TEXTEDITOR_LINE_NUMBER_MARGIN = 32; -const S32 UI_TEXTEDITOR_LINE_NUMBER_DIGITS = 4; +const S32 UI_TEXTEDITOR_LINE_NUMBER_MARGIN = 48; +const S32 UI_TEXTEDITOR_LINE_NUMBER_DIGITS = 6; const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds const S32 CURSOR_THICKNESS = 2; const S32 SPACES_PER_TAB = 4; @@ -246,7 +252,7 @@ class LLTextEditor::LLTextCmdRemove : public LLTextEditor::LLTextCmd /////////////////////////////////////////////////////////////////// -LLTextEditor::LLTextEditor( +LLTextEditor::LLTextEditor( const std::string& name, const LLRect& rect, S32 max_length, // In bytes @@ -301,14 +307,17 @@ LLTextEditor::LLTextEditor( // reset desired x cursor position mDesiredXPixel = -1; - if (font) - { - mGLFont = font; - } - else - { - mGLFont = LLFontGL::getFontSansSerif(); - } + // if (font) + // { + // mGLFont = font; + // } + // else + // { + // mGLFont = LLFontGL::getFontSansSerif(); + // } + // + // mGLFont = LLFontGL::getFontSansSerifSmall(); + mGLFont = LLFontGL::getFontSansSerif(); updateTextRect(); @@ -333,7 +342,7 @@ LLTextEditor::LLTextEditor( mScrollbar->setFollowsTop(); mScrollbar->setFollowsBottom(); mScrollbar->setEnabled( TRUE ); - mScrollbar->setVisible( TRUE ); + mScrollbar->setVisible(gSavedSettings.getBOOL("EditorHasScrollbar")); mScrollbar->setOnScrollEndCallback(mOnScrollEndCallback, mOnScrollEndData); addChild(mScrollbar); @@ -359,6 +368,24 @@ LLTextEditor::LLTextEditor( menu->addChild(new LLMenuItemCallGL("Delete", context_delete, NULL, this)); menu->addChild(new LLMenuItemCallGL("Select All", context_selectall, NULL, this)); menu->addSeparator(); + // [Ratany:] putting this here so it's always available + menu->addChild(new LLMenuItemCallGL("Save contents and Edit ...", context_saveandedit, NULL, this)); + menu->addChild(new LLMenuItemCallGL("Replace contents from File ...", context_loadfile, NULL, this)); + menu->addChild(new LLMenuItemCallGL("Start external Editor", context_start_external, NULL, this)); + menu->addSeparator(); + menu->addChild(new LLMenuItemCallGL("Use big Font", context_use_big_font, NULL, this)); + menu->addChild(new LLMenuItemCallGL("Use bold Font", context_use_bold_font, NULL, this)); + menu->addChild(new LLMenuItemCallGL("Use default Font", context_use_default_font, NULL, this)); + menu->addChild(new LLMenuItemCallGL("Use huge Font", context_use_huge_font, NULL, this)); + menu->addChild(new LLMenuItemCallGL("Use monospace Font", context_use_monospace_font, NULL, this)); + menu->addChild(new LLMenuItemCallGL("Use small Font", context_use_small_font, NULL, this)); + menu->addSeparator(); + menu->addChild(new LLMenuItemCallGL("Toggle line numbers", context_toggle_line_numbers, NULL, this)); + menu->addChild(new LLMenuItemCallGL("Toggle scroll bar", context_toggle_scrollbar, NULL, this)); + menu->addSeparator(); + menu->addChild(new LLMenuItemCallGL("Delete all contents from this editor!", context_empty_buffer, NULL, this)); + // [/Ratany] + menu->addSeparator(); menu->setCanTearOff(FALSE); menu->setVisible(FALSE); mPopupMenuHandle = menu->getHandle(); @@ -386,6 +413,225 @@ void LLTextEditor::context_copy(void* data) } +// [Ratany] +void LLTextEditor::context_empty_buffer(void *data) +{ + LLTextEditor* line = (LLTextEditor*)data; + line->clear(); + line->draw(); +} +void LLTextEditor::context_toggle_line_numbers(void *data) +{ + LLTextEditor* line = (LLTextEditor*)data; + line->mShowLineNumbers = !line->mShowLineNumbers; + line->draw(); +} +void LLTextEditor::context_toggle_scrollbar(void *data) +{ + LLTextEditor* line = (LLTextEditor*)data; + line->mScrollbar->setVisible(!line->mScrollbar->getVisible()); + line->draw(); +} +void LLTextEditor::context_use_big_font(void *data) +{ + LLTextEditor* line = (LLTextEditor*)data; + line->mGLFont = LLFontGL::getFontSansSerifBig(); + line->reshape(line->getRect().getWidth(), line->getRect().getHeight(), true); +} +void LLTextEditor::context_use_bold_font(void *data) +{ + LLTextEditor* line = (LLTextEditor*)data; + line->mGLFont = LLFontGL::getFontSansSerifBold(); + line->reshape(line->getRect().getWidth(), line->getRect().getHeight(), true); +} +void LLTextEditor::context_use_default_font(void *data) +{ + LLTextEditor* line = (LLTextEditor*)data; + line->mGLFont = LLFontGL::getFontSansSerif(); + line->reshape(line->getRect().getWidth(), line->getRect().getHeight(), true); +} +void LLTextEditor::context_use_huge_font(void *data) +{ + LLTextEditor* line = (LLTextEditor*)data; + line->mGLFont = LLFontGL::getFontSansSerifHuge(); + line->reshape(line->getRect().getWidth(), line->getRect().getHeight(), true); +} +void LLTextEditor::context_use_monospace_font(void *data) +{ + LLTextEditor* line = (LLTextEditor*)data; + line->mGLFont = LLFontGL::getFontMonospace(); + line->reshape(line->getRect().getWidth(), line->getRect().getHeight(), true); +} +void LLTextEditor::context_use_small_font(void *data) +{ + LLTextEditor* line = (LLTextEditor*)data; + line->mGLFont = LLFontGL::getFontSansSerifSmall(); + line->reshape(line->getRect().getWidth(), line->getRect().getHeight(), true); +} + +bool LLTextEditor::run_external_editor(const std::string filename) +{ + LLExternalEditor e; + LLExternalEditor::EErrorCode ec; + + ec = e.setCommand(gSavedSettings.getString("ExternalEditor")); + if(ec != LLExternalEditor::EC_SUCCESS) + { + LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", "Error using '" + gSavedSettings.getString("ExternalEditor") + "' as command. Did you set the ExternalEditor debug setting correctly?")); + return true; + } + + ec = e.run(filename); + if(ec != LLExternalEditor::EC_SUCCESS) + { + LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", "Starting the external editor seems to have failed.")); + return true; + } + + return false; // return false on success +} + + +void LLTextEditor::context_saveandedit(void *data) +{ + LLUUID uuid = LLUUID::generateNewID(); + std::string filename; + filename = "singularity-edit-" + uuid.asString() + ".txt"; + + AIFilePicker *filepicker = AIFilePicker::create(); + filepicker->open(filename, FFSAVE_ALL, "", "savefile"); + filepicker->run(boost::bind(&LLTextEditor::context_saveandedit_picked, data, filepicker)); +} + +void LLTextEditor::context_saveandedit_picked(void *data, AIFilePicker* filepicker) +{ + LLTextEditor *line = (LLTextEditor *)data; + + if(filepicker && line) + { + if (!filepicker->hasFilename()) + { + return; + } + } + else + { + LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", "A strange error occured: Null pointer.")); + return; + } + + std::string filename = filepicker->getFilename(); + + // I'm lazy here + if(LLFile::isfile(filename) || LLFile::isdir(filename)) + { + // what about links? + LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", filename + " already exists, please chose a different file name.")); + return; + } + + LLFILE *f = LLFile::fopen(filename, "w"); + if(!f) + { + LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", "Cannot open " + filename + " for writing!")); + return; + } + + std::string content = "[There has been an error!]"; + if(line) + { + content = line->getText(); + } + + // assuming that this is text, though items may be embedded + if(fputs(content.c_str(), f) == EOF) + { + LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", "Writing to " + filename + " failed.")); + clearerr(f); + } + fclose(f); + run_external_editor(filename); +} + + +void LLTextEditor::context_loadfile(void *data) +{ + AIFilePicker *filepicker = AIFilePicker::create(); + filepicker->open(FFLOAD_ALL, "", "openfile", false); + filepicker->run(boost::bind(&LLTextEditor::context_loadfile_picked, data, filepicker)); +} + +void LLTextEditor::context_loadfile_picked(void *data, AIFilePicker* filepicker) +{ + LLTextEditor *line = (LLTextEditor *)data; + + if(filepicker && line) + { + if (!filepicker->hasFilename()) + { + return; + } + } + else + { + LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", "A strange error occured: Null pointer.")); + return; + } + + std::string filename = filepicker->getFilename(); + std::string content; + content.reserve(line->mMaxTextByteLength); + + LLFILE *f = LLFile::fopen(filename, "r"); + if(f) + { + int c; + std::size_t offset = 0; + while(((c = fgetc(f)) != EOF) && (offset < line->mMaxTextByteLength)) + { + content.push_back(c); + offset++; + } + if(ferror(f)) + { + LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", "Error reading " + filename + ".")); + clearerr(f); + } + else + { + if(c != EOF) + { + LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", "This editor does not fathom all the content from the file, hence the input will appear truncated here.")); + } + } + fclose(f); + } + + if(content.empty()) + { + LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", "Content is not replaced because the replacement does not contain content.")); + } + else + { + line->clear(); + line->setText(content); + } +} + + +void LLTextEditor::context_start_external(void *data) +{ + // there's no file name here, so provide an artificial one + // + LLUUID uuid = LLUUID::generateNewID(); + std::string filename; + filename = "singularity-edit-" + uuid.asString() + ".txt"; + + run_external_editor(filename); +} +// [/Ratany] + + void LLTextEditor::spell_correct(void* data) { SpellMenuBind* tempBind = (SpellMenuBind*)data; diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 7f68c41f89..d4f13a2bd7 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -45,6 +45,14 @@ #include "llpreeditor.h" #include "llmenugl.h" +// [Ratany:] for the filepicker +#include "../llplugin/llpluginclassmedia.h" +#include "../llplugin/llpluginclassbasic.h" +#include "../newview/llviewerpluginmanager.h" +#include "../newview/statemachine/aifilepicker.h" +// [/Ratany] + + class LLFontGL; class LLScrollbar; class LLViewBorder; @@ -156,6 +164,26 @@ class LLTextEditor : public LLUICtrl, LLEditMenuHandler, protected LLPreeditor static void context_paste(void* data); static void context_delete(void* data); static void context_selectall(void* data); + + // [Ratany] + static void context_empty_buffer(void *data); + static void context_toggle_line_numbers(void *data); + static void context_toggle_scrollbar(void *data); + static void context_use_big_font(void *data); + static void context_use_bold_font(void *data); + static void context_use_default_font(void *data); + static void context_use_huge_font(void *data); + static void context_use_monospace_font(void *data); + static void context_use_small_font(void *data); + + static bool run_external_editor(const std::string filename); + static void context_saveandedit(void *data); + static void context_start_external(void *data); + static void context_saveandedit_picked(void *data, AIFilePicker* filepicker); + static void context_loadfile(void *data); + static void context_loadfile_picked(void *data, AIFilePicker* filepicker); + // [/Ratany] + static void spell_correct(void* data); static void spell_add(void* data); static void spell_show(void* data); @@ -214,6 +242,7 @@ class LLTextEditor : public LLUICtrl, LLEditMenuHandler, protected LLPreeditor LLKeywords::keyword_iterator_t keywordsBegin() { return mKeywords.begin(); } LLKeywords::keyword_iterator_t keywordsEnd() { return mKeywords.end(); } + // Color support void setCursorColor(const LLColor4& c) { mCursorColor = c; } void setFgColor( const LLColor4& c ) { mFgColor = c; } @@ -502,6 +531,7 @@ class LLTextEditor : public LLUICtrl, LLEditMenuHandler, protected LLPreeditor // // Data // + LLKeywords mKeywords; static LLColor4 mLinkColor; diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index d7a11ad00a..f1c267c997 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -1345,15 +1345,15 @@ void LLView::reshape(S32 width, S32 height, BOOL called_from_parent) child_rect.mLeft += delta_width; child_rect.mRight += delta_width; } - else if (viewp->followsLeft()) - { - // left is 0, don't need to adjust coords - } - else - { - // BUG what to do when we don't follow anyone? - // for now, same as followsLeft - } + // else if (viewp->followsLeft()) + // { + // // left is 0, don't need to adjust coords + // } + // else + // { + // // BUG what to do when we don't follow anyone? + // // for now, same as followsLeft + // } if (viewp->followsTop() && viewp->followsBottom()) { @@ -1841,6 +1841,16 @@ void LLView::centerWithin(const LLRect& bounds) translate( left - getRect().mLeft, bottom - getRect().mBottom ); } + +void LLView::centerRight(const LLRect& bounds) +{ + S32 left = bounds.mLeft + (bounds.getWidth() - getRect().getWidth()); + S32 bottom = bounds.mBottom + (bounds.getHeight() - getRect().getHeight()) / 2; + + translate( left - getRect().mLeft, bottom - getRect().mBottom ); +} + + BOOL LLView::localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, LLView* other_view) const { const LLView* cur_view = this; diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 89d2fc33ce..09e480b705 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -443,6 +443,7 @@ class LLView BOOL translateIntoRect( const LLRect& constraint, BOOL allow_partial_outside ); BOOL translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, BOOL allow_partial_outside ); void centerWithin(const LLRect& bounds); + void centerRight(const LLRect& bounds); void setShape(const LLRect& new_rect, bool by_user = false); virtual LLView* findSnapRect(LLRect& new_rect, const LLCoordGL& mouse_dir, LLView::ESnapType snap_type, S32 threshold, S32 padding = 0); diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index a264b810c4..f93f174b00 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -841,6 +841,151 @@ Value 0 + RtyAddaEnabled + + Comment + Dynamically adjust the draw distance when enabled; not intended to be used together with distance stepping. + Persist + 1 + Type + Boolean + Value + 0 + + RtyAddaDown + + Comment + When dynamically adjusting the draw distance, decrease it by this much per adjustment. + Persist + 1 + Type + F32 + Value + 2.5 + + RtyAddaFrames + + Comment + When dynamically adjusting the draw distance, calculate the average frame rate over this many frames. The higher this number is, the less often the draw distance is adjusted. Keep this number as high as possible. + Persist + 1 + Type + U32 + Value + 60 + + RtyAddaLevel + + Comment + Reference for dynamically adjusting the draw distance. A lower number biases towards more FPS. + Persist + 1 + Type + U32 + Value + 50 + + RtyAddaMaxDD + + Comment + When dynamically adjusting the draw distance, do not set it higher than this. + Persist + 1 + Type + F32 + Value + 368.0 + + RtyAddaMinDD + + Comment + When dynamically adjusting the draw distance, do not set it lower than this. + Persist + 1 + Type + F32 + Value + 32.0 + + RtyAddaUp + + Comment + When dynamically adjusting the draw distance, increase it by this much per adjustment. + Persist + 1 + Type + F32 + Value + 1.25 + + RtyFloaterProfileCenters + + Comment + Whether or not to block your view by centering the profile floater when opening it. + Persist + 1 + Type + Boolean + Value + 0 + + RtyHideInventoryOnTP + + Comment + Whether or not to hide all inventory windows when teleporting. + Persist + 1 + Type + Boolean + Value + 0 + + RtyShowBanLinesFar + + Comment + Do not fade out ban lines with distance when enabled. + Persist + 1 + Type + Boolean + Value + 0 + + RtyShowBothTimes + + Comment + Whether or not to display both local and server time in the menu bar. + Persist + 1 + Type + Boolean + Value + 0 + + UISndRtyFriendOffer + + Comment + Sound file for friendship offers (uuid for sound asset) + Persist + 1 + Type + String + Value + 00000000-0000-0000-0000-000000000116 + IsCOA + 1 + + RtyChatUsesLocalTime + + Comment + Whether or not to use local time for chat log time stamps. + Persist + 1 + Type + Boolean + Value + 0 + LiruMapShowAvCount Comment @@ -1068,7 +1213,7 @@ Found in Advanced->Rendering->Info Displays Type Boolean Value - 0 + 1 LiruUseZQSDKeys @@ -1841,6 +1986,138 @@ This should be as low as possible, but too low may break functionality Value 0 + AntiSpamXtendedAllowedExecutables + + Comment + A list of programs XAntiSpam is allowed to execute when rules call for it. The list is delimited by exclamation marks, e. g.: /bin/emacs!/bin/gedit + Persist + 1 + Type + String + Value + + + AntiSpamXtendedDebug + + Comment + When enabled, XAntiSpam shows the requests it processes as notifications to help you debugging your rules. (resets on relog) + Persist + 0 + Type + Boolean + Value + 0 + + AntiSpamXtendedEnabled + + Comment + When false, XAntiSpam is disabled. + Persist + 1 + Type + Boolean + Value + 0 + + AntiSpamXtendedMaxTabLength + + Comment + Specifies the maximum length of tabs in the communications floater when the [ampersand]-IMShortTab rule is applied. + Persist + 1 + Type + U32 + Value + 20 + + AntiSpamXtendedPersistent + + Comment + When true, persistent fine-grained anti spam rules are enabled. + Persist + 1 + Type + Boolean + Value + 0 + + AntiSpamXtendedQueries + + Comment + When FALSE, enabled fine-grained anti spam rules will be active, but no questions asked. + Persist + 1 + Type + Boolean + Value + 0 + + AntiSpamXtendedRelaxed + + Comment + When true, only blacklisted dialogs will be blocked. + Persist + 1 + Type + Boolean + Value + 0 + + AntiSpamXtendedRxMinLength + + Comment + Minimum length a chat message must have to undergo spam filtering by content. + Persist + 1 + Type + U32 + Value + 85 + + AntiSpamXtendedRxScoreThreshold + + Comment + Minimum score xantispam must have computed for a chat message for it to be considered as spam. + Persist + 1 + Type + U32 + Value + 5 + + AntiSpamXtendedMaxCallsPer10Sec + + Comment + When this timeout kicks in, requests are blocked before further processing. This is against extreme spamming situations. + Persist + 1 + Type + U32 + Value + 300 + + AntiSpamXtendedVolatile + + Comment + When true, non-persistent fine-grained anti spam rules are enabled. + Persist + 1 + Type + Boolean + Value + 0 + + EditorHasScrollbar + + Comment + When true, the chat history has a scrollbar. + Persist + 1 + Type + Boolean + Value + 1 + _NACL_Antispam Comment @@ -11107,6 +11384,17 @@ This should be as low as possible, but too low may break functionality Value 1 + NotifyRecievesFocus + + Comment + When enabled, notifications steal keyboard focus when they are shown. + Persist + 1 + Type + Boolean + Value + 1 + NotifyTipDuration Comment diff --git a/indra/newview/ascentprefschat.cpp b/indra/newview/ascentprefschat.cpp index aacfe68267..3ae736ef57 100644 --- a/indra/newview/ascentprefschat.cpp +++ b/indra/newview/ascentprefschat.cpp @@ -48,6 +48,10 @@ #include "llstartup.h" +// This should be declared somewhere else. +extern void xantispam_buttons(const int); + + LLPrefsAscentChat::LLPrefsAscentChat() { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_preferences_ascent_chat.xml"); @@ -91,6 +95,14 @@ LLPrefsAscentChat::LLPrefsAscentChat() childSetEnabled("reset_antispam", started); getChild("reset_antispam")->setCommitCallback(boost::bind(NACLAntiSpamRegistry::purgeAllQueues)); + // xantispam buttons --- associated numbers see llviewermessage.cpp + getChild("xantispam_BtnClrCachesP")->setCommitCallback(boost::bind(xantispam_buttons, 20)); + getChild("xantispam_BtnClrCachesV")->setCommitCallback(boost::bind(xantispam_buttons, 21)); + getChild("xantispam_BtnEditBlack")->setCommitCallback(boost::bind(xantispam_buttons, 22)); + getChild("xantispam_BtnEditWhite")->setCommitCallback(boost::bind(xantispam_buttons, 23)); + getChild("xantispam_BtnEnable")->setCommitCallback(boost::bind(xantispam_buttons, 6)); + // + getChild("autoreplace")->setCommitCallback(boost::bind(LLFloaterAutoReplaceSettings::showInstance, LLSD())); getChild("KeywordsOn")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitKeywords, this, _1)); getChild("KeywordsList")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitKeywords, this, _1)); diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 4f421318da..8ea3d1a80d 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -47,6 +47,7 @@ #include "llgroupmgr.h" #include "llhomelocationresponder.h" #include "llhudmanager.h" +#include "llpanelmaininventory.h" #include "lljoystickbutton.h" #include "llmorphview.h" #include "llmoveview.h" @@ -838,27 +839,37 @@ void LLAgent::standUp() void LLAgent::handleServerBakeRegionTransition(const LLUUID& region_id) { - LL_INFOS() << "called" << LL_ENDL; + // LL_INFOS() << "called" << LL_ENDL; + if(!isAgentAvatarValid()) { + return; + } + + + bool usb = gAgentAvatarp->isUsingServerBakes(); // Old-style appearance entering a server-bake region. - if (isAgentAvatarValid() && - !gAgentAvatarp->isUsingServerBakes() && - (mRegionp->getCentralBakeVersion()>0)) + if (!usb && (mRegionp->getCentralBakeVersion() > 0)) { LL_INFOS() << "update requested due to region transition" << LL_ENDL; LLAppearanceMgr::instance().requestServerAppearanceUpdate(); + + return; } - // new-style appearance entering a non-bake region, - // need to check for existence of the baking service. - else if (isAgentAvatarValid() && - gAgentAvatarp->isUsingServerBakes() && - mRegionp->getCentralBakeVersion()==0) + + if (usb && (mRegionp->getCentralBakeVersion() == 0)) { + // new-std::yle appearance entering a non-bake region, + // need to check for existence of the baking service. + gAgentAvatarp->checkForUnsupportedServerBakeAppearance(); } + + // really do nothing when new style appearance enters a + // server-bake region? } + void LLAgent::changeParcels() { LL_DEBUGS("AgentLocation") << "Calling ParcelChanged callbacks" << LL_ENDL; @@ -1934,7 +1945,18 @@ BOOL LLAgent::needsRenderHead() //----------------------------------------------------------------------------- void LLAgent::startTyping() { - if (gSavedSettings.getBOOL("FakeAway")) return; + LLCachedControl hide_typing("AscentHideTypingNotification"); + if(hide_typing) + { + return; + } + + LLCachedControl fake_away("FakeAway"); + if (fake_away) + { + return; + } + mTypingTimer.reset(); if (getRenderState() & AGENT_STATE_TYPING) @@ -1953,12 +1975,13 @@ void LLAgent::startTyping() } } - if (gSavedSettings.getBOOL("PlayTypingAnim")) + LLCachedControl pta("PlayTypingAnim"); + if (pta) { sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_START); } - gChatBar-> - sendChatFromViewer("", CHAT_TYPE_START, FALSE); + + gChatBar->sendChatFromViewer("", CHAT_TYPE_START, FALSE); } //----------------------------------------------------------------------------- @@ -1966,12 +1989,17 @@ void LLAgent::startTyping() //----------------------------------------------------------------------------- void LLAgent::stopTyping() { + LLCachedControl hide_typing("AscentHideTypingNotification"); + if(hide_typing) + { + return; + } + if (mRenderState & AGENT_STATE_TYPING) { clearRenderState(AGENT_STATE_TYPING); sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_STOP); - gChatBar-> - sendChatFromViewer("", CHAT_TYPE_STOP, FALSE); + gChatBar->sendChatFromViewer("", CHAT_TYPE_STOP, FALSE); } } @@ -3961,6 +3989,16 @@ bool LLAgent::teleportCore(bool is_local) // gAgentCamera.resetView(FALSE); + // take keyboard focus away (particularly from inventory panel) + gFocusMgr.setKeyboardFocus(NULL); + + // make all inventory views invisible after teleport + static const LLCachedControl rtyhideinventoryontp("RtyHideInventoryOnTP"); + if(rtyhideinventoryontp) + { + LLInventoryView::hideAllViews(); + } + // local logic LLViewerStats::getInstance()->incStat(LLViewerStats::ST_TELEPORT_COUNT); if (is_local) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index ab8d4f59ae..42fb364894 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -3335,13 +3335,15 @@ bool LLAppViewer::initCache() // Init the texture cache // Allocate 80% of the cache size for textures - const S32 MB = 1024 * 1024; - const S64 MIN_CACHE_SIZE = 64 * MB; + const S32 MB = (1024 * 1024); + // const S64 MIN_CACHE_SIZE = 64 * MB; const S64 MAX_CACHE_SIZE = 9984ll * MB; - const S64 MAX_VFS_SIZE = 1024 * MB; // 1 GB + // const S64 MAX_CACHE_SIZE = 102400ll * MB; + const S64 MAX_VFS_SIZE = (S64)(1024 * MB); // 1 GB - S64 cache_size = (S64)(gSavedSettings.getU32("CacheSize")) * MB; - cache_size = llclamp(cache_size, MIN_CACHE_SIZE, MAX_CACHE_SIZE); + // S64 cache_size = (S64)(gSavedSettings.getU32("CacheSize")) * MB; + // cache_size = llclamp(cache_size, MIN_CACHE_SIZE, MAX_CACHE_SIZE); + S64 cache_size = MAX_CACHE_SIZE; S64 texture_cache_size = ((cache_size * 8) / 10); S64 vfs_size = cache_size - texture_cache_size; @@ -4367,7 +4369,7 @@ void LLAppViewer::idleNameCache() #define TIME_THROTTLE_MESSAGES #ifdef TIME_THROTTLE_MESSAGES -#define CHECK_MESSAGES_DEFAULT_MAX_TIME .020f // 50 ms = 50 fps (just for messages!) +#define CHECK_MESSAGES_DEFAULT_MAX_TIME 0.250f // 50 ms = 50 fps (just for messages!) static F32 CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME; #endif diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 5062fe9eb3..b115615cbd 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -337,13 +337,27 @@ static const char* get_profile_floater_name(const LLUUID& avatar_id) static void on_avatar_name_show_profile(const LLUUID& agent_id, const LLAvatarName& av_name, bool web) { - if (gSavedSettings.getString("WebProfileURL").empty() || !(web || gSavedSettings.getBOOL("UseWebProfiles"))) + static LLCachedControl web_profile_url("WebProfileURL"); + static LLCachedControl use_web_profiles("UseWebProfiles"); + + std::string wpu = web_profile_url; + if (wpu.empty() || !(web || use_web_profiles)) { LLFloaterAvatarInfo* floater = LLFloaterAvatarInfo::getInstance(agent_id); if(!floater) { floater = new LLFloaterAvatarInfo(av_name.getCompleteName()+" - "+LLTrans::getString("Command_Profile_Label"), agent_id); - floater->center(); + + // I'd rather have it remember its position ... + static LLCachedControl floater_centers("RtyFloaterProfileCenters"); + if(floater_centers) + { + floater->center(); + } + else + { + floater->center_right(); + } } // ...bring that window to front diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp index b85cd8fad2..fa9fde0f17 100644 --- a/indra/newview/llcallingcard.cpp +++ b/indra/newview/llcallingcard.cpp @@ -1,11 +1,11 @@ -/** +/** * @file llcallingcard.cpp * @brief Implementation of the LLPreviewCallingCard class * * $LicenseInfo:firstyear=2002&license=viewergpl$ - * + * * Copyright (c) 2002-2009, Linden Research, Inc. - * + * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 @@ -13,17 +13,17 @@ * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * + * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * + * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. - * + * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. @@ -62,6 +62,10 @@ #include "llimview.h" #include "llimpanel.h" +// [Ratany:] +#include +// [/Ratany] + ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs ///---------------------------------------------------------------------------- @@ -75,7 +79,7 @@ class LLTrackingData void setTrackedCoarseLocation(const LLVector3d& global_pos); void agentFound(const LLUUID& prey, const LLVector3d& estimated_global_pos); - + public: LLUUID mAvatarID; std::string mName; @@ -106,7 +110,7 @@ static void on_avatar_name_cache_notify(const LLUUID& agent_id, LLAvatarTracker::LLAvatarTracker() : mTrackingData(NULL), mTrackedAgentValid(false), - mModifyMask(0x0) + mModifyMask(0x0) { } @@ -178,7 +182,7 @@ LLVector3d LLAvatarTracker::getGlobalPos() { if(!mTrackedAgentValid || !mTrackingData) return LLVector3d(); LLVector3d global_pos; - + LLVOAvatar* av = gObjectList.findAvatar(mTrackingData->mAvatarID); if(av && !av->isDead()) { @@ -281,7 +285,7 @@ S32 LLAvatarTracker::addBuddyList(const LLAvatarTracker::buddy_map_t& buds) } } notifyObservers(); - + return new_buddy_count; } @@ -340,8 +344,7 @@ void LLAvatarTracker::setBuddyOnline(const LLUUID& id, bool is_online) } else { - LL_WARNS() << "!! No buddy info found for " << id - << ", setting to " << (is_online ? "Online" : "Offline") << LL_ENDL; + LL_WARNS() << "!! No buddy info found for " << id << ", setting to " << (is_online ? "Online" : "Offline") << LL_ENDL; } } @@ -513,34 +516,34 @@ void LLAvatarTracker::removeParticularFriendObserver(const LLUUID& buddy_id, LLF observer_map_t::iterator obs_it = mParticularFriendObserverMap.find(buddy_id); if(obs_it == mParticularFriendObserverMap.end()) - return; + return; obs_it->second.erase(observer); // purge empty sets from the map if (obs_it->second.size() == 0) - mParticularFriendObserverMap.erase(obs_it); + mParticularFriendObserverMap.erase(obs_it); } void LLAvatarTracker::notifyParticularFriendObservers(const LLUUID& buddy_id) { observer_map_t::iterator obs_it = mParticularFriendObserverMap.find(buddy_id); if(obs_it == mParticularFriendObserverMap.end()) - return; + return; // Notify observers interested in buddy_id. observer_set_t& obs = obs_it->second; for (observer_set_t::iterator ob_it = obs.begin(); ob_it != obs.end(); ob_it++) { - (*ob_it)->changed(mModifyMask); + (*ob_it)->changed(mModifyMask); } } // store flag for change // and id of object change applies to void LLAvatarTracker::addChangedMask(U32 mask, const LLUUID& referent) -{ - mModifyMask |= mask; +{ + mModifyMask |= mask; if (referent.notNull()) { mChangedBuddyIDs.insert(referent); @@ -575,7 +578,7 @@ void LLAvatarTracker::processAgentFound(LLMessageSystem* msg, void**) { LLUUID id; - + msg->getUUIDFast(_PREHASH_AgentBlock, _PREHASH_Hunter, id); msg->getUUIDFast(_PREHASH_AgentBlock, _PREHASH_Prey, id); // *FIX: should make sure prey id matches. @@ -597,14 +600,14 @@ void LLAvatarTracker::agentFound(const LLUUID& prey, mTrackingData->agentFound(prey, estimated_global_pos); } -// static +// static void LLAvatarTracker::processOnlineNotification(LLMessageSystem* msg, void**) { LL_DEBUGS() << "LLAvatarTracker::processOnlineNotification()" << LL_ENDL; instance().processNotify(msg, true); } -// static +// static void LLAvatarTracker::processOfflineNotification(LLMessageSystem* msg, void**) { LL_DEBUGS() << "LLAvatarTracker::processOfflineNotification()" << LL_ENDL; @@ -712,8 +715,7 @@ void LLAvatarTracker::processNotify(LLMessageSystem* msg, bool online) } else { - LL_WARNS() << "Received online notification for unknown buddy: " - << agent_id << " is " << (online ? "ONLINE" : "OFFLINE") << LL_ENDL; + LL_WARNS() << "Received online notification for unknown buddy: " << agent_id << " is " << (online ? "ONLINE" : "OFFLINE") << LL_ENDL; } if(tracking_id == agent_id) @@ -736,41 +738,57 @@ void LLAvatarTracker::processNotify(LLMessageSystem* msg, bool online) } } + +extern bool xantispam_check(const std::string&, const std::string&, const std::string&); + + static void on_avatar_name_cache_notify(const LLUUID& agent_id, - const LLAvatarName& av_name, - bool online, - LLSD payload) + const LLAvatarName& av_name, + bool online, + LLSD payload) { // Popup a notify box with online status of this agent + // Use display name only because this user is your friend LLSD args; args["NAME"] = av_name.getNSName(friend_name_system()); args["STATUS"] = online ? LLTrans::getString("OnlineStatus") : LLTrans::getString("OfflineStatus"); + std::string name = args["NAME"]; + boost::algorithm::trim(name); + xantispam_check(agent_id.asString(), (online ? "&-ExecFriendIsOnline!" : "&-ExecFriendIsOffline!"), (online ? name + " is online" : name + " is offline")); + + // do not pop up a notification when disabled for this agent + if(!xantispam_check(agent_id.asString(), (online ? "!StatusFriendIsOnline" : "!StatusFriendIsOffline"), name)) + { + return; + } + // / xantispam + // Popup a notify box with online status of this agent LLNotificationPtr notification; if (online) - { - notification = - LLNotifications::instance().add("FriendOnlineOffline", - args, - payload, - boost::bind(&LLAvatarActions::startIM, agent_id)); - } + { + notification = + LLNotifications::instance().add("FriendOnlineOffline", + args, + payload, + boost::bind(&LLAvatarActions::startIM, agent_id)); + } else - { - notification = - LLNotifications::instance().add("FriendOnlineOffline", args, payload); - } + { + notification = + LLNotifications::instance().add("FriendOnlineOffline", args, payload); + } // If there's an open IM session with this agent, send a notification there too. LLUUID session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, agent_id); if (LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(session_id)) - { - std::string notify_msg = notification->getMessage(); - if (!notify_msg.empty()) - floater->addHistoryLine(notify_msg, gSavedSettings.getColor4("SystemChatColor")); - } + { + std::string notify_msg = notification->getMessage(); + if (!notify_msg.empty()) + floater->addHistoryLine(notify_msg, gSavedSettings.getColor4("SystemChatColor")); + } } void LLAvatarTracker::formFriendship(const LLUUID& id) diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp index 32323b8f59..fb78322530 100644 --- a/indra/newview/llchatbar.cpp +++ b/indra/newview/llchatbar.cpp @@ -584,25 +584,25 @@ void LLChatBar::onInputEditorKeystroke() /* Doesn't work -- can't tell the difference between a backspace that killed the selection vs. backspace at the end of line. - if (length > 1 - && text[0] == '/' - && key == KEY_BACKSPACE) - { - // the selection will already be deleted, but we need to trim - // off the character before - std::string new_text = raw_text.substr(0, length-1); - mInputEditor->setText( new_text ); - mInputEditor->setCursorToEnd(); - length = length - 1; - } + if (length > 1 + && text[0] == '/' + && key == KEY_BACKSPACE) + { + // the selection will already be deleted, but we need to trim + // off the character before + std::string new_text = raw_text.substr(0, length-1); + mInputEditor->setText( new_text ); + mInputEditor->setCursorToEnd(); + length = length - 1; + } */ KEY key = gKeyboard->currentKey(); // Ignore "special" keys, like backspace, arrows, etc. if (length > 1 - && raw_text[0] == '/' - && key < KEY_SPECIAL) + && raw_text[0] == '/' + && key < KEY_SPECIAL) { // we're starting a gesture, attempt to autocomplete diff --git a/indra/newview/llfloateravatarlist.cpp b/indra/newview/llfloateravatarlist.cpp index a1d17ddadc..830adafa76 100644 --- a/indra/newview/llfloateravatarlist.cpp +++ b/indra/newview/llfloateravatarlist.cpp @@ -21,6 +21,8 @@ #include "llavatarconstants.h" #include "llavatarnamecache.h" +#include "llpanelmaininventory.h" + #include "llnotificationsutil.h" #include "llradiogroup.h" #include "llscrolllistcolumn.h" @@ -245,13 +247,13 @@ LLFloaterAvatarList::~LLFloaterAvatarList() //static void LLFloaterAvatarList::toggleInstance(const LLSD&) { - if (!instanceVisible()) + if(instanceExists()) { - showInstance(); + getInstance()->setVisible(!getInstance()->getVisible()); } else { - getInstance()->close(); + showInstance(); } } @@ -1203,6 +1205,8 @@ bool LLFloaterAvatarList::lookAtAvatar(const LLUUID& uuid) void LLFloaterAvatarList::onClickGetKey() { + LLInventoryView::toggleVisibility(); + if (LLScrollListItem* item = mAvatarList->getFirstSelected()) { gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(item->getUUID().asString())); diff --git a/indra/newview/llfloaterchat.cpp b/indra/newview/llfloaterchat.cpp index 449fe6028a..3d9f8b1ab4 100644 --- a/indra/newview/llfloaterchat.cpp +++ b/indra/newview/llfloaterchat.cpp @@ -239,7 +239,7 @@ void log_chat_text(const LLChat& chat) } // static void LLFloaterChat::addChatHistory(const LLChat& chat, bool log_to_file) -{ +{ // [RLVa:KB] - Checked: 2009-07-08 (RLVa-1.0.0e) if (rlv_handler_t::isEnabled()) { @@ -264,7 +264,7 @@ void LLFloaterChat::addChatHistory(const LLChat& chat, bool log_to_file) } // [/RLVa:KB] - if (gSavedPerAccountSettings.getBOOL("LogChat") && log_to_file) + if (log_to_file && gSavedPerAccountSettings.getBOOL("LogChat")) { log_chat_text(chat); } diff --git a/indra/newview/llfloaterchatterbox.cpp b/indra/newview/llfloaterchatterbox.cpp index 2e3598d23b..b14c64f046 100644 --- a/indra/newview/llfloaterchatterbox.cpp +++ b/indra/newview/llfloaterchatterbox.cpp @@ -266,9 +266,9 @@ void LLFloaterChatterBox::removeFloater(LLFloater* floaterp) LLMultiFloater::removeFloater(floaterp); } -void LLFloaterChatterBox::addFloater(LLFloater* floaterp, - BOOL select_added_floater, - LLTabContainer::eInsertionPoint insertion_point) +void LLFloaterChatterBox::addFloater(LLFloater* floaterp, + BOOL select_added_floater, + LLTabContainer::eInsertionPoint insertion_point) { S32 num_locked_tabs = mTabContainer->getNumLockedTabs(); @@ -320,6 +320,65 @@ void LLFloaterChatterBox::addFloater(LLFloater* floaterp, } } + +void LLFloaterChatterBox::addFloaterSmallTab(LLFloater* floaterp, + BOOL select_added_floater, S32 tabwidth, + LLTabContainer::eInsertionPoint insertion_point) +{ + S32 num_locked_tabs = mTabContainer->getNumLockedTabs(); + + // already here + if (floaterp->getHost() == this) return; + + // set the tab width + mTabContainer->setMaxTabWidth(tabwidth); + + // make sure my friends and chat history both locked when re-attaching chat history + if (floaterp->getName() == "chat floater") + { + mTabContainer->unlockTabs(); + // add chat history as second tab if contact window is present, first tab otherwise + if (findChild("floater_my_friends")) + { + // assuming contacts window is first tab, select it + mTabContainer->selectFirstTab(); + // and add ourselves after + LLMultiFloater::addFloater(floaterp, select_added_floater, LLTabContainer::RIGHT_OF_CURRENT); + } + else + { + LLMultiFloater::addFloater(floaterp, select_added_floater, LLTabContainer::START); + } + + // make sure first two tabs are now locked + mTabContainer->lockTabs(num_locked_tabs + 1); + gSavedSettings.setBOOL("ChatHistoryTornOff", FALSE); + floaterp->childSetVisible("chat_layout_panel", true); + floaterp->setCanClose(FALSE); + } + else if (floaterp->getName() == "floater_my_friends") + { + mTabContainer->unlockTabs(); + // add contacts window as first tab + LLMultiFloater::addFloater(floaterp, select_added_floater, LLTabContainer::START); + // make sure first two tabs are now locked + mTabContainer->lockTabs(num_locked_tabs + 1); + gSavedSettings.setBOOL("ContactsTornOff", FALSE); + floaterp->setCanClose(FALSE); + } + else + { + LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point); + } + + // make sure active voice icon shows up for new tab + if (floaterp == mActiveVoiceFloater) + { + mTabContainer->setTabImage(floaterp, "active_voice_tab.tga"); + } +} + + //static LLFloater* LLFloaterChatterBox::getCurrentVoiceFloater() { diff --git a/indra/newview/llfloaterchatterbox.h b/indra/newview/llfloaterchatterbox.h index 70f6314fe5..73acb48b8e 100644 --- a/indra/newview/llfloaterchatterbox.h +++ b/indra/newview/llfloaterchatterbox.h @@ -56,8 +56,11 @@ class LLFloaterChatterBox : public LLMultiFloater, public LLUISingletonmID.asString(); + LLScrollListCell::Params icon_column; icon_column.column = "icon"; icon_column.type = "icon"; @@ -267,6 +272,7 @@ void LLFloaterMute::refreshMuteList() } element.columns.add(icon_column); element.columns.add(name_column); + element.columns.add(uuid_column); mMuteList->addNameItemRow(element); } mMuteList->updateSort(); diff --git a/indra/newview/llfloaterstats.cpp b/indra/newview/llfloaterstats.cpp index 53a6c17085..44f5c5652b 100644 --- a/indra/newview/llfloaterstats.cpp +++ b/indra/newview/llfloaterstats.cpp @@ -234,7 +234,7 @@ void LLFloaterStats::buildStats() stat_barp->mPrecision = 1; stat_barp->mPerSec = FALSE; - stat_barp = texture_statviewp->addStat("Raw Mem", &(LLViewerStats::getInstance()->mRawMemStat), "DebugStatModeRawMem"); + stat_barp = texture_statviewp->addStat("Raw Mem GB", &(LLViewerStats::getInstance()->mRawMemStat), "DebugStatModeRawMem"); stat_barp->setUnitLabel(""); stat_barp->mMinBar = 0.f; stat_barp->mMaxBar = 400.f; diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp index 7bf80e0211..69f8616ff9 100644 --- a/indra/newview/llfolderviewitem.cpp +++ b/indra/newview/llfolderviewitem.cpp @@ -60,7 +60,7 @@ LLFontGL* LLFolderViewItem::getLabelFontForStyle(U8 style) LLFontGL* rtn = sFonts[style]; if (!rtn) // grab label font with this style, lazily { - LLFontDescriptor labelfontdesc("SansSerif", "Small", style); + LLFontDescriptor labelfontdesc("SansSerif", "Medium", style); rtn = LLFontGL::getFont(labelfontdesc); if (!rtn) { diff --git a/indra/newview/llglsandbox.cpp b/indra/newview/llglsandbox.cpp index 26396ec27a..b357fb2b15 100644 --- a/indra/newview/llglsandbox.cpp +++ b/indra/newview/llglsandbox.cpp @@ -1,11 +1,11 @@ -/** +/** * @file llglsandbox.cpp * @brief GL functionality access * * $LicenseInfo:firstyear=2003&license=viewergpl$ - * + * * Copyright (c) 2003-2009, Linden Research, Inc. - * + * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 @@ -13,25 +13,25 @@ * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * + * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * + * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. - * + * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ -/** - * Contains ALL methods which directly access GL functionality +/** + * Contains ALL methods which directly access GL functionality * except for core rendering engine functionality. */ @@ -82,8 +82,8 @@ void LLToolSelectRect::handleRectangleSelection(S32 x, S32 y, MASK mask) // Block rectangle selection if: // - prevented from editing and no exceptions are set (see below for the case where exceptions are set) // - prevented from interacting at all - if ( (rlv_handler_t::isEnabled()) && - ( ((gRlvHandler.hasBehaviour(RLV_BHVR_EDIT)) && (!gRlvHandler.hasException(RLV_BHVR_EDIT))) || + if ( (rlv_handler_t::isEnabled()) && + ( ((gRlvHandler.hasBehaviour(RLV_BHVR_EDIT)) && (!gRlvHandler.hasException(RLV_BHVR_EDIT))) || (gRlvHandler.hasBehaviour(RLV_BHVR_INTERACT)) ) ) { return; @@ -175,8 +175,8 @@ void LLToolSelectRect::handleRectangleSelection(S32 x, S32 y, MASK mask) select_dist_squared = 1.5f * 1.5f; } // [/RLVa:KB] - LLViewerCamera::getInstance()->setPerspective(FOR_SELECTION, - center_x-width/2, center_y-height/2, width, height, + LLViewerCamera::getInstance()->setPerspective(FOR_SELECTION, + center_x-width/2, center_y-height/2, width, height, limit_select_distance); if (shrink_selection) @@ -215,8 +215,8 @@ void LLToolSelectRect::handleRectangleSelection(S32 x, S32 y, MASK mask) if (grow_selection) { std::vector potentials; - - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + + for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); iter != LLWorld::getInstance()->getRegionList().end(); ++iter) { LLViewerRegion* region = *iter; @@ -229,7 +229,7 @@ void LLToolSelectRect::handleRectangleSelection(S32 x, S32 y, MASK mask) } } } - + for (std::vector::iterator iter = potentials.begin(); iter != potentials.end(); iter++) { @@ -237,7 +237,7 @@ void LLToolSelectRect::handleRectangleSelection(S32 x, S32 y, MASK mask) LLViewerObject* vobjp = drawable->getVObj(); if (!drawable || !vobjp || - vobjp->getPCode() != LL_PCODE_VOLUME || + vobjp->getPCode() != LL_PCODE_VOLUME || vobjp->isAttachment() || (deselect && !vobjp->isSelected())) { @@ -334,7 +334,7 @@ void LLWind::renderVectors() // Used by lltoolselectland -void LLViewerParcelMgr::renderRect(const LLVector3d &west_south_bottom_global, +void LLViewerParcelMgr::renderRect(const LLVector3d &west_south_bottom_global, const LLVector3d &east_north_top_global ) { LLGLSUIDefault gls_ui; @@ -606,8 +606,8 @@ void LLViewerParcelMgr::renderHighlightSegments(const U8* segments, LLViewerRegi const S32 STRIDE = (mParcelsPerEdge+1); // Cheat and give this the same pick-name as land - - + + for (y = 0; y < STRIDE; y++) { for (x = 0; x < STRIDE; x++) @@ -621,7 +621,7 @@ void LLViewerParcelMgr::renderHighlightSegments(const U8* segments, LLViewerRegi x2 = x1 + PARCEL_GRID_STEP_METERS; y2 = y1; - + if (!has_segments) { has_segments = true; @@ -667,7 +667,7 @@ void LLViewerParcelMgr::renderCollisionSegments(U8* segments, BOOL use_pass, LLV F32 collision_height; const S32 STRIDE = (mParcelsPerEdge+1); - + LLVector3 pos = gAgent.getPositionAgent(); F32 pos_x = pos.mV[VX]; @@ -676,7 +676,7 @@ void LLViewerParcelMgr::renderCollisionSegments(U8* segments, BOOL use_pass, LLV LLGLSUIDefault gls_ui; LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); LLGLDisable cull(GL_CULL_FACE); - + if (mCollisionBanned == BA_BANNED || regionp->getRegionFlag(REGION_FLAGS_BLOCK_FLYOVER)) { @@ -687,7 +687,7 @@ void LLViewerParcelMgr::renderCollisionSegments(U8* segments, BOOL use_pass, LLV collision_height = PARCEL_HEIGHT; } - + if (use_pass && (mCollisionBanned == BA_NOT_ON_LIST)) { gGL.getTexUnit(0)->bind(mPassImage); @@ -697,6 +697,8 @@ void LLViewerParcelMgr::renderCollisionSegments(U8* segments, BOOL use_pass, LLV gGL.getTexUnit(0)->bind(mBlockedImage); } + static const LLCachedControl RtyShowBanLinesFar("RtyShowBanLinesFar"); + gGL.begin(LLRender::QUADS); for (y = 0; y < STRIDE; y++) @@ -718,34 +720,37 @@ void LLViewerParcelMgr::renderCollisionSegments(U8* segments, BOOL use_pass, LLV x2 = x1 + PARCEL_GRID_STEP_METERS; y2 = y1; - dy = (pos_y - y1) + DIST_OFFSET; + if(RtyShowBanLinesFar) { + alpha = 1.f; + } else { + dy = (pos_y - y1) + DIST_OFFSET; - if (pos_x < x1) - dx = pos_x - x1; - else if (pos_x > x2) - dx = pos_x - x2; - else - dx = 0; + if (pos_x < x1) + dx = pos_x - x1; + else if (pos_x > x2) + dx = pos_x - x2; + else + dx = 0; - dist = dx*dx+dy*dy; + dist = dx*dx+dy*dy; - if (dist < MIN_DIST_SQ) - alpha = MAX_ALPHA; - else if (dist > MAX_DIST_SQ) - alpha = 0.0f; - else - alpha = 30/dist; + if (dist < MIN_DIST_SQ) + alpha = MAX_ALPHA; + else if (dist > MAX_DIST_SQ) + alpha = 0.0f; + else + alpha = 30/dist; - alpha = llclamp(alpha, 0.0f, MAX_ALPHA); + alpha = llclamp(alpha, 0.0f, MAX_ALPHA); + } gGL.color4f(1.f, 1.f, 1.f, alpha); if ((pos_y - y1) < 0) direction = SOUTH_MASK; - else direction = NORTH_MASK; + else direction = NORTH_MASK; // avoid Z fighting renderOneSegment(x1+0.1f, y1+0.1f, x2+0.1f, y2+0.1f, collision_height, direction, regionp); - } if (segment_mask & WEST_MASK) @@ -756,34 +761,36 @@ void LLViewerParcelMgr::renderCollisionSegments(U8* segments, BOOL use_pass, LLV x2 = x1; y2 = y1 + PARCEL_GRID_STEP_METERS; - dx = (pos_x - x1) + DIST_OFFSET; + if(RtyShowBanLinesFar) { + alpha = 1.f; + } else { + dx = (pos_x - x1) + DIST_OFFSET; - if (pos_y < y1) - dy = pos_y - y1; - else if (pos_y > y2) - dy = pos_y - y2; - else - dy = 0; + if (pos_y < y1) + dy = pos_y - y1; + else if (pos_y > y2) + dy = pos_y - y2; + else + dy = 0; - dist = dx*dx+dy*dy; + dist = dx*dx+dy*dy; - if (dist < MIN_DIST_SQ) - alpha = MAX_ALPHA; - else if (dist > MAX_DIST_SQ) - alpha = 0.0f; - else - alpha = 30/dist; - - alpha = llclamp(alpha, 0.0f, MAX_ALPHA); + if (dist < MIN_DIST_SQ) + alpha = MAX_ALPHA; + else if (dist > MAX_DIST_SQ) + alpha = 0.0f; + else + alpha = 30/dist; + alpha = llclamp(alpha, 0.0f, MAX_ALPHA); + } gGL.color4f(1.f, 1.f, 1.f, alpha); if ((pos_x - x1) > 0) direction = WEST_MASK; - else direction = EAST_MASK; - + else direction = EAST_MASK; + // avoid Z fighting renderOneSegment(x1+0.1f, y1+0.1f, x2+0.1f, y2+0.1f, collision_height, direction, regionp); - } } } @@ -842,7 +849,7 @@ void LLViewerObjectList::renderObjectBeacons() S32 last_line_width = -1; // gGL.begin(LLRender::LINES); // Always happens in (line_width != last_line_width) - + for (std::vector::iterator iter = mDebugBeacons.begin(); iter != mDebugBeacons.end(); ++iter) { const LLDebugBeacon &debug_beacon = *iter; @@ -857,7 +864,7 @@ void LLViewerObjectList::renderObjectBeacons() } const LLVector3 &thisline = debug_beacon.mPositionAgent; - + gGL.begin(LLRender::LINES); gGL.color4fv(color.mV); gGL.vertex3f(thisline.mV[VX],thisline.mV[VY],thisline.mV[VZ] - 50.f); @@ -868,7 +875,7 @@ void LLViewerObjectList::renderObjectBeacons() gGL.vertex3f(thisline.mV[VX],thisline.mV[VY] + 2.f,thisline.mV[VZ]); draw_line_cube(0.10f, thisline); - + gGL.end(); } } @@ -876,10 +883,10 @@ void LLViewerObjectList::renderObjectBeacons() { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); - + S32 last_line_width = -1; // gGL.begin(LLRender::LINES); // Always happens in (line_width != last_line_width) - + for (std::vector::iterator iter = mDebugBeacons.begin(); iter != mDebugBeacons.end(); ++iter) { const LLDebugBeacon &debug_beacon = *iter; @@ -906,7 +913,7 @@ void LLViewerObjectList::renderObjectBeacons() gGL.end(); } - + gGL.flush(); glLineWidth(1.f); @@ -931,5 +938,3 @@ void LLViewerObjectList::renderObjectBeacons() } } } - - diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 319e9a7e8d..771415e873 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -62,6 +62,7 @@ #include "llvoicechannel.h" #include +#include // [RLVa:KB] - Checked: 2013-05-10 (RLVa-1.4.9) #include "rlvactions.h" @@ -268,6 +269,12 @@ bool send_start_session_messages( } +// [Ratany:] +extern bool xantispam_check(const std::string&, const std::string&, const std::string&); +// bool xantispam_check(const std::string&, const std::string&, const std::string&); +// [/Ratany] + + // // LLFloaterIMPanel // @@ -366,7 +373,23 @@ LLFloaterIMPanel::LLFloaterIMPanel( if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") ) { - LLLogChat::loadHistory(mLogLabel, + // [Ratany:] Check for distinction + std::string filename(mLogLabel); + if(!isGroupSessionType()) + { + // if(!xantispam_check(mOtherParticipantUUID.asString(), "&-IMLogDistinct", label)) + // { + // filename = (LLAvatarActions::isFriend(mOtherParticipantUUID) ? "friend_" : "resident_") + filename; + // } + } + else + { + if(!xantispam_check(filename, "&-GRLogDistinct", mLogLabel)) + { + filename = "nonagent_" + mLogLabel; + } + } + LLLogChat::loadHistory(filename, &chatFromLogFile, (void *)this); } @@ -726,6 +749,9 @@ bool LLFloaterIMPanel::inviteToSession(const std::vector& ids) return TRUE; } +// from llviewermessage.cpp +extern void send_nothing_im(const LLUUID& to_id, const std::string& message); + void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, LLColor4 incolor, bool log_to_file, const LLUUID& source, const std::string& name) { bool is_agent(gAgentID == source), from_user(source.notNull()); @@ -800,30 +826,95 @@ void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, LLColor4 incol prepend_newline = false; } + std::string histstr; + if (gSavedPerAccountSettings.getBOOL("IMLogTimestamp")) + histstr = LLLogChat::timestamp(gSavedPerAccountSettings.getBOOL("LogTimestampDate")) + show_name + utf8msg; + else + histstr = show_name + utf8msg; + + // [Ratany:] + std::string label(mLogLabel); + boost::algorithm::trim(label); + std::string from = ((source == gAgentID) ? mOtherParticipantUUID.asString() : source.asString()); + std::string thismsg = utf8msg; + if(!isGroupSessionType() && (source != gAgentID)) + { + // could run an external notifier or whatever + xantispam_check(from, "&-ExecOnEachIM!", "IM-session: " + label + ": " + histstr); + } + else + { + xantispam_check(from, "&-ExecOnEachGS!", "GR-session: " + label + ": " + histstr); + } + + // run the message itself through spam filtering + if((source != gAgentID) && xantispam_check(utf8msg, "&-regex", label)) + { + // still logs the original message + // + thismsg = " [blocked spam from]"; + send_nothing_im(mOtherParticipantUUID, "[spam message blocked by recipients client]"); + send_nothing_im(gAgentID, "'[spam message blocked by recipients client]' sent to " + show_name); + } + // Append the chat message in style { LLStyleSP style(new LLStyle); style->setColor(incolor); style->mItalic = is_irc; style->mBold = from_user && gSavedSettings.getBOOL("SingularityBoldGroupModerator") && isModerator(source); - mHistoryEditor->appendStyledText(utf8msg, false, prepend_newline, style); + mHistoryEditor->appendStyledText(thismsg, false, prepend_newline, style); } if (log_to_file && gSavedPerAccountSettings.getBOOL("LogInstantMessages") ) { - std::string histstr; - if (gSavedPerAccountSettings.getBOOL("IMLogTimestamp")) - histstr = LLLogChat::timestamp(gSavedPerAccountSettings.getBOOL("LogTimestampDate")) + show_name + utf8msg; - else - histstr = show_name + utf8msg; - - // [Ansariel: Display name support] - // Floater title contains display name -> bad idea to use that as filename - // mLogLabel, however, is the old legacy name - //LLLogChat::saveHistory(getTitle(),histstr); - LLLogChat::saveHistory(mLogLabel, histstr); - // [/Ansariel: Display name support] + std::string filename(mLogLabel); + bool dont_log = false; + // Optionally distinguish the log files by friends, non-friends and groups: + if(!isGroupSessionType()) + { + // log this IM? + if(xantispam_check(from, "&-IMLogDontSave", label)) + { + // use a distinguished name for the log file? + // if(!xantispam_check(from, "&-IMLogDistinct", label)) + // { + // // hmm ... + // bool source_is_self = (source == gAgentID); + // bool source_is_friend = source_is_self ? false : LLAvatarActions::isFriend(source); + // bool log_friend = (source_is_self && source_is_friend) ? true : source_is_friend; + // filename = (log_friend ? "friend_" : "resident_") + filename; + // } + } + else + { + dont_log = true; + } + } + else // for group sessions + { + if(xantispam_check(label, "&-GRLogDontSave", label)) + { + if(!xantispam_check(label, "&-GRLogDistinct", label)) + { + filename = "nonagent_" + label; + } + } + else + { + dont_log = true; + } + } + if(!dont_log) + { + // [Ansariel: Display name support] + // Floater title contains display name -> bad idea to use that as filename + // mLogLabel, however, is the old legacy name + //LLLogChat::saveHistory(getTitle(),histstr); + LLLogChat::saveHistory(filename, histstr); + // [/Ansariel: Display name support] + } } if (from_user) @@ -1025,9 +1116,17 @@ void LLFloaterIMPanel::onFlyoutCommit(LLComboBox* flyout, const LLSD& value) } } +// extern bool xantispam_check(const std::string&, const std::string&, const std::string&); void show_log_browser(const std::string& name, const std::string& id) { const std::string file(LLLogChat::makeLogFileName(name)); + + if(!xantispam_check(file, "&-IMLogHistoryExternal", file)) + { + // start the external editor with the log and return + return; + } + if (gSavedSettings.getBOOL("LiruLegacyLogLaunch")) { #if LL_WINDOWS || LL_DARWIN @@ -1050,6 +1149,38 @@ void LLFloaterIMPanel::onClickHistory() { if (mOtherParticipantUUID.notNull()) { + // [Ratany:] Check for distinction friend/resident + std::string label = mLogLabel; + boost::algorithm::trim(label); + std::string filename = label; + if(!isGroupSessionType()) + { + // if(!xantispam_check(mOtherParticipantUUID.asString(), "&-IMLogDistinct", label)) + // { + // filename = (LLAvatarActions::isFriend(mOtherParticipantUUID) ? "friend_" : "resident_") + filename; + // } + } + else + { + // needs the group name as origin for distinction here + if(!xantispam_check(label, "&-GRLogDistinct", label)) + { + filename = "nonagent_" + label; + } + } + + filename = LLLogChat::makeLogFileName(filename); + + // see if the user wants full history file in external + // editor, which is what I always expected to happen + // from a button like this ... + if(!xantispam_check(filename, "&-IMLogHistoryExternal", filename)) + { + // ... and if so, this starts the external editor with the log, hence return + return; + } + // [/Ratany] + // [Ansariel: Display name support] //show_log_browser(getTitle(), mOtherParticipantUUID.asString()); show_log_browser(mLogLabel, mOtherParticipantUUID.asString()); @@ -1406,8 +1537,12 @@ void LLFloaterIMPanel::setTyping(bool typing) void LLFloaterIMPanel::sendTypingState(bool typing) { - if(gSavedSettings.getBOOL("AscentHideTypingNotification")) + LLCachedControl hide_typing("AscentHideTypingNotification"); + if(hide_typing) + { return; + } + // Don't want to send typing indicators to multiple people, potentially too // much network traffic. Only send in person-to-person IMs. if (mSessionType != P2P_SESSION) return; diff --git a/indra/newview/llimpanel.h b/indra/newview/llimpanel.h index e016c4ca35..d74d9b141e 100644 --- a/indra/newview/llimpanel.h +++ b/indra/newview/llimpanel.h @@ -135,6 +135,10 @@ class LLFloaterIMPanel : public LLFloater, public LLFriendObserver, public LLMut const std::string& error_string); void showSessionForceClose(const std::string& reason); + // [Ratany: needed for some checks for xantispam] + bool isGroupSessionType() const { return mSessionType == GROUP_SESSION;} + // [/Ratany] + static bool onConfirmForceCloseError(const LLSD& notification, const LLSD& response); // LLIMModel Functionality diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index c36143d499..7ca517f9c6 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -412,6 +412,9 @@ LLIMMgr::~LLIMMgr() // Children all cleaned up by default view destructor. } + +extern bool xantispam_check(const std::string&, const std::string&, const std::string&); + // Add a message to a session. void LLIMMgr::addMessage( const LLUUID& session_id, @@ -455,15 +458,18 @@ void LLIMMgr::addMessage( if (LLVOAvatar* from_avatar = find_avatar_from_object(target_id)) from_avatar->mIdleTimer.reset(); // Not idle, message sent to somewhere // create IM window as necessary + std::string name = (session_name.size() > 1) ? session_name : from; if(!floater) { - // Return now if we're blocking this group's chat or conferences - if (gAgent.isInGroup(session_id) ? getIgnoreGroup(session_id) : dialog != IM_NOTHING_SPECIAL && dialog != IM_SESSION_P2P_INVITE && block_conference(other_participant_id)) + // Return now if we're blocking this group's chat or conferences + bool hasgroup = gAgent.isInGroup(session_id); + if (hasgroup ? getIgnoreGroup(session_id) : dialog != IM_NOTHING_SPECIAL && dialog != IM_SESSION_P2P_INVITE && block_conference(other_participant_id)) return; - std::string name = (session_name.size() > 1) ? session_name : from; - - floater = createFloater(new_session_id, other_participant_id, name, dialog); + // if this is a group session, append the floater at the right + std::vector ids; + ids.push_back(other_participant_id); + floater = createFloater(new_session_id, other_participant_id, name, dialog, ids, hasgroup); // When we get a new IM, and if you are a god, display a bit // of information about the source. This is to help liaisons @@ -485,7 +491,12 @@ void LLIMMgr::addMessage( floater->addHistoryLine(bonus_info.str(), gSavedSettings.getColor4("SystemChatColor")); } - make_ui_sound("UISndNewIncomingIMSession"); + // see if sound or program execution is desired on new sessions + if(xantispam_check(other_participant_id.asString(), (floater->isGroupSessionType() ? "&-GRNewSessionNoSnd" : "&-IMNewSessionNoSnd"), name)) + { + make_ui_sound("UISndNewIncomingIMSession"); + } + xantispam_check(other_participant_id.asString(), (floater->isGroupSessionType() ? "&-ExecOnNewGRSession!" : "&-ExecOnNewIMSession!"), "New chat session: " + name); } // now add message to floater @@ -516,8 +527,12 @@ void LLIMMgr::addMessage( if (!gIMMgr->getFloaterOpen() && floater->getParent() != gFloaterView) { - // If the chat floater is closed and not torn off) notify of a new IM - mIMUnreadCount++; + // only show the button and count if wanted + if(!(xantispam_check(other_participant_id.asString(), "&-IMCountingButton", name) && !xantispam_check(other_participant_id.asString(), "&-IMNoCountingButton", name))) + { + // If the chat floater is closed and not torn off) notify of a new IM + mIMUnreadCount++; + } } } @@ -1062,7 +1077,21 @@ LLFloaterIMPanel* LLIMMgr::createFloater( << " in session " << session_id << LL_ENDL; LLFloaterIMPanel* floater = new LLFloaterIMPanel(session_label, session_id, other_participant_id, dialog, ids); LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END; - LLFloaterChatterBox::getInstance(LLSD())->addFloater(floater, FALSE, i_pt); + + // shorten the tab by shortening the name --- useful for horizantal tabs + // this is a merged request for xantispam_check() + std::string xa_name(session_label); + LLStringUtil::trim(xa_name); + if(xantispam_check(other_participant_id.asString(), "&-IMLongOrShortTab", xa_name)) + { + // from ./indra/llui/lltabcontainer.cpp:const S32 TABCNTR_TAB_MAX_WIDTH = 150; + LLFloaterChatterBox::getInstance(LLSD())->addFloaterSmallTab(floater, FALSE, 150, i_pt); + } + else + { + LLFloaterChatterBox::getInstance(LLSD())->addFloaterSmallTab(floater, FALSE, gSavedSettings.getU32("AntiSpamXtendedMaxTabLength"), i_pt); + } + static LLCachedControl tear_off("OtherChatsTornOff"); if (tear_off) { diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index 029a2c4dfe..0c94b7a9f7 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -37,6 +37,10 @@ #include "llappviewer.h" #include "llfloaterchat.h" +#include "boost/algorithm/string.hpp" + +extern bool xantispam_check(const std::string&, const std::string&, const std::string&); + //static std::string LLLogChat::makeLogFileName(std::string filename) @@ -66,6 +70,9 @@ std::string LLLogChat::makeLogFileName(std::string filename) std::string LLLogChat::cleanFileName(std::string filename) { + // [Ratany: When this isn't trimmed, there will be (at least) two different + // file names, one with space/underscore and one without. /Ratany] + boost::algorithm::trim(filename); std::string invalidChars = "\"\'\\/?*:<>|[]{}~"; // Cannot match glob or illegal filename chars S32 position = filename.find_first_of(invalidChars); while (position != filename.npos) @@ -84,9 +91,20 @@ std::string LLLogChat::timestamp(bool withdate) // There's only one internal tm buffer. struct tm* timep; - // Convert to Pacific, based on server's opinion of whether - // it's daylight savings time there. - timep = utc_to_pacific_time(utc_time, gPacificDaylightTime); + // PDT/PDS is totally irrelevant + static const LLCachedControl use_local_time("RtyChatUsesLocalTime"); + + if(use_local_time) + { + // use local time + timep = std::localtime(&utc_time); + } + else + { + // Convert to Pacific, based on server's opinion of whether + // it's daylight savings time there. + timep = utc_to_pacific_time(utc_time, gPacificDaylightTime); + } static LLCachedControl withseconds("SecondsInLog"); std::string text; @@ -127,81 +145,130 @@ void LLLogChat::saveHistory(std::string const& filename, std::string line) } } -static long const LOG_RECALL_BUFSIZ = 2048; -void LLLogChat::loadHistory(std::string const& filename , void (*callback)(ELogLineType, std::string, void*), void* userdata) +static long const LOG_RECALL_BUFSIZ = 65536; +extern LLUUID gAgentID; +extern void send_nothing_im(const LLUUID& to_id, const std::string& message); + +long LLLogChat::computeFileposition(LLFILE *fptr, U32 lines) { - bool filename_empty = filename.empty(); - if (filename_empty) + // Set pos to point to the last byte of the file, if any. + if(fseek(fptr, 0, SEEK_END)) { - LL_WARNS() << "filename is empty!" << LL_ENDL; + return -1; } - else while(1) // So we can use break. + long pos = ftell(fptr); + if(pos == -1) { - // The number of lines to return. - static const LLCachedControl lines("LogShowHistoryLines", 32); - if (lines == 0) break; + send_nothing_im(gAgentID, "INFO: ftell() indicates error when loading chat history"); + return -1; + } - // Open the log file. - LLFILE* fptr = LLFile::fopen(makeLogFileName(filename), "rb"); - if (!fptr) break; + char buffer[LOG_RECALL_BUFSIZ]; + if(sizeof(*buffer) * LOG_RECALL_BUFSIZ != LOG_RECALL_BUFSIZ) + { + send_nothing_im(gAgentID, "The size of a char must match the size of a char in a file."); + return -1; + } - // Set pos to point to the last character of the file, if any. - if (fseek(fptr, 0, SEEK_END)) break; - long pos = ftell(fptr) - 1; - if (pos < 0) break; + send_nothing_im(gAgentID, "INFO: loading chat history"); + std::size_t nlines = 0; + while(pos > 0) + { + // reposition file pointer towards SEEK_SET + size_t size = llmin(LOG_RECALL_BUFSIZ, pos); + fseek(fptr, -size, SEEK_CUR); // starts at SEEK_END, so step backwards + size_t haveread = fread(buffer, sizeof(*buffer), size, fptr) * sizeof(*buffer); + if(ferror(fptr)) + { + send_nothing_im(gAgentID, "INFO: fread() indicates error when loading chat history"); + return -1; + } - char buffer[LOG_RECALL_BUFSIZ]; - bool error = false; - U32 nlines = 0; - while (pos > 0 && nlines < lines) + // Count the number of newlines in the buffer and set + // pos to the beginning of the first line to return + // when we found enough. + size_t filepos = pos; + pos -= (haveread - 1); + char const* p = buffer + haveread; + while(p > buffer) { - // Read the LOG_RECALL_BUFSIZ characters before pos. - size_t size = llmin(LOG_RECALL_BUFSIZ, pos); - pos -= size; - fseek(fptr, pos, SEEK_SET); - size_t len = fread(buffer, 1, size, fptr); - error = len != size; - if (error) break; - // Count the number of newlines in it and set pos to the beginning of the first line to return when we found enough. - for (char const* p = buffer + size - 1; p >= buffer; --p) + --p; + + if(*p == 0x0a) { - if (*p == '\n') + nlines++; + if(nlines > lines) { - if (++nlines == lines) - { - pos += p - buffer + 1; - break; - } + return filepos; } } + + --filepos; } - if (error) - { - fclose(fptr); - break; - } + } - // Set the file pointer at the first line to return. - fseek(fptr, pos, SEEK_SET); + // maybe there aren't so many lines in the file + return 0; +} - // Read lines from the file one by one until we reach the end of the file. - while (fgets(buffer, LOG_RECALL_BUFSIZ, fptr)) - { - size_t len = strlen(buffer); - int i = len - 1; - while (i >= 0 && (buffer[i] == '\r' || buffer[i] == '\n')) // strip newline chars from the end of the string - { - buffer[i] = '\0'; - i--; - } - callback(LOG_LINE, buffer, userdata); - } - fclose(fptr); - callback(LOG_END, LLStringUtil::null, userdata); +void LLLogChat::loadHistory(std::string const& filename , void (*callback)(ELogLineType, std::string, void*), void* userdata) +{ + // The number of lines to read. + U32 lines = gSavedSettings.getU32("LogShowHistoryLines"); + + if(!lines || filename.empty()) + { + callback(LOG_EMPTY, LLStringUtil::null, userdata); return; } - callback(LOG_EMPTY, LLStringUtil::null, userdata); -} + // Open the log file. + // reading in binary mode might disable newline conversions + LLFILE* fptr = LLFile::fopen(makeLogFileName(filename), "rb"); + if (!fptr) + { + callback(LOG_EMPTY, LLStringUtil::null, userdata); + return; + } + + long pos = 0; + if(filename == "chat" || xantispam_check(filename, "&-IM/GRLogFullHistory", filename)) + { + pos = computeFileposition(fptr, lines); + } + + if(pos < 0) + { + callback(LOG_EMPTY, LLStringUtil::null, userdata); + } + else + { + // Set the file pointer at the first line to read. + if(!fseek(fptr, pos, SEEK_SET)) + { + // Read lines from the file one by one until we reach the end of the file. + char buffer[LOG_RECALL_BUFSIZ]; + while(fgets(buffer, LOG_RECALL_BUFSIZ, fptr) == buffer) + { + size_t len = strlen(buffer); + if(len > 0) + { + // fgets() does null-terminate the buffer on success + // overwrite '\n' and a possible EOF; '\n' is appended by callback() + buffer[len - 1] = '\0'; + callback(LOG_LINE, buffer, userdata); + } + } + callback(LOG_END, LLStringUtil::null, userdata); + } + else + { + callback(LOG_EMPTY, LLStringUtil::null, userdata); + } + } + + clearerr(fptr); + fclose(fptr); +} diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h index 84f6760ab6..43fb884211 100644 --- a/indra/newview/lllogchat.h +++ b/indra/newview/lllogchat.h @@ -48,6 +48,7 @@ class LLLogChat static std::string timestamp(bool withdate = false); static std::string makeLogFileName(std::string filename); static void saveHistory(std::string const& filename, std::string line); + static long computeFileposition(LLFILE *fptr, U32 lines); static void loadHistory(std::string const& filename, void (*callback)(ELogLineType,std::string,void*), void* userdata); diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 02f3eedf06..982ce3bdc9 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -483,6 +483,24 @@ void LLInventoryView::cleanup() } +// static +void LLInventoryView::hideAllViews() +{ + S32 count = sActiveViews.size(); + while(count > 1) + { + --count; + LLInventoryView* iv = sActiveViews.at(count); + + // destroy all but one for otherwise they might "leak" + // since the user cannot make them visible again + iv->destroy(); + } + + sActiveViews.at(0)->setVisible(false); +} + + void LLInventoryView::updateSortControls() { U32 order = mActivePanel ? mActivePanel->getSortOrder() : gSavedSettings.getU32("InventorySortOrder"); diff --git a/indra/newview/llpanelmaininventory.h b/indra/newview/llpanelmaininventory.h index 0b258ac8b5..2fda62ae7b 100644 --- a/indra/newview/llpanelmaininventory.h +++ b/indra/newview/llpanelmaininventory.h @@ -67,6 +67,9 @@ friend class LLFloaterInventoryFinder; // Final cleanup, destroy all open inventory views. static void cleanup(); + // hide all views + static void hideAllViews(); + // LLView & LLFloater functionality virtual void onClose(bool app_quitting); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 7be6ffd4fe..28493c9b1b 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -504,7 +504,7 @@ void init_audio() bool idle_startup() { const F32 PRECACHING_DELAY = gSavedSettings.getF32("PrecachingDelay"); - const F32 TIMEOUT_SECONDS = 5.f; + const F32 TIMEOUT_SECONDS = 25.f; const S32 MAX_TIMEOUT_COUNT = 3; static LLTimer timeout; diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index ba97444971..e4f15cb9b1 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -330,6 +330,9 @@ void LLStatusBar::refresh() // Singu Note: Use system's time if the user desires, otherwise use server time static const LLCachedControl show_local_time("LiruLocalTime"); + static const LLCachedControl short_time_format("ShortTimeFormat"); + static const LLCachedControl long_date_format("LongDateFormat"); + static const LLCachedControl show_both_times("RtyShowBothTimes"); // Get current UTC time, adjusted for the user's clock // being off. @@ -342,9 +345,8 @@ void LLStatusBar::refresh() // it's daylight savings time there. internal_time = show_local_time ? std::localtime(&utc_time) : utc_to_pacific_time(utc_time, gPacificDaylightTime); - static const LLCachedControl short_time_fmt(gSavedSettings, "ShortTimeFormat"); std::string t; - timeStructToFormattedString(internal_time, short_time_fmt, t); + timeStructToFormattedString(internal_time, short_time_format, t); if (show_local_time) { static const std::string local(" " + getString("Local")); @@ -358,11 +360,27 @@ void LLStatusBar::refresh() { t += " PST"; } - mTextTime->setText(t); - static const LLCachedControl long_date_fmt(gSavedSettings, "LongDateFormat"); std::string date; - timeStructToFormattedString(internal_time, long_date_fmt, date); + timeStructToFormattedString(internal_time, long_date_format, date); + + // show both clocks if wanted + if(show_both_times) + { + internal_time = std::localtime(&utc_time); + + std::string second_t; + timeStructToFormattedString(internal_time, short_time_format, second_t); + // unfortunately, this is a rather short field :( + t = second_t + "/" + t; + + std::string second_date; + // add the second date if displayed + timeStructToFormattedString(internal_time, long_date_format, second_date); + date = second_date + "\n/" + date; + } + + mTextTime->setText(t); mTextTime->setToolTip(date); LLRect r; diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index b201bfcca9..cbefb2f8ab 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -567,7 +567,7 @@ void LLGLTexMemBar::draw() S32 bound_mem = BYTES_TO_MEGA_BYTES(LLViewerTexture::sBoundTextureMemoryInBytes); S32 max_bound_mem = LLViewerTexture::sMaxBoundTextureMemInMegaBytes; S32 total_mem = BYTES_TO_MEGA_BYTES(LLViewerTexture::sTotalTextureMemoryInBytes); - S32 max_total_mem = LLViewerTexture::sMaxTotalTextureMemInMegaBytes; + S64 max_total_mem = LLViewerTexture::sMaxTotalTextureMemInMegaBytes; F32 discard_bias = LLViewerTexture::sDesiredDiscardBias; F32 cache_usage = (F32)BYTES_TO_MEGA_BYTES(LLAppViewer::getTextureCache()->getUsage()) ; F32 cache_max_usage = (F32)BYTES_TO_MEGA_BYTES(LLAppViewer::getTextureCache()->getMaxUsage()) ; @@ -578,16 +578,16 @@ void LLGLTexMemBar::draw() U32 total_http_requests = LLAppViewer::getTextureFetch()->getTotalNumHTTPRequests() ; //---------------------------------------------------------------------------- LLGLSUIDefault gls_ui; - LLColor4 text_color(1.f, 1.f, 1.f, 0.75f); + LLColor4 text_color(0.f, 1.f, 0.f, 1.0f); LLColor4 color; std::string text = ""; - - S32 global_raw_memory; + + U64 global_raw_memory; { - global_raw_memory = *AIAccess(LLImageRaw::sGlobalRawMemory); + global_raw_memory = *AIAccess(LLImageRaw::sGlobalRawMemory); } - text = llformat("GL Tot: %d/%d MB Bound: %d/%d MB FBO: %d MB Raw Tot: %d MB Bias: %.2f Cache: %.1f/%.1f MB Net Tot Tex: %.1f MB Tot Obj: %.1f MB Tot Htp: %d", + text = llformat("GL Tot: %d/%lld MB Bound: %d/%d MB FBO: %d MB Raw Tot: %llu MB Bias: %.2f Cache: %.1f/%.1f MB Net Tot Tex: %.1f MB Tot Obj: %.1f MB Tot Htp: %d", total_mem, max_total_mem, bound_mem, @@ -597,8 +597,7 @@ void LLGLTexMemBar::draw() cache_usage, cache_max_usage, total_texture_downloaded, total_object_downloaded, total_http_requests); //, cache_entries, cache_max_entries - LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3, - text_color, LLFontGL::LEFT, LLFontGL::TOP); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3, text_color, LLFontGL::LEFT, LLFontGL::TOP); //---------------------------------------------------------------------------- #if 0 diff --git a/indra/newview/lltoolgrab.cpp b/indra/newview/lltoolgrab.cpp index f17754f97a..5802a50592 100644 --- a/indra/newview/lltoolgrab.cpp +++ b/indra/newview/lltoolgrab.cpp @@ -658,22 +658,26 @@ void LLToolGrab::handleHoverActive(S32 x, S32 y, MASK mask) // ...build mode moves camera about focus point if (grab_center_gl.mX < ROTATE_H_MARGIN) { +#if 0 if (gAgentCamera.getFocusOnAvatar()) { gAgent.yaw(rotate_angle); } else +#endif { gAgentCamera.cameraOrbitAround(rotate_angle); } } else if (grab_center_gl.mX > gViewerWindow->getWorldViewWidthScaled() - ROTATE_H_MARGIN) { +#if 0 if (gAgentCamera.getFocusOnAvatar()) { gAgent.yaw(-rotate_angle); } else +#endif { gAgentCamera.cameraOrbitAround(-rotate_angle); } diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 3d57b6b4bb..c4aa75332d 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -246,6 +246,142 @@ void display_stats() } } + +// +// automatic draw distance adjustment +// +// This does a much better job than speed stepping, and it does it all +// the time. +// +bool rty_adda() +{ + static LLCachedControl addaenabled("RtyAddaEnabled"); + if(addaenabled) + { + // increase/decrease dd by this much per step + static LLCachedControl addadeltaup("RtyAddaUp"); + static LLCachedControl addadeltadn("RtyAddaDown"); + + // do not increase/decrease dd beyond these limits + // FIXME: there needs to be a lower and an upper hardcoded limit + static LLCachedControl addamindd("RtyAddaMinDD"); + static LLCachedControl addamaxdd("RtyAddaMaxDD"); + + // use average frame rate as reference for adjustment + static F32 addaavg = 0.0f; + static F32 addaavglast = addaavg; + static U32 addaframes = 0; + // how many frames to average + static LLCachedControl addaf2avg("RtyAddaFrames"); + if(addaf2avg < 10) + { + addaf2avg = 10; + gSavedSettings.setU32("RtyAddaFrames", 10); + } + + addaavg += gFrameIntervalSeconds; + addaframes++; + if(addaframes > addaf2avg) + { + addaavg /= (F32)addaframes; + addaframes = 0; + + static LLCachedControl addalevel("RtyAddaLevel"); + // note: dd gets reset in llagent.cpp after tp when speedstepping is enabled + // begin with medium dd + static F32 addadd = (addamindd + addamaxdd) * 0.5f; + + // when there's a big change in average, adjust dd rapidly + if(addaavg < addaavglast * 0.6f) + { + addadd *= 0.55f; + if(addadd < addamindd) + { + addadd = addamindd; + } + gSavedSettings.setF32("RenderFarClip", addadd); + } + else + { + bool changed = 0; + + if(addaavg > addalevel / 1000.0f) + { + // + // average fps rate too low + // + + if(addadd < addamindd) + { + addadd = addamindd; + gSavedSettings.setF32("RenderFarClip", addadd); + changed = 1; + // llinfos << "dd min: " << addadd << " (" << addaavg << ")" << llendl; + } + else + { + if(addadd != addamindd) + { + addadd -= addadeltadn; + gSavedSettings.setF32("RenderFarClip", addadd); + changed = 1; + // llinfos << "dd dec: " << addadd << " (" << addaavg << ")" << llendl; + } + } + } + else + { + // + // average fps rate high enough + // + + if(addadd < addamaxdd) + { + addadd += addadeltaup; + gSavedSettings.setF32("RenderFarClip", addadd); + changed = 1; + // llinfos << "dd inc: " << addadd << " (" << addaavg << ")" << llendl; + } + else + { + if(addadd != addamaxdd) + { + addadd = addamaxdd; + gSavedSettings.setF32("RenderFarClip", addadd); + changed = 1; + // llinfos << "dd max: " << addadd << " (" << addaavg << ")" << llendl; + } + } + } + + if(changed) + { + if(addadd < addamaxdd * 0.25) + { + // disable shadows when dd is forced rather low + gSavedSettings.setS32("RenderShadowDetail", 0); + } + else + { + if(addadd > addamaxdd * 0.85) + { + // enable shadows when dd has come up again + gSavedSettings.setS32("RenderShadowDetail", 2); + } + } + } + } + + addaavglast = addaavg; + } + + return true; + } + + return false; +} + + static LLFastTimer::DeclareTimer FTM_PICK("Picking"); static LLFastTimer::DeclareTimer FTM_RENDER("Render", true); static LLFastTimer::DeclareTimer FTM_UPDATE_SKY("Update Sky"); @@ -274,9 +410,9 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot, boo //Nope /*if (LLPipeline::sRenderDeferred) - { //hack to make sky show up in deferred snapshots - for_snapshot = FALSE; - }*/ + { //hack to make sky show up in deferred snapshots + for_snapshot = FALSE; + }*/ if (LLPipeline::sRenderFrameTest) { @@ -306,8 +442,8 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot, boo // In fact, must explicitly check the minimized state before drawing. // Attempting to draw into a minimized window causes a GL error. JC if ( !gViewerWindow->getActive() - || !gViewerWindow->getWindow()->getVisible() - || gViewerWindow->getWindow()->getMinimized() ) + || !gViewerWindow->getWindow()->getVisible() + || gViewerWindow->getWindow()->getMinimized() ) { // Clean up memory the pools may have allocated if (rebuild) @@ -386,9 +522,11 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot, boo stop_glerror(); LLImageGL::updateStats(gFrameTimeSeconds); - - LLVOAvatar::sRenderName = gSavedSettings.getS32("RenderName"); - LLVOAvatar::sRenderGroupTitles = !gSavedSettings.getBOOL("RenderHideGroupTitleAll"); + + static LLCachedControl rname("RenderName"); + static LLCachedControl rhgta("RenderHideGroupTitleAll"); + LLVOAvatar::sRenderName = rname; + LLVOAvatar::sRenderGroupTitles = !rhgta; gPipeline.mBackfaceCull = TRUE; gFrameCount++; @@ -464,7 +602,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot, boo case LLAgent::TELEPORT_START_ARRIVAL: // Transition to ARRIVING. Viewer has received avatar update, etc., from destination simulator gTeleportArrivalTimer.reset(); - gViewerWindow->setProgressCancelButtonVisible(FALSE, LLTrans::getString("Cancel")); + gViewerWindow->setProgressCancelButtonVisible(FALSE, LLTrans::getString("Cancel")); gViewerWindow->setProgressPercent(75.f); gAgent.setTeleportState( LLAgent::TELEPORT_ARRIVING ); gAgent.setTeleportMessage( @@ -476,34 +614,34 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot, boo case LLAgent::TELEPORT_ARRIVING: // Make the user wait while content "pre-caches" + { + F32 arrival_fraction = (gTeleportArrivalTimer.getElapsedTimeF32() / TELEPORT_ARRIVAL_DELAY); + if( arrival_fraction > 1.f || hide_tp_screen) { - F32 arrival_fraction = (gTeleportArrivalTimer.getElapsedTimeF32() / TELEPORT_ARRIVAL_DELAY); - if( arrival_fraction > 1.f || hide_tp_screen) - { - arrival_fraction = 1.f; - LLFirstUse::useTeleport(); - gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); - } - gViewerWindow->setProgressCancelButtonVisible(FALSE, LLTrans::getString("Cancel")); - gViewerWindow->setProgressPercent( arrival_fraction * 25.f + 75.f); - gViewerWindow->setProgressString(message); + arrival_fraction = 1.f; + LLFirstUse::useTeleport(); + gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); } - break; + gViewerWindow->setProgressCancelButtonVisible(FALSE, LLTrans::getString("Cancel")); + gViewerWindow->setProgressPercent( arrival_fraction * 25.f + 75.f); + gViewerWindow->setProgressString(message); + } + break; case LLAgent::TELEPORT_LOCAL: // Short delay when teleporting in the same sim (progress screen active but not shown - did not // fall-through from TELEPORT_START) + { + // + // is this really needed.... I say no. + //if( gTeleportDisplayTimer.getElapsedTimeF32() > TELEPORT_LOCAL_DELAY ) + // { - // - // is this really needed.... I say no. - //if( gTeleportDisplayTimer.getElapsedTimeF32() > TELEPORT_LOCAL_DELAY ) - // - { - LLFirstUse::useTeleport(); - gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); - } + LLFirstUse::useTeleport(); + gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); } - break; + } + break; case LLAgent::TELEPORT_NONE: // No teleport in progress @@ -513,10 +651,10 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot, boo break; default: - break; + break; } } - else if(LLAppViewer::instance()->logoutRequestSent()) + else if(LLAppViewer::instance()->logoutRequestSent()) { LLAppViewer::instance()->pingMainloopTimeout("Display:Logout"); F32 percent_done = gLogoutTimer.getElapsedTimeF32() * 100.f / gLogoutMaxTime; @@ -533,48 +671,54 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot, boo gViewerWindow->setProgressPercent( percent_done ); } else - if (gRestoreGL) - { - LLAppViewer::instance()->pingMainloopTimeout("Display:RestoreGL"); - F32 percent_done = gRestoreGLTimer.getElapsedTimeF32() * 100.f / RESTORE_GL_TIME; - if( percent_done > 100.f ) - { - gViewerWindow->setShowProgress(FALSE); - gRestoreGL = FALSE; - } - else + if (gRestoreGL) { - - if( LLApp::isExiting() ) + LLAppViewer::instance()->pingMainloopTimeout("Display:RestoreGL"); + F32 percent_done = gRestoreGLTimer.getElapsedTimeF32() * 100.f / RESTORE_GL_TIME; + if( percent_done > 100.f ) { - percent_done = 100.f; + gViewerWindow->setShowProgress(FALSE); + gRestoreGL = FALSE; } + else + { + + if( LLApp::isExiting() ) + { + percent_done = 100.f; + } - gViewerWindow->setProgressPercent( percent_done ); + gViewerWindow->setProgressPercent( percent_done ); + } } - } - // Progressively increase draw distance after TP when required. - if (gSavedDrawDistance > 0.0f && gAgent.getTeleportState() == LLAgent::TELEPORT_NONE) + // speed rezzing is pretty pointless when adda is enabled, so + // don't do it in that case + if(!rty_adda()) { - if (gTeleportArrivalTimer.getElapsedTimeF32() >= - (F32)gSavedSettings.getU32("SpeedRezInterval")) + // Progressively increase draw distance after TP when required. + if (gSavedDrawDistance > 0.0f && gAgent.getTeleportState() == LLAgent::TELEPORT_NONE) { - gTeleportArrivalTimer.reset(); - F32 current = gSavedSettings.getF32("RenderFarClip"); - if (gSavedDrawDistance > current) + if (gTeleportArrivalTimer.getElapsedTimeF32() >= + (F32)gSavedSettings.getU32("SpeedRezInterval")) { - current *= 2.0; - if (current > gSavedDrawDistance) + gTeleportArrivalTimer.reset(); + F32 current = gSavedSettings.getF32("RenderFarClip"); + if (gSavedDrawDistance > current) { - current = gSavedDrawDistance; + // current *= 2.0; + current += current; + if (current > gSavedDrawDistance) + { + current = gSavedDrawDistance; + } + gSavedSettings.setF32("RenderFarClip", current); + } + if (current >= gSavedDrawDistance) + { + gSavedDrawDistance = 0.0f; + gSavedSettings.setF32("SavedRenderFarClip", 0.0f); } - gSavedSettings.setF32("RenderFarClip", current); - } - if (current >= gSavedDrawDistance) - { - gSavedDrawDistance = 0.0f; - gSavedSettings.setF32("SavedRenderFarClip", 0.0f); } } } @@ -686,8 +830,8 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot, boo gFrameStats.start(LLFrameStats::UPDATE_CULL); S32 water_clip = 0; if ((LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_ENVIRONMENT) > 1) && - (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_WATER) || - gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_VOIDWATER))) + (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_WATER) || + gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_VOIDWATER))) { if (LLViewerCamera::getInstance()->cameraUnderWater()) { @@ -708,9 +852,9 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot, boo LLTexUnit::sWhiteTexture = LLViewerFetchedTexture::sWhiteImagep->getTexName(); /*if (LLPipeline::sUseOcclusion && LLPipeline::sRenderDeferred) - { //force occlusion on for all render types if doing deferred render - LLPipeline::sUseOcclusion = 3; - }*/ + { //force occlusion on for all render types if doing deferred render + LLPipeline::sUseOcclusion = 3; + }*/ S32 occlusion = LLPipeline::sUseOcclusion; if (gDepthDirty) @@ -734,7 +878,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot, boo LLGLState::checkClientArrays(); BOOL to_texture = LLGLSLShader::sNoFixedFunction && - LLPipeline::sRenderGlow; + LLPipeline::sRenderGlow; LLAppViewer::instance()->pingMainloopTimeout("Display:Swap"); @@ -824,7 +968,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot, boo { LLFastTimer t(FTM_IMAGE_UPDATE_CLASS); LLViewerTexture::updateClass(LLViewerCamera::getInstance()->getVelocityStat()->getMean(), - LLViewerCamera::getInstance()->getAngularVelocityStat()->getMean()); + LLViewerCamera::getInstance()->getAngularVelocityStat()->getMean()); } @@ -835,17 +979,21 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot, boo { LLFastTimer t(FTM_IMAGE_UPDATE_LIST); - F32 max_image_decode_time = 0.050f*gFrameIntervalSeconds; // 50 ms/second decode time - max_image_decode_time = llclamp(max_image_decode_time, 0.002f, 0.005f ); // min 2ms/frame, max 5ms/frame) - gTextureList.updateImages(max_image_decode_time); + // F32 max_image_decode_time = 0.050f*gFrameIntervalSeconds; // 50 ms/second decode time + // llinfos << "frame-i: " << gFrameIntervalSeconds << llendl; + // max_image_decode_time = llclamp(max_image_decode_time, 0.002f, 0.005f ); // min 2ms/frame, max 5ms/frame) + // gTextureList.updateImages(max_image_decode_time); + // F32 max_image_decode_time = 0.001f / gFrameIntervalSeconds; + // if(max_image_decode_time > 0.0015f) gTextureList.updateImages(max_image_decode_time); + gTextureList.updateImages(gFrameIntervalSeconds); } /*{ - LLFastTimer t(FTM_IMAGE_UPDATE_DELETE); - //remove dead textures from GL - LLImageGL::deleteDeadTextures(); - stop_glerror(); - }*/ + LLFastTimer t(FTM_IMAGE_UPDATE_DELETE); + //remove dead textures from GL + LLImageGL::deleteDeadTextures(); + stop_glerror(); + }*/ } LL_PUSH_CALLSTACKS(); LLGLState::checkStates(); @@ -973,7 +1121,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot, boo LLAppViewer::instance()->pingMainloopTimeout("Display:RenderGeom"); if (!(LLAppViewer::instance()->logoutRequestSent() && LLAppViewer::instance()->hasSavedFinalSnapshot()) - && !gRestoreGL) + && !gRestoreGL) { LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_WORLD; @@ -999,10 +1147,10 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot, boo gGL.color4fv( LLColor4::white.mV ); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.begin( LLRender::QUADS ); - gGL.vertex3f(rect.mLeft, rect.mTop,0.f); - gGL.vertex3f(rect.mLeft, rect.mBottom,0.f); - gGL.vertex3f(rect.mRight, rect.mBottom,0.f); - gGL.vertex3f(rect.mRight, rect.mTop,0.f); + gGL.vertex3f(rect.mLeft, rect.mTop,0.f); + gGL.vertex3f(rect.mLeft, rect.mBottom,0.f); + gGL.vertex3f(rect.mRight, rect.mBottom,0.f); + gGL.vertex3f(rect.mRight, rect.mTop,0.f); gGL.end(); gGL.matrixMode(LLRender::MM_PROJECTION); @@ -1075,10 +1223,10 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot, boo if(gPipeline.mDeferredScreen.getFBO()) { LLRenderTarget::copyContentsToFramebuffer(gPipeline.mDeferredScreen, 0, 0, gPipeline.mDeferredScreen.getWidth(), - gPipeline.mDeferredScreen.getHeight(), 0, 0, - gPipeline.mDeferredScreen.getWidth(), - gPipeline.mDeferredScreen.getHeight(), - GL_DEPTH_BUFFER_BIT, GL_NEAREST); + gPipeline.mDeferredScreen.getHeight(), 0, 0, + gPipeline.mDeferredScreen.getWidth(), + gPipeline.mDeferredScreen.getHeight(), + GL_DEPTH_BUFFER_BIT, GL_NEAREST); } } else @@ -1087,10 +1235,10 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot, boo if(gPipeline.mScreen.getFBO()) { LLRenderTarget::copyContentsToFramebuffer(gPipeline.mScreen, 0, 0, gPipeline.mScreen.getWidth(), - gPipeline.mScreen.getHeight(), 0, 0, - gPipeline.mScreen.getWidth(), - gPipeline.mScreen.getHeight(), - GL_DEPTH_BUFFER_BIT, GL_NEAREST); + gPipeline.mScreen.getHeight(), 0, 0, + gPipeline.mScreen.getWidth(), + gPipeline.mScreen.getHeight(), + GL_DEPTH_BUFFER_BIT, GL_NEAREST); } } } diff --git a/indra/newview/llviewerdisplay.h b/indra/newview/llviewerdisplay.h index 50a0060943..79e380a19c 100644 --- a/indra/newview/llviewerdisplay.h +++ b/indra/newview/llviewerdisplay.h @@ -38,6 +38,8 @@ class LLPostProcess; void display_startup(); void display_cleanup(); +bool rty_adda(); + void display(BOOL rebuild = TRUE, F32 zoom_factor = 1.f, int subfield = 0, BOOL for_snapshot = FALSE, bool tiling = false); extern BOOL gDisplaySwapBuffers; diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index d2fdcb6fa8..63a03bf6e0 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -1727,74 +1727,1960 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& return false; } -void script_msg_api(const std::string& msg); -bool is_spam_filtered(const EInstantMessage& dialog, bool is_friend, bool is_owned_by_me) + +// xantispam +// (Start reading below, at xantispam_check().) + +#include +#include + + +#define XANTISPAM_QUERYUSER 10 +#define XANTISPAM_PERMBLACK 0 +#define XANTISPAM_PERMWHITE 1 +#define XANTISPAM_NOP 2 +#define XANTISPAM_TEMPBLACK 3 +#define XANTISPAM_TEMPWHITE 4 +#define XANTISPAM_CLEARCACHE 5 +#define XANTISPAM_ENABLE 6 +#define XANTISPAM_DISABLE_QUERIES 7 +#define XANTISPAM_UNNOTIFY 8 +// these are for cache control in xantispam_check() +#define XANTISPAM_ADDBLACK "XAAdd2Black:" +#define XANTISPAM_ADDWHITE "XAAdd2White:" + +// filename is configurable +#define XANTISPAM_WHITELISTFILE "xantispam-whitelist.org" +#define XANTISPAM_BLACKLISTFILE "xantispam-blacklist.org" + +// Max number of entries each cache can hold. Caches are handled +// FILO, with most recent entries at the end. Most recent entries are +// assumed to be the most likely ones to be looked up (again), so +// cache lookups start at the end of the caches. +// +#define XANTISPAM_CACHE_CAPACITY 65535 +// XANTISPAM_THRESHOLD defines how many entries to remove when the +// cache flows over +#define XANTISPAM_THRESHOLD 600 +// +#define XANTISPAM_CLEARCACHE_RQSTRING "XAClearCache" +#define XANTISPAM_SPECIAL_ADDBLACK 32 +#define XANTISPAM_SPECIAL_ADDWHITE 33 +#define XANTISPAM_SPECIAL_CLEARCACHE 34 +#define XANTISPAM_SPECIAL_INVALID 30 + +// When changing these defines, change numbers in ascentprefschat.cpp +// accordingly (or better, use same defines there). +#define XANTISPAM_CLEARCACHE_PERSISTENT 20 +#define XANTISPAM_CLEARCACHE_VOLATILE 21 +#define XANTISPAM_EDIT_BLACKLIST 22 +#define XANTISPAM_EDIT_WHITELIST 23 + + +// structure to help decision making when queries are generated +typedef struct +{ + bool isblacklisted; + bool iswhitelisted; +} xantispam_blackwhite; + + +// structure to store a request +typedef struct +{ + std::string from; + std::string type; +} xantispam_request; + + +static xantispam_blackwhite xantispam_notify(const xantispam_request *, const int, const std::string&); +bool xantispam_check(const std::string&, const std::string&, const std::string&); +void xantispam_buttons(const int); + + +// read from a file until either the buffer is full, the line ends +// with '\n', or the whole file has been read +// +static int xantispam_read_line(LLFILE *src, std::string& line, std::size_t max) +{ + // reserve line to max before calling and clear for each line + int c; + std::size_t offset = 0; + while(((c = fgetc(src)) != EOF) && (c != '\n') && (offset < max)) + { + line.push_back(c); + offset++; + } + LL_DEBUGS("xantispam") << "returning" << LL_ENDL; + return ((c == EOF) || (c != '\n')); +} + + +// requests fed to xantispam_check() must go to through this syntax +// transformation +// +void xantispam_apply_syntax(xantispam_request *rq) +{ + // strip whitespace + rq->from.erase(std::remove_if(rq->from.begin(), rq->from.end(), isspace), rq->from.end()); + + // replace ":" with ";" + boost::algorithm::replace_all(rq->from, ":", ";"); + + // same for request type + rq->type.erase(std::remove_if(rq->type.begin(), rq->type.end(), isspace), rq->type.end()); + boost::algorithm::replace_all(rq->type, ":", ";"); +} + + +// Convert a line read from a blacklist or whitelist file into a +// request. This request is called a rule (because it's in a file); +// requests are matched against the rule. The data type is the same +// as for requests. +// +// Returns false on success. +// +// First a helper function, returning true on error: +static bool xantispam_syntaxofline(const std::string& line) +{ + if(line.empty()) + { + return true; + } + + if(line.find(':') != std::string::npos) + { + if(line.find_first_not_of(":") != std::string::npos) + { + llinfos << "rule syntax error: wildcards are not allowed amongst other characters in '" << line << "'" << llendl; + return true; + } + } + return false; +} +// +// This transforms a line from an emacs org-mode table into an +// xantispam_request structure. Whitespace is stripped, lines +// starting with "|-" and not with '|' are skipped, comments are +// removed and the syntax is checked. +// +static bool xantispam_line2request(std::string& line, xantispam_request *request) +{ + // ignore emtpy lines and comments + if(line.empty()) + { + LL_DEBUGS("xantispam") << "skipping empty line: " << line << LL_ENDL; + return true; + } + if((line.at(0) != '|') || !line.find("|-")) + { + LL_DEBUGS("xantispam") << "skipping comment: " << line << LL_ENDL; + return true; + } + + // truncate inlined comments + std::size_t comment = line.find_first_of("#["); + if(comment != std::string::npos) line = line.substr(0, comment - 1); + + // strip whitespace + line.erase(std::remove_if(line.begin(), line.end(), isspace), line.end()); + + // split the line + std::vector elements; + boost::algorithm::split(elements, line, boost::algorithm::is_any_of("|")); + + // transform into an xantispam_request + if(elements.size() > 2) + { + // elements[0] is empty because lines start with '|' + request->from = elements[1]; + request->type = elements[2]; + // check syntax + LL_DEBUGS("xantispam") << "syntax check on: '" << line << "' --> [" << request->from << "]{" << request->type << "}" << LL_ENDL; + return (xantispam_syntaxofline(request->from) || xantispam_syntaxofline(request->type)); + } + + return true; +} + + +// See if a rule matches a request and return true when they match. +// +// Order does matter because the rule must be searched within the +// request rather than the request within the rule: Requests can have +// additional data appended to the type, like the name of an object. +// This allows rules using the additional information, for example for +// the rule "::greeter", to block dialogs from anything that has +// 'greeter' somewhere in its name. +// +// The colon is used for a wildcard that matches anything. This is +// especially useful for blocking requests with particular filter +// types from any source and for the formation of rules using the +// additional data. +// +static bool xantispam_wildcardmatch(const std::string& a, const std::string& b, const bool by_find) +{ + if((a == ":") || (b == ":")) + { + return true; + } + if(by_find) + { + return (b.find(a) != std::string::npos); + } + return (a == b); +} +static bool xantispam_compare_requests(const xantispam_request *rule, const xantispam_request *request) +{ + LL_DEBUGS("xantispam") << "comparing rule [" << rule->from << "]{" << rule->type << "} with [" << request->from << "]{" << request->type << "}" << LL_ENDL; + + if(!xantispam_wildcardmatch(rule->from, request->from, false)) + { + return false; + } + return xantispam_wildcardmatch(rule->type, request->type, true); +} + + +// Look up a rule in a whitelist or blacklist file and fill the +// corresponding cache while reading. Return false when a rule +// matching the request which is looked up is found. +// +static bool xantispam_filelookup(bool type, const bool prefill, const xantispam_request *request, std::vector& cache) +{ + bool ret = true; + bool isdecided = false; + bool full = false; + LLFILE *f = LLFile::fopen(gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter() + (type ? XANTISPAM_WHITELISTFILE : XANTISPAM_BLACKLISTFILE), "r"); + if(f) + { + std::string line; + line.reserve(1024); + xantispam_request lookat; + lookat.from.reserve(36); + lookat.type.reserve(1024 - 36); + LL_DEBUGS("xantispam") << "starting readline() on " << gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter() + (type ? XANTISPAM_WHITELISTFILE : XANTISPAM_BLACKLISTFILE) << LL_ENDL; + while(!xantispam_read_line(f, line, 1024)) + { + if(!xantispam_line2request(line, &lookat)) + { + if(prefill) + { + // If this rule from the file was already cached, it wouldn't be + // looked up here with pre-filling enabled unless there are more + // rules in the file than the cache can hold. + if(cache.size() > XANTISPAM_CACHE_CAPACITY) + { + // cut 1/2 of the entries off at the end + // Perhaps 1/3 is better; can be even less when + // memory isn't freed and re-allocated but re-used. + cache.resize((XANTISPAM_CACHE_CAPACITY >> 1)); + full = true; + } + cache.push_back(lookat); + } + if(!isdecided) + { + if(xantispam_compare_requests(&lookat, request)) // order does matter + { + ret = false; + isdecided = true; + LL_DEBUGS("xantispam") << "decided: [" << request->from << "]{" << request->type << "} by [" << lookat.from << "]{" << lookat.type << "}" << LL_ENDL; + } + } + else + { + if(!prefill) + { + break; + } + } + } + line.clear(); + } + fclose(f); + } + else + { + llinfos << "cannot open " << gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter() + (type ? XANTISPAM_WHITELISTFILE : XANTISPAM_BLACKLISTFILE) << " for reading" << llendl; + } + + if(full) + { + LLSD args; + args["TYPE"] = (type ? "Whitelist" : "Blacklist"); + LLNotificationsUtil::add("xantispamNadvfull", args); + } + + return ret; +} + +// cache lookup functions: two different functions here rather than +// one that does both depending on a flag, for better performance +// +// Look up a request in a blacklist or whitelist cache, return false +// if the request is on cache, else return true --- searches backwards +// because most recent elements are expected to be at end of cache. +// +// Compares "normal" (forward): search rule within request. +// +static bool xantispam_cachelookup(const std::vector& cache, const xantispam_request *data) +{ + std::vector::const_iterator it = cache.end(); + while(it != cache.begin() ) + { + --it; + if(xantispam_compare_requests(&(*it), data)) // order does matter + { + return false; + } + } + return true; +} + +// Look up a request in a blacklist or whitelist cache, return false +// if the request is on cache, else return true --- searches backwards +// because most recent elements are expected to be at end of cache. +// +// Compares inversed (backward): search request within the rule. +// +static xantispam_request xantispam_inverse_cachelookup(const std::vector& cache, const xantispam_request *data, bool *found) +{ + std::vector::const_iterator it = cache.end(); + while(it != cache.begin() ) + { + --it; + if(it->type.find(':') == std::string::npos) // rule must not be wildcard + { + if(xantispam_compare_requests(data, &(*it))) // order does matter, is inversed! + { + *found = false; + return *it; + } + } + } + *found = true; + return *data; +} + + +// handle lookups and cache pre-filling transparently +// +// "Transparently" means that when a rule is not found on cache, it is +// looked up in the corresponding file without making a difference for +// the calling function. Lookups on background and silent requests +// should not use this because for every rule that isn't cached, a +// file lookup is performed (and a stupid number of file lookups can +// result). +// +// This is a bit awkward considering xantispam_lookup_selectively() +// which prefills the caches once. Yet the file lookup is only +// performed when the rule is not found on cache, and when the cache +// already has been prefilled, it shall be found and a file lookup is +// averted. +// +static bool xantispam_transparentlookup(std::vector& cache, const xantispam_request *search, const bool type) +{ + static bool black = true; + static bool white = true; + + LL_DEBUGS("xantispam") << "lookup type: " << (type ? "white" : "black") << LL_ENDL; + + // This greatly simplifies the logic in xantispam_check(), plus it + // makes it possible to prefill caches on demand while already + // searching through the files anyway. + bool ret = xantispam_cachelookup(cache, search); + if(ret) + { + ret = xantispam_filelookup(type, (type ? white : black), search, cache); + LL_DEBUGS("xantispam") << "filelookup done" << LL_ENDL; + if(!ret) + { + // make sure the request found in file is on cache + if(xantispam_cachelookup(cache, search)) + { + if(cache.size() > XANTISPAM_CACHE_CAPACITY) + { + // take off not so many to keep the cache full so it doesn't get + // prefilled again from top of file during file lookups + cache.erase(cache.begin(), cache.begin() + XANTISPAM_CACHE_CAPACITY / XANTISPAM_THRESHOLD / 2 + 1); + } + cache.push_back(*search); + LL_DEBUGS("xantispam") << "rule put back on " << (type ? "white" : "black") << "cache: [" << search->from << "]{" << search->type << "}" << LL_ENDL; + + } + } + // Prefilling is needed only once because when caches are emptied by + // the user, they are prefilled otherwise. New rules are put onto + // the caches right away. + if(type) + { + white = false; + } + else + { + black = false; + } + } + return ret; +} + + +// prefill a cache +// +// This is different from prefilling on demand through +// xantispam_filelookup() in that it makes an estimation about where +// to actually start reading depending on the number of lines in the +// file. This is so as to put only the (supposedly) most recent rules +// into the cache, i. e. the ones at the end of the file. +// +// This should only be called once, after that only on user demand. +// Clear the cache before calling this to avoid dupes! +// +static void xantispam_prefill_cache(std::vector& cache, const bool type) +{ + // figure out which file to read from + std::string fn(gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter() + (type ? XANTISPAM_WHITELISTFILE : XANTISPAM_BLACKLISTFILE)); + + LLFILE *f = LLFile::fopen(fn, "r"); + if(f) + { + // I refuse to read the file backwards, so find a file position + // to start reading at. + std::size_t lines = 0; + char c; + while((c = fgetc(f)) != EOF) + { + if(c == '\n') + { + lines++; + } + } + rewind(f); + if(lines > XANTISPAM_CACHE_CAPACITY - XANTISPAM_CACHE_CAPACITY / XANTISPAM_THRESHOLD) + { + std::size_t start = lines - XANTISPAM_CACHE_CAPACITY + XANTISPAM_CACHE_CAPACITY / XANTISPAM_THRESHOLD; + lines = 0; + while((lines < start) && ((c = fgetc(f)) != EOF)) + { + if(c == '\n') + { + lines++; + } + } + LL_DEBUGS("xantispam") << fn << " might have a greater number of rules than the maximum number of cache entries: skipped to line " << lines << LL_ENDL; + cache.reserve(XANTISPAM_CACHE_CAPACITY); + } + // start filling + std::string agentuuid(gAgentID.asString()); + std::string line; + line.reserve(1024); + xantispam_request add; + add.from.reserve(36); + add.type.reserve(1024 - 36); + while(!xantispam_read_line(f, line, 1024)) + { + if(!xantispam_line2request(line, &add) ) + { + // Not checking for dupes here because it's about 120+ times as fast + // without. Still checking for overflow since someone might manage to + // append another 5 billion entries to the file while the cache is + // still being filled :) + if(cache.size() > XANTISPAM_CACHE_CAPACITY) + { + llwarns << "an attempt to overfill the cache has been defeated, aborting pre-fill" << llendl; + break; + } + cache.push_back(add); + } + line.clear(); + } + fclose(f); + } + + if(gSavedSettings.getBOOL("AntiSpamNotify")) + { + LLSD args; + args["TYPE"] = (type ? "Whitelist" : "Blacklist"); + args["ENTRIES"] = boost::lexical_cast(cache.size()); + args["PERCENTAGE"] = boost::lexical_cast(cache.size() * 100 / XANTISPAM_CACHE_CAPACITY); + LLNotificationsUtil::add("xantispamNfillcache", args); + } +} + +// make a cute timestamp +// +static std::string xantispam_get_timestamp(void) +{ + char timestamp[32]; + timestamp[0] = '\0'; + time_t t; + if(time(&t) != -1) + { + if(!strftime(timestamp, 31, "[%F %H:%M:%S]", localtime(&t))) + { + timestamp[0] = '\0'; + } + } + std::string ts(timestamp); + return ts; +} + + +// When the white or blacklist doesn't exist yet, create a header with +// some information for users who edit it. +// +// There must be a better way to do this. There should be more +// information in the header. +// +static void xantispam_make_listheader(const std::string& filename) +{ + if(LLFile::isfile(filename) || LLFile::isdir(filename)) + { + // what about links? + return; + } + + LLFILE *f = LLFile::fopen(filename, "a"); + if(f) { + const std::string header = filename + " -*- mode: org; -*-" + "\n\n* Created\n\n" + xantispam_get_timestamp() + "\nRules must be in an emacs org-mode table like below.\n\n* Rules\n|---------------------+-------------------+-------------+-----------|\n|[ | | [Timestamp] | [Comment] ]|\n|---------------------+-------------------+-------------+-----------|\n"; + + if(fwrite(header.c_str(), header.length(), 1, f) != 1) + { + llwarns << "fwrite() failed to append header to " << filename << llendl; + if(ferror(f)) + { + clearerr(f); + } + } + fclose(f); + } +} + + +// Append a new rule entry to a whitelist or blacklist file. Each +// entry in the lists has a timestamp to allow cleanups. Hopefully, +// cleanups won't be neeeded because with black- and whitelisting and +// by making use of wildcards and object names in rules, the number of +// required rules can be kept small. +// +static void xantispam_make_listentry(const bool type, const xantispam_request *data, const std::string& from_name) +{ + // No dupes should get entered into the lists. This sometimes + // requires another file lookup to verify that the entry + // doesn't already exist. This may change. + + static xantispam_request lastrq; + static bool lasttype; + + // Order basically does matter for comparison, but since this deals + // with requests that have been formed internally and not with rules + // from files, it doesn't matter. + if(xantispam_compare_requests(&lastrq, data)) + { + LL_DEBUGS("xantispam") << "rule entry denied: previous rule for " << (lasttype ? XANTISPAM_WHITELISTFILE : XANTISPAM_BLACKLISTFILE) << " matches current rule: [" << lastrq.from << "]{" << lastrq.type << "}" << LL_ENDL; + return; + } + lastrq.from = data->from; + lastrq.type = data->type; + lasttype = type; + + std::string whichlist(gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter() + (type ? XANTISPAM_WHITELISTFILE : XANTISPAM_BLACKLISTFILE)); + + // write user-friendly header to list if it doesn't exist + xantispam_make_listheader(whichlist); + + std::vector foo; + if(xantispam_filelookup(type, false, data, foo)) + { + LL_DEBUGS("xantispam") << "rule [" << data->from << "]{" << data->type << "} shall enter " << (type ? XANTISPAM_WHITELISTFILE : XANTISPAM_BLACKLISTFILE) << LL_ENDL; + LLFILE *f = LLFile::fopen(whichlist, "a"); + if(f) + { + std::string listentry("| " + data->from + " | " + data->type + " | " + xantispam_get_timestamp() + " | " + from_name + " |\n"); + if(fwrite(listentry.c_str(), listentry.length(), 1, f) != 1) + { + llwarns << "fwrite() failed to write rule to file '" << listentry << "' to " << whichlist << llendl; + if(ferror(f)) + { + clearerr(f); + } + } + fclose(f); + } + else + { + llinfos << "cannot open " << whichlist << " for appending" << llendl; + } + } + else + { + llinfos << "rule entry denied: rule '" << lastrq.from << "', '" << lastrq.type << "' is already in " << whichlist << llendl; + } +} + + +// split a special request to xantispam_check() into something useable +// +static int xantispam_split_special(const std::string special, xantispam_request *request) +{ + std::size_t where = special.find(":"); + if(where != std::string::npos) + { + where++; + std::size_t wend = special.find(":", where); + if(wend != std::string::npos) + { + request->from = special.substr(where, wend - where); + request->type = special.substr(wend + 1); + } + } + + where = special.find(XANTISPAM_ADDBLACK); + if(where != std::string::npos) { + return XANTISPAM_SPECIAL_ADDBLACK; + } + where = special.find(XANTISPAM_ADDWHITE); + if(where != std::string::npos) { + return XANTISPAM_SPECIAL_ADDWHITE; + } + where = special.find(XANTISPAM_CLEARCACHE_RQSTRING); + if(where != std::string::npos) { + return XANTISPAM_SPECIAL_CLEARCACHE; + } + return XANTISPAM_SPECIAL_INVALID; +} + + +// callback for xantispam_check() --- act upon buttons clicked by the +// user when they received a notification +// +static void xantispam_notify_cb(const LLSD& notification, const LLSD& response, const xantispam_request data, const std::string from_name) +{ + int action = LLNotificationsUtil::getSelectedOption(notification, response); + + switch(action) + { + case XANTISPAM_PERMBLACK: + // Permanently Blacklist + xantispam_make_listentry(false, &data, from_name); + // save another file lookup by adding request to cache right away + xantispam_check(gAgentID.asString(), XANTISPAM_ADDBLACK + data.from + ":" + data.type, ""); + break; + case XANTISPAM_PERMWHITE: + // Permanently Whitelist + xantispam_make_listentry(true, &data, from_name); + // save another file lookup by adding request to cache right away + xantispam_check(gAgentID.asString(), XANTISPAM_ADDWHITE + data.from + ":" + data.type, ""); + break; + case XANTISPAM_NOP: + // do nothing + break; + case XANTISPAM_TEMPBLACK: + // Temporarily Blacklist + xantispam_notify(&data, XANTISPAM_TEMPBLACK, from_name); + break; + case XANTISPAM_TEMPWHITE: + // Temporarily Whitelist + xantispam_notify(&data, XANTISPAM_TEMPWHITE, from_name); + break; + case XANTISPAM_ENABLE: + xantispam_buttons(XANTISPAM_ENABLE); + break; + case XANTISPAM_DISABLE_QUERIES: + gSavedSettings.setBOOL("AntiSpamXtendedQueries", 0); + LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", "Queries have been disabled. You can change the settings in the preferences floater, Adv. Chat-->Spam.")); + default: + llwarns << "unknown case" << llendl; + } + xantispam_notify(&data, XANTISPAM_UNNOTIFY, from_name); +} + +// Deal with volatiles rules: When persistent rules didn't lead to a +// decision, examine the volatile rules. In case they don't render a +// decision, query the user. +// +static xantispam_blackwhite xantispam_notify(const xantispam_request *data, const int action, const std::string& from_name) +{ + // cache these for better performance + static LLCachedControl use_notify(gSavedSettings, "AntiSpamNotify"); + static LLCachedControl use_queries(gSavedSettings, "AntiSpamXtendedQueries"); + + // volatile request caches here, filled from answers to user requests + static std::vector whitecache; + static std::vector blackcache; + + // keep track of what has been queried about to avoid duplicate queries + static std::vector notificationcache; + + xantispam_blackwhite ret; + ret.isblacklisted = ret.iswhitelisted = false; + + switch(action) + { + case XANTISPAM_QUERYUSER: + // Order is "deny", "allow", same as persistent rules. + if(!xantispam_cachelookup(blackcache, data)) + { + // deny when blacklisted + ret.isblacklisted = true; + return ret; + } + + // check cache if whitelisted + ret.iswhitelisted = !xantispam_cachelookup(whitecache, data); + if(!ret.iswhitelisted && use_queries) + { + if(xantispam_cachelookup(notificationcache, data)) + { + // ask when undecided + LL_DEBUGS("xantispam") << "adding notification" << LL_ENDL; + LLSD args; + args["FROM"] = data->from + " (" + from_name + ")"; + args["TYPE"] = data->type; + LLNotificationsUtil::add("xantispamVolatilePersistent", args, LLSD(), boost::bind(&xantispam_notify_cb, _1, _2, *data, from_name)); + LL_DEBUGS("xantispam") << "done adding notification" << LL_ENDL; + + // keep track of what was notified about + if(notificationcache.size() > XANTISPAM_CACHE_CAPACITY) + { + notificationcache.erase(notificationcache.begin(), notificationcache.begin() + XANTISPAM_CACHE_CAPACITY / XANTISPAM_THRESHOLD + 1); + } + notificationcache.push_back(*data); + } + else + { + // default to blacklist a repeated request + ret.isblacklisted = true; + } + } + break; + case XANTISPAM_TEMPWHITE: + // put request on temp whitecache unless already there + if(xantispam_cachelookup(whitecache, data)) + { + if(whitecache.size() > XANTISPAM_CACHE_CAPACITY) + { + whitecache.erase(whitecache.begin(), whitecache.begin() + XANTISPAM_CACHE_CAPACITY / XANTISPAM_THRESHOLD + 1); + } + whitecache.push_back(*data); + } + ret.iswhitelisted = true; + break; + case XANTISPAM_TEMPBLACK: + // put request on temp blackcache unless already there + if(xantispam_cachelookup(blackcache, data)) + { + if(blackcache.size() > XANTISPAM_CACHE_CAPACITY) + { + blackcache.erase(blackcache.begin(), blackcache.begin() + XANTISPAM_CACHE_CAPACITY / XANTISPAM_THRESHOLD + 1); + } + blackcache.push_back(*data); + } + ret.isblacklisted = true; + break; + case XANTISPAM_CLEARCACHE: + llinfos << "clearing entries from volatile blackcache: " << blackcache.size() << llendl; + llinfos << "clearing entries from volatile whitecache: " << whitecache.size() << llendl; + llinfos << "clearing entries from notifications cache: " << notificationcache.size() << llendl; + whitecache.clear(); + blackcache.clear(); + notificationcache.clear(); + if(use_notify) + { + LLSD args; + args["TYPE"] = "Volatile"; + LLNotificationsUtil::add("xantispamNclrcache", args); + } + break; + case XANTISPAM_UNNOTIFY: + { + std::vector::iterator it = notificationcache.end(); + while(it != notificationcache.begin() ) + { + --it; + if(xantispam_compare_requests(&(*it), data)) + { + notificationcache.erase(it); + LL_DEBUGS("xantispam") << "Request removed from notifications cache: [" << data->from << "]{" << data->type << "}" << LL_ENDL; + } + } + } + } + return ret; +} + + +// For instances of extreme spamming just block. This is decided by +// how many calls there have been over a period of time (calls per 10 +// seconds). +// +static bool xantispam_callspersec(void) +{ + static LLCachedControl max_calls_per_10sec(gSavedSettings, "AntiSpamXtendedMaxCallsPer10Sec"); + if(max_calls_per_10sec > 20000) + { + gSavedSettings.setU32("AntiSpamXtendedMaxCallsPer10Sec", static_cast(20000)); + LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", "AntiSpamXtendedMaxCallsPer10Sec reset to maximum of 90.")); + } + if(max_calls_per_10sec < 150) + { + gSavedSettings.setU32("AntiSpamXtendedMaxCallsPer10Sec", static_cast(150)); + LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", "AntiSpamXtendedMaxCallsPer10Sec reset to minimum of 5.")); + } + static time_t lastcall = (time_t)0; + static unsigned long long calls_total = 0; + static unsigned long long calls_last = 0; + static bool status = false; + + calls_total++; + + // how long ago was the last call? + time_t now; + if(time(&now) != -1) + { + int delta = (int)difftime(now, lastcall); + lastcall = now; + + // too many calls since then? + if((delta < 10) && (calls_total - calls_last > max_calls_per_10sec)) + { + if(!status) + { + status = true; + + LLSD args; + args["CALLS"] = boost::lexical_cast((calls_total - calls_last)); + args["MAXCALLS"] = boost::lexical_cast(max_calls_per_10sec); + LLNotificationsUtil::add("xantispamNtimeout", args); + } + } + else + { + // not too many calls anymore + if(delta > 9) + { + calls_last = calls_total; + status = false; + } + } + } + else + { + LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", "Calling the time() function has failed. This can break the XAntiSpam flood protection, and all incoming dialogs may be denied without applying rules.")); + llwarns << "call to time() failed!" << llendl; + return true; + } + return status; +} + + +// a process launcher that picks what to launch from a rule +// Return false on error, otherwise true, even on dryrun. +// +// A dryrun sets up to start the external process and informs the user +// what would be run without actually running it. +// +static bool xantispam_process_launcher(const std::string& rule, const std::string& info) +{ + // Limit what can be executed --- some security is advisable + // here since users may mistype their rules. Only what is + // explicitly allowed in this setting may be executed. The + // setting is a list of allowed executables, entries delimited + // by exclamation marks ('!'). + static LLCachedControl allowed_executables(gSavedSettings, "AntiSpamXtendedAllowedExecutables"); + + std::vector command; + boost::algorithm::split(command, rule, boost::algorithm::is_any_of("!")); + if(command.size() < 2) + { + LLSD args; + args["RULE"] = "{" + rule + "}"; + LLNotificationsUtil::add("xantispamNexecMissing", args); + llinfos << "missing executable in rule: {" << rule << "}" << llendl; + return false; + } + std::vector::iterator it = command.begin(); + it++; // skip "&-ExecOnEachIM" + std::string executable = *it; + if(executable.empty()) + { + LLSD args; + args["RULE"] = "{" + rule + "}"; + LLNotificationsUtil::add("xantispamNexecMissing", args); + llinfos << "denying to execute an empty command in rule: {" << rule << "}" << llendl; + return false; + } + + bool dryrun = (executable == "DryRun"); + if(dryrun) + { + llinfos << "preparing for a dry run" << llendl; + it++; + executable = *it; + if(executable.empty()) + { + LLSD args; + args["RULE"] = "{" + rule + "}"; + LLNotificationsUtil::add("xantispamNexecMissing", args); + llinfos << "denying to execute an empty command in rule: {" << rule << "}" << llendl; + return false; + } + } + + // see if this executable is in the list of allowed ones + std::vector split_allowed_executables; + std::string these_executables = allowed_executables; + boost::algorithm::split(split_allowed_executables, these_executables, boost::algorithm::is_any_of("!")); // string.find() instead would be insecure + bool allowed = false; + std::vector::iterator ti = split_allowed_executables.begin(); + while(ti != split_allowed_executables.end()) + { + if(*ti == executable) + { + allowed = true; + break; + } + ti++; + } + + if(!allowed) + { + LLSD args; + args["EXECUTABLE"] = executable; + LLNotificationsUtil::add("xantispamNexecIllegal", args); + llinfos << "No other executables than listed in AntiSpamXtendedAllowedExecutables are allowed to be run via XAntiSpam rules, and '" << executable << "' is not listed." << llendl; + return false; + } + + LLProcessLauncher launcher; + + if(!dryrun) + { + launcher.clearArguments(); + launcher.setExecutable(executable); + launcher.setWorkingDirectory(LLFile::tmpdir()); + } + std::string parameters(""); + it++; + while(it != command.end()) // add arguments + { + if(*it == "%s") + { + if(!info.empty()) + { + parameters += info + " "; + if(!dryrun) + { + launcher.addArgument(info); + } + } + } + else + { + parameters += *it + " "; + if(!dryrun) + { + launcher.addArgument(*it); + } + } + it++; + } + + if(dryrun) + { + LLSD args; + args["RUNTYPE"] = "Dry run"; + args["EXECUTABLE"] = executable; + args["OPTIONS"] = parameters; + args["STATUS"] = "dry"; + LLNotificationsUtil::add("xantispamNexecRun", args); + llinfos << "dryrun: " << executable << " '" << parameters << "'" << llendl; + return true; + } + + LL_DEBUGS("xantispam") << "running: " << executable << " '" << parameters << "'" << LL_ENDL; + int result = launcher.launch(); + launcher.orphan(); // don't kill process on return + + if(result) + { + LLSD args; + args["RUNTYPE"] = "Running"; + args["EXECUTABLE"] = executable; + args["OPTIONS"] = parameters; + args["STATUS"] = "ERROR"; + LLNotificationsUtil::add("xantispamNexecRun", args); + } + return (result == 0 ? true : false); +} + + +static bool xantispam_lookup_selectively(std::vector& blackcache, std::vector& whitecache, const xantispam_request *request, const bool which) +{ + // Transparent lookups do file lookups when a rule isn't found in the cache + // There isn't too much point in repeating file lookups for rules that + // aren't there anyway, and it can become a bad idea performance wise. + // + // The disadvantage of doing transpartent lookup only once is that the rules + // can be dropped from the cache when it flows over. This may yield + // apparently inconsistent behaviour. I'd rather crank the cache capacity + // up if that happens. + // + // So let's prefill the caches when they aren't yet. This may yield + // interesting results and probably needs to be changed. + + static bool cache_only = FALSE; + + if(!cache_only) + { + // clear caches first because they can be prefilled on demand through + // transparent lookups, depending on what requests have been processed + // before ending up here + blackcache.clear(); + whitecache.clear(); + xantispam_prefill_cache(blackcache, false); + xantispam_prefill_cache(whitecache, true); + cache_only = true; + } + + if(which) + { + return xantispam_cachelookup(whitecache, request); + } + return xantispam_cachelookup(blackcache, request); +} + + +// +// search a message for regular expressions and return a score +// +// The message to search through is in the 'from' part of the +// request. The regular expression is in the 'from' part of a rule. +// That rule gives the score to use if the regex matches the message +// as a parameter. All such rules in the cache are checked to compute +// a total score from all the matches. +// +int xantispam_matching(const std::string haystack, std::vector& cache) +{ + static LLCachedControl minlength(gSavedSettings, "AntiSpamXtendedRxMinLength"); + + if(haystack.length() < minlength) + { + return 0; + } + + static LLCachedControl threshold(gSavedSettings, "AntiSpamXtendedRxScoreThreshold"); + + int score = 0; + std::vector::const_iterator it = cache.end(); + while(it != cache.begin()) + { + --it; + + if(it->type.length() < 10) + { + continue; + } + + if(!it->type.find("&-RxScore!")) + { + int thisscore = boost::lexical_cast(it->type.substr(10, std::string::npos)); + boost::regex needle(it->from, boost::regex::icase); + + if(boost::regex_search(haystack, needle)) + { + score += thisscore; + + if(threshold < score) + { + return true; + } + } + } + } + + return (threshold < score); +} + + +// handle the "&-" background requests +// +// These may generate notifications which should be limited to show +// errors and must not generate queries. +// +static bool xantispam_backgnd(const xantispam_request *request, std::vector& whitecache, std::vector& blackcache, const std::string& info) +{ + // info may be used to transfer further information if needed + // to process a request + // + // Types: + // + // # &-ConfigInverseOrderForAcceptInventory (internal, generated here) + // # &-ConfigInverseOrderForSilent + // # REMOVED due to changes in media filter: &-DomainHandleMediaURLs, PlayFromMediaURL + // # &-ExecFriendIsOffline![!parameter_1!parameter_2!...!parameter_N][!%s] + // # &-ExecFriendIsOnline![!parameter_1!parameter_2!...!parameter_N][!%s] + // # &-ExecOnEachGS![!parameter_1!parameter_2!...!parameter_N][!%s] + // # &-ExecOnEachIM![!parameter_1!parameter_2!...!parameter_N][!%s] + // # &-ExecOnNewGRSession![!parameter_1!parameter_2!...!parameter_N][!%s] + // # &-ExecOnNewIMSession![!parameter_1!parameter_2!...!parameter_N][!%s] + // # &-GRNewSessionNoSnd + // # &-IM/GRLogFullHistory + // # &-IMLogHistoryExternal + // # &-IMLogHistoryExternal![parameter_1!parameter_2!...!parameter_N][!%s] + // # &-IMLongOrShortTab (merges &-IMLongTab and &-IMShortTab into a single request) + // # &-IMLongOrShortTabOrder?Short + // # &-IMNewSessionNoSnd + // # &-IMSendNoAutoresponses + // # &-InventoryHandleAccept?AcceptInventory?[type] + // # &-StatusFriendIsOffline + // # &-StatusFriendIsOnline + // + // + // regular expression matching for filtering the content of + // messages (IMs/group msgs): + // + // # &-RxScore! + // + // The 'from' part of such a rule specifies a regular + // expression to match a message with; the is the + // score which will be added to a total score for the message. + // When the message gets a score above a threshold, the + // message is classified as spam. + // + // # // &-IMLogDistinct DISABLED + + if(!request->type.find("&-regex")) + { + return xantispam_matching(request->from, blackcache); + } + + if(!request->type.find("&-ExecFriendIsOnline!") || !request->type.find("&-ExecFriendIsOffline!") || !request->type.find("&-ExecOnEachIM!") || !request->type.find("&-ExecOnEachGS!") || !request->type.find("&-ExecOnNewIMSession!") || !request->type.find("&-ExecOnNewGRSession!") || !request->type.find("&-IMLogHistoryExternal!")) + { + // this requires an inverse lookup because the request must be found + // within the rule + bool result_invalid; + xantispam_request result = xantispam_inverse_cachelookup(whitecache, request, &result_invalid); + if(!result_invalid) + { + // llinfos << "rule found on inverse lookup: [" << result.from << "]{" << result.type << "}" << llendl; + return !xantispam_process_launcher(result.type, info); + } + return true; + } + + // second case of hybrid "&-IMLogHistoryExternal": use default editor to show history + if(!request->type.find("&-IMLogHistoryExternal")) + { + std::string rule = "echo!" + gSavedSettings.getString("ExternalEditor"); + // assuming that ExternalEditor is something like 'emacsclient "%s"' + boost::algorithm::erase_all(rule, "\""); + boost::algorithm::replace_all(rule, " ", "!"); + + return !xantispam_process_launcher(rule, info); + } + + // wrapper for distinct inventory offer handling + if(!request->type.find("&-InventoryHandleAccept?AcceptInventory?")) + { + // no need to go through checking when the origin is the user + if(request->from == gAgentID.asString()) + { + return false; // policy: don't block yourself + } + // transform into an ordinary request + std::vector elements; + boost::algorithm::split(elements, request->type, boost::algorithm::is_any_of("?")); + if(elements.size() == 3) + { + if(elements[2].empty()) + { + llwarns << "syntax error (undefined inventory type) in request: [" << request->from << "]{" << request->type << "}" << llendl; + return false; // probably better accept in this case + } + if(elements[1] == "AcceptInventory") + { + // the order might have been changed to "deny all, except for whitelisted" + xantispam_request bw; + bw.from = request->from; + bw.type = "&-ConfigInverseOrderForAcceptInventory"; + bool order_inversed = !xantispam_cachelookup(whitecache, &bw); + + // allow wildcard for type of inventory item + bw.type = "AcceptInventory?ANY"; + + if(order_inversed) + { + // when the order is inversed, accept if whitelisted, otherwise deny + // first look up with wildcarded inventory type ... + if(!xantispam_cachelookup(whitecache, &bw)) + { + return false; // accept inventory item if all types are whitelisted + } + // ... and if that didn't render a decision, look up whith the particular type + bw.type = "AcceptInventory?" + elements[2]; + return xantispam_transparentlookup(whitecache, &bw, true); // this breaks policy + } + else + { + // first look up with wildcarded inventory type ... + if(!xantispam_cachelookup(blackcache, &bw)) + { + return true; // deny inventory item if all types are blacklisted + } + if(!xantispam_cachelookup(whitecache, &bw)) + { + return false; // accept inventory item if all types are whitelisted + } + // ... and if that didn't render a decision, look up whith the particular type + return xantispam_check(request->from, "AcceptInventory?" + elements[2], info); + } + } + else + { + llwarns << "syntax error (unexpected argument '" << elements[1] << "') in request: [" << request->from << "]{" << request->type << "}" << llendl; + return false; // probably better accept in this case + } + } + else + { + llwarns << "syntax error (unexpected number of arguments) in request: [" << request->from << "]{" << request->type << "}" << llendl; + return false; // probably better accept in this case + } + } + + // wrapper for merged request: The request is merged in that it avoids + // having to make two calls to xantispam_check() to get a result + // Return false when a short tab is wanted. + if(request->type == "&-IMLongOrShortTab") + { + xantispam_request longtab; + longtab.from = request->from; + longtab.type = "&-IMShortTab"; + + // Is a short tab wanted? + bool want_short = !xantispam_lookup_selectively(blackcache, whitecache, &longtab, true); + if(!want_short) + { + // The default behaviour is long tabs, and no further ado is needed + // when a short tab isn't wanted anyway. + return true; + } + + // A short tab is MAYBE wanted. Is there a rule demanding long tabs? + longtab.type = "&-IMLongTab"; + bool want_long = !xantispam_lookup_selectively(blackcache, whitecache, &longtab, true); + if(!want_long) + { + // nothing demands long tabs + return false; + } + + // Now both long and short tabs are demanded. Which shall win? + longtab.type = "&-IMLongOrShortTabOrder?Short"; + bool short_tabs_lose = xantispam_lookup_selectively(blackcache, whitecache, &longtab, true); + return short_tabs_lose; + + // After all, this is probably what users want when they wildcard all + // tabs to short and excempt someone from this rule. Should they want + // it the other way round, they can still change order. + } + + // For the rules that don't do something special, return the result of the lookup. + return xantispam_lookup_selectively(blackcache, whitecache, request, true); +} + + +// handle silent requests +// +// The main reason to handle them here is that it is not reasonably +// possible to allow only some online status notifications to be shown +// in non-relaxed mode. +// +static bool xantispam_silent(const xantispam_request *request, std::vector& whitecache, std::vector& blackcache, const std::string& info) +{ + if((request->type == "!StatusFriendIsOffline") || (request->type == "!StatusFriendIsOnline")) + { + // The request is undecided. Is it blacklisted? + bool isblack = !xantispam_lookup_selectively(blackcache, whitecache, request, false); + + xantispam_request config; + config.from = request->from; + config.type = "&-ConfigInverseOrderForSilent"; + if(xantispam_backgnd(&config, whitecache, blackcache, "[config]")) // policy: requests are looked up by the appropriate function + { + // order is not inversed + if(isblack) + { + return true; + } + + // The request is not blacklisted. Is it whitelisted? + if(!xantispam_lookup_selectively(blackcache, whitecache, request, true)) + { + return false; + } + } + // order is inversed: deny if blacklisted, otherwise decide by whitelist only + bool iswhite = !xantispam_lookup_selectively(blackcache, whitecache, request, true); + return isblack ? !isblack : !iswhite; + } + + llwarns << "denying unprocessable silent request: [" << request->from << "]{" << request->type << "}" << llendl; + return true; +} + + +// (Start reading from here.) +// +// + chat logging can be distinguished by resident/friend/group +// + chat logging can be disabled selectively +// + media filtering can consider full URLs including the port +// + external programs can be started for a number of events +// + friend online notifications can be shown selectively +// + sound for new chat sessions can be suppressed selectively +// + chat history can be displayed in external editor or any program +// + autoresponses can be sent/disabled distinctively +// + automatically accepting inventory items can be selective +// + various dialogs can be suppressed selectively +// + internal text editor can import and export files +// + the external editor can be started from the internal one +// + and some more ... +// +// These features are implemented by intercepting requests and making +// decisions about what to do by checking volatile and persistent +// rules, each of which are optionally used or not. In case a +// decision cannot be made from existing rules, the user is queried +// for a decision unless queries are disabled. +// +// Persistent rules are stored in tables in emacs org-mode files. A +// blacklist and a whitelist is being used. The design allows for a +// virtually unlimited number of persistent rules and employs +// overenginered caching of the rules to avoid performance issues. +// +// However, different types of rules are used for different types of +// requests: ordinary requests, background requests and silent +// requests. Since background and silent rules can be looked up +// rather frequently, they are looked up in files only once and from +// thereon, in the caches. In case a user has a great number of +// rules, they may have a problem with some background and silent +// rules being dropped from the caches. There are some ways to solve +// this problem if that becomes necessary. +// +// policy: requests pass when rules and queries are disabled or when a +// decision making logic is disabled +// +// Please direct questions and suggestions about xantispam to +// lee@yun.yagibdah.de. +// +bool xantispam_check(const std::string& fromstr, const std::string& filtertype, const std::string& from_name) +{ + // is xantispam enabled? + static LLCachedControl use_xantispam(gSavedSettings,"AntiSpamXtendedEnabled"); + if(!use_xantispam) + { + // consistently return true when xantispam is disabled + return true; + } + + // cache these for better performance + static LLCachedControl use_notify(gSavedSettings, "AntiSpamNotify"); + static LLCachedControl use_queries(gSavedSettings, "AntiSpamXtendedQueries"); + static LLCachedControl use_relaxed(gSavedSettings, "AntiSpamXtendedRelaxed"); + static LLCachedControl use_debug(gSavedSettings, "AntiSpamXtendedDebug"); + + // persistent caches here, filled from rules files + static std::vector blackcache; + static std::vector whitecache; + // Use a volatile cache for undecided requests. This saves both cache + // and file lookups when it is already known that a request is + // undecided. It is filled from decisions made by the user. + static std::vector undeccache; + + // llinfos << "XA-RQ-log: [" << fromstr << "]{" << filtertype << "}'" << from_name << "', backgnd: " << (request_is_backgnd ? "Yes" : "No") << llendl; + + // background and silent requests have their own handlers + bool request_is_backgnd = (filtertype.find("&-") == 0); + bool request_is_silent = (filtertype.at(0) == '!'); + + // handle special cases here + if(!request_is_silent && !request_is_backgnd && (fromstr == gAgentID.asString())) + { + // handle special calls + xantispam_request clean; + int action = xantispam_split_special(filtertype, &clean); + // strip whitespace and replace ":" with ";" + xantispam_apply_syntax(&clean); + + switch(action) + { + case XANTISPAM_SPECIAL_ADDBLACK: + if(xantispam_cachelookup(blackcache, &clean) ) { + if(blackcache.size() > XANTISPAM_CACHE_CAPACITY) + { + blackcache.erase(blackcache.begin(), blackcache.begin() + XANTISPAM_CACHE_CAPACITY / XANTISPAM_THRESHOLD + 1); + } + blackcache.push_back(clean); + LL_DEBUGS("xantispam") << "Request put on blacklist cache directly: [" << clean.from << "]{" << clean.type << "}" << LL_ENDL; + } + break; + case XANTISPAM_SPECIAL_ADDWHITE: + if(xantispam_cachelookup(whitecache, &clean) ) { + if(whitecache.size() > XANTISPAM_CACHE_CAPACITY) + { + whitecache.erase(whitecache.begin(), whitecache.begin() + XANTISPAM_CACHE_CAPACITY / XANTISPAM_THRESHOLD + 1); + } + whitecache.push_back(clean); + LL_DEBUGS("xantispam") << "Request put on whitelist cache directly: [" << clean.from << "]{" << clean.type << "}" << LL_ENDL; + } + break; + case XANTISPAM_SPECIAL_CLEARCACHE: + // special call to clear the persistent caches + llinfos << "clearing entries from blackcache: " << blackcache.size() << llendl; + llinfos << "clearing entries from whitecache: " << whitecache.size() << llendl; + blackcache.clear(); + whitecache.clear(); + // the undecided cache must be cleared as well because there could be + // requests on it that have become decided through changes to the + // files + llinfos << "clearing entries from undeccache: " << undeccache.size() << llendl; + undeccache.clear(); + if(use_notify) + { + LLSD args; + args["TYPE"] = "Persistent and Volatile-Undecided"; + LLNotificationsUtil::add("xantispamNclrcache", args); + } + xantispam_prefill_cache(blackcache, false); + xantispam_prefill_cache(whitecache, true); + llinfos << "persistent caches prefilled" << llendl; + return false; + break; + case XANTISPAM_SPECIAL_INVALID: + { + // Users may wonder why some requests always pass. policy: They cannot block themselves. + LLSD args; + args["UUID"] = fromstr + " (" + from_name + ")"; + LLNotificationsUtil::add("xantispamNselfblk", args); + return false; + } + break; + default: + llwarns << "Control: Unknown special-request type may yield unexpected decisions: " << filtertype << llendl; + return false; + } + // Requests are left on the undecided cache unless they are persistently decided. + // File lookups to find out about a request are pointless when it is already + // known that there is nothing in the files about the request. This also saves + // searching through the persistent caches: since they have the same information + // the files have, it's as pointless to seach them as it is to search the files. + // The persistent caches likely hold many more rules than the undecided cache, so + // this will be faster in any case. + std::vector::iterator it = undeccache.end(); + while(it != undeccache.begin() ) + { + --it; + + // Order basically does matter for comparison, but since this deals + // with requests that have been formed internally and not with rules + // from files, it doesn't matter. + if(xantispam_compare_requests(&(*it), &clean)) + { + undeccache.erase(it); + LL_DEBUGS("xantispam") << "Request removed from undecided cache: [" << clean.from << "]{" << clean.type << "}" << LL_ENDL; + } + } + return false; + } // special requests + + // prevent flooding --- blocking the request may not be the right + // thing in all cases, but it is the most secure thing to do + if(xantispam_callspersec()) + { + return true; + } + + // finally, handle a request + xantispam_request request; + request.from = fromstr; + request.type = filtertype; + // strip whitespace and replace ":" with ";" + xantispam_apply_syntax(&request); + + // show a debug notification if wanted + if(use_debug) + { + LLSD args; + args["FROM"] = request.from; + args["TYPE"] = request.type; + args["INFO"] = from_name; + LLNotificationsUtil::add("xantispamNdebug", args); + } + + // policy: background requests do not generate queries + // policy: handle background requests always by rules + // Without exisiting rules, this results in + // background requests being denied, which + // effictively results in the same behaviour + // as would result without xantispam. + if(request_is_backgnd) + { + return xantispam_backgnd(&request, whitecache, blackcache, from_name); + } + + // policy: silent requests do not generate notifications + // policy: handle silent requests always by rules + // Without exisiting rules, this results in + // silent requests being denied, which + // effictively results in the same behaviour + // as would result without xantispam. + if(request_is_silent) + { + return xantispam_silent(&request, whitecache, blackcache, from_name); + } + + // long_request provides using rules like ":: greeter" + std::string stripped_name = from_name; + std::transform(stripped_name.begin(), stripped_name.end(), stripped_name.begin(), ::tolower); + xantispam_request long_request; + long_request.from = request.from; + long_request.type = request.type + stripped_name; + // strip whitespace and replace ":" with ";" + xantispam_apply_syntax(&long_request); + + if(xantispam_cachelookup(undeccache, &request) ) + { + // The request undecided. Is it blacklisted? + if(!xantispam_transparentlookup(blackcache, &long_request, false)) + { + LL_DEBUGS("xantispam") << "is blacklisted" << LL_ENDL; + // yes, notify about what happened + if(use_notify) + { + LLSD args; + args["SOURCE"] = long_request.from + " (" + from_name + ")"; + args["TYPE"] = long_request.type; + LLNotificationsUtil::add("xantispamNblk", args); + } + return true; + } + + LL_DEBUGS("xantispam") << "is not blacklisted" << LL_ENDL; + // The request is not blacklisted. Is it whitelisted? + if(!xantispam_transparentlookup(whitecache, &long_request, true)) + { + LL_DEBUGS("xantispam") << "filelookup whitelist returned" << LL_ENDL; + return false; + } + + LL_DEBUGS("xantispam") << "is not whitelisted" << LL_ENDL; + // the request is still undecided, so put it on the undecided cache + if(undeccache.size() > XANTISPAM_CACHE_CAPACITY) + { + undeccache.erase(undeccache.begin(), undeccache.begin() + XANTISPAM_CACHE_CAPACITY / XANTISPAM_THRESHOLD + 1); + } + undeccache.push_back(request); + LL_DEBUGS("xantispam") << "Request put on undecided cache directly: [" << request.from << "]{" << request.type << "}" << LL_ENDL; + } + + // handle busy and afk situations gracefully + bool reenable_queries_busy = false; + if(gAgent.isDoNotDisturb()) + { + xantispam_request config; + config.from = gAgentID.asString(); + config.type = "&-ConfigQueriesWhenBUSY"; // the default is: no queries when busy + if(!xantispam_backgnd(&config, whitecache, blackcache, "[config]")) + { + gSavedSettings.setBOOL("AntiSpamXtendedQueries", false); + reenable_queries_busy = true; + } + } + else + { + if(reenable_queries_busy) + { + gSavedSettings.setBOOL("AntiSpamXtendedQueries", true); + reenable_queries_busy = false; + } + } + + static bool reenable_queries_afk = false; + if(!reenable_queries_busy) + { + if(gAgent.getAFK()) + { + xantispam_request config; + config.from = gAgentID.asString(); + config.type = "&-ConfigNoQueriesWhenAFK"; + if(!xantispam_backgnd(&config, whitecache, blackcache, "[config]")) + { + gSavedSettings.setBOOL("AntiSpamXtendedQueries", false); + reenable_queries_afk = true; + } + } + else + { + if(reenable_queries_afk) + { + gSavedSettings.setBOOL("AntiSpamXtendedQueries", true); + reenable_queries_afk = false; + } + } + } + + // either the voliatile caches or the user will decide this request + xantispam_blackwhite bw = xantispam_notify(&request, XANTISPAM_QUERYUSER, from_name); + + if(bw.isblacklisted) { + // notify about what happened + if(use_notify) + { + LLSD args; + args["SOURCE"] = request.from + " (" + from_name + ")"; + args["TYPE"] = request.type; + LLNotificationsUtil::add("xantispamNblk", args); + } + return true; + } + + // When relaxed, only deny requests that are blacklisted: This + // allows to accept requests first and decide whether to black- or + // to whitelist them later. + if(bw.iswhitelisted || use_relaxed) { + return false; + } + + if(use_notify) + { + // notify about what happened + LLSD args; + args["SOURCE"] = request.from + " (" + from_name + ")"; + args["TYPE"] = request.type; + LLNotificationsUtil::add("xantispamNblk", args); + } + return true; +} + + +#if 0 +// used to generate lots of rules for testing +static void xantispam_generate_test_entries(void) +{ + int cnt = 0; + while(cnt < 300000) { + LLUUID uuid = LLUUID::generateNewID(); + xantispam_request request; + request.from = uuid.asString(); + request.type = "InvalidRequest"; + xantispam_make_listentry(false, &request, "test entry"); + cnt++; + } +} +#endif + + +// act on some button presses (preferences floater) +// see ascentprefschat.cpp +// +void xantispam_buttons(const int action) { + xantispam_request request; + request.from = gAgentID.asString(); + request.type = XANTISPAM_CLEARCACHE_RQSTRING; + + // caches can not be cleared when not in use + switch(action) + { + case XANTISPAM_CLEARCACHE_PERSISTENT: + // xantispam_generate_test_entries(); + xantispam_check(request.from, request.type + ":Clear:Cache", ""); + break; + case XANTISPAM_CLEARCACHE_VOLATILE: + { + xantispam_notify(&request, XANTISPAM_CLEARCACHE, request.from); + } + break; + case XANTISPAM_EDIT_BLACKLIST: + { + std::string rule = "echo!" + gSavedSettings.getString("ExternalEditor"); + boost::algorithm::erase_all(rule, "\""); + boost::algorithm::replace_all(rule, " ", "!"); + xantispam_process_launcher(rule, gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter() + XANTISPAM_BLACKLISTFILE); + } + break; + case XANTISPAM_EDIT_WHITELIST: + { + std::string rule = "echo!" + gSavedSettings.getString("ExternalEditor"); + boost::algorithm::erase_all(rule, "\""); + boost::algorithm::replace_all(rule, " ", "!"); + xantispam_process_launcher(rule, gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter() + XANTISPAM_WHITELISTFILE); + } + break; + case XANTISPAM_ENABLE: + // enable xantispam with default settings + gSavedSettings.setBOOL("_NACL_Antispam", 0); + gSavedSettings.setBOOL("AntiSpamNotify", 1); + gSavedSettings.setBOOL("AntiSpamAlerts", 1); + gSavedSettings.setBOOL("AntiSpamFriendshipOffers", 1); + // depend + gSavedSettings.setBOOL("AntiSpamGroupInvites", 1); + gSavedSettings.setBOOL("AntiSpamGroupFeeInvites", 0); + // /depend + gSavedSettings.setBOOL("AntiSpamItemOffers", 1); + gSavedSettings.setBOOL("AntiSpamScripts", 1); + gSavedSettings.setBOOL("AntiSpamTeleports", 1); + gSavedSettings.setBOOL("AntiSpamGroupNotices", 1); + gSavedSettings.setBOOL("AntiSpamTeleportRequests", 1); + gSavedSettings.setBOOL("AntiSpamNotMine", 1); + gSavedSettings.setBOOL("AntiSpamNotFriend", 0); + gSavedSettings.setBOOL("AntiSpamEnabled", 1); + gSavedSettings.setBOOL("EnableGestureSounds", 0); + + gSavedSettings.setBOOL("AntiSpamXtendedEnabled", 1); + + gSavedSettings.setBOOL("AntiSpamXtendedPersistent", 1); + gSavedSettings.setBOOL("AntiSpamXtendedQueries", 1); + gSavedSettings.setBOOL("AntiSpamXtendedVolatile", 1); + gSavedSettings.setBOOL("AntiSpamXtendedRelaxed", 1); + gSavedSettings.setBOOL("NotifyRecievesFocus", 0); + + xantispam_make_listheader(gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter() + XANTISPAM_WHITELISTFILE); + xantispam_make_listheader(gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter() + XANTISPAM_BLACKLISTFILE); + + LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", "XAntiSpam has been enabled with default settings. You can change the settings in the preferences floater.")); + // + // Note: This is a crazy amount of settings. + // It would be simpler to just run everyting through xantispam. + // + default: + // just in case ... + llwarns << "The parameter speficying which action to take is out of bounds." << llendl; + } +} + + +#undef XANTISPAM_ADDBLACK +#undef XANTISPAM_ADDWHITE +#undef XANTISPAM_BLACKLISTFILE +#undef XANTISPAM_CACHE_CAPACITY +#undef XANTISPAM_CLEARCACHE +#undef XANTISPAM_CLEARCACHE_PERSISTENT +#undef XANTISPAM_CLEARCACHE_RQSTRING +#undef XANTISPAM_CLEARCACHE_VOLATILE +#undef XANTISPAM_EDIT_BLACKLIST +#undef XANTISPAM_EDIT_WHITELIST +#undef XANTISPAM_ENABLE +#undef XANTISPAM_NOP +#undef XANTISPAM_PERMBLACK +#undef XANTISPAM_PERMWHITE +#undef XANTISPAM_QUERYUSER +#undef XANTISPAM_SPECIAL_ADDBLACK +#undef XANTISPAM_SPECIAL_ADDWHITE +#undef XANTISPAM_SPECIAL_CLEARCACHE +#undef XANTISPAM_SPECIAL_INVALID +#undef XANTISPAM_TEMPBLACK +#undef XANTISPAM_TEMPWHITE +#undef XANTISPAM_THRESHOLD +#undef XANTISPAM_UNNOTIFY +#undef XANTISPAM_WHITELISTFILE + + +bool is_spam_filtered(const EInstantMessage& dialog, bool is_friend, bool is_owned_by_me, std::string from_id, const std::string from_name) +{ + // Ratany: Lirusaito: Checking the bypasses after the filters may have + // returned false seems to potentially disable the bypasses? In + // case of IM_INVENTORY_OFFERED | IM_TASK_INVENTORY_OFFERED, + // is_owned_by_me can apparently be false even when I own the object + // that is gving me a dialog. I've added a check in + // xantispam_check() so users cannot blacklist themselves. /Ratany + // First, check the master filter static LLCachedControl antispam(gSavedSettings,"_NACL_Antispam"); if (antispam) return true; + // Third, possibly filtered, check the filter bypasses + static LLCachedControl antispam_not_mine(gSavedSettings,"AntiSpamNotMine"); + if (antispam_not_mine && is_owned_by_me) + return false; + + static LLCachedControl antispam_not_friend(gSavedSettings,"AntiSpamNotFriend"); + if (antispam_not_friend && is_friend) + return false; + + static LLCachedControl alerts(gSavedSettings, "AntiSpamAlerts"); + static LLCachedControl friendship_offers(gSavedSettings, "AntiSpamFriendshipOffers"); + static LLCachedControl group_invites(gSavedSettings, "AntiSpamGroupInvites"); + static LLCachedControl group_notices(gSavedSettings, "AntiSpamGroupNotices"); + static LLCachedControl item_offers(gSavedSettings, "AntiSpamItemOffers"); + static LLCachedControl scripts(gSavedSettings, "AntiSpamScripts"); + static LLCachedControl teleport_offers(gSavedSettings, "AntiSpamTeleports"); + static LLCachedControl teleport_requests(gSavedSettings, "AntiSpamTeleportRequests"); + + // allow to en-/disable xantispam + // Without this, xantispam would pass the requests in case there aren't any rules, + // effectively disabling these checks. + static LLCachedControl use_xantispam(gSavedSettings,"AntiSpamXtendedEnabled"); + // Second, check if this dialog type is even being filtered switch(dialog) { case IM_GROUP_NOTICE: + if (!group_notices) + { + return false; + } + if(use_xantispam) + { + return xantispam_check(from_id, "GroupNoticesNonRequested", from_name); + } + break; + case IM_BUSY_AUTO_RESPONSE: + if(use_xantispam) + { + return xantispam_check(from_id, "AutoResponseIsBusy", from_name); + } + return false; + break; case IM_GROUP_NOTICE_REQUESTED: - if (!gSavedSettings.getBOOL("AntiSpamGroupNotices")) return false; + if (!group_notices) + { + return false; + } + if(use_xantispam) + { + return xantispam_check(from_id, "GroupNoticesRequested", from_name); + } break; case IM_GROUP_INVITATION: - if (!gSavedSettings.getBOOL("AntiSpamGroupInvites")) return false; + if (!group_invites) + { + return false; + } + if(use_xantispam) + { + return xantispam_check(from_id, "GroupInvites", from_name); + } break; case IM_INVENTORY_OFFERED: + if (!item_offers) + { + return false; + } + if(use_xantispam) + { + if(!xantispam_check(from_id, "&-InventoryHandleDistinctly", from_name)) + { + return false; // filter by type of item in inventory_offer_handler() + } + return xantispam_check(from_id, "ItemOffersNonTask", from_name); + } + break; case IM_TASK_INVENTORY_OFFERED: - if (!gSavedSettings.getBOOL("AntiSpamItemOffers")) return false; + if (!item_offers) + { + return false; + } + if(use_xantispam) + { + if(!xantispam_check(from_id, "&-InventoryHandleDistinctly", from_name)) + { + return false; // filter by type of item in inventory_offer_handler() + } + return xantispam_check(from_id, "ItemOffersFromTask", from_name); + } break; case IM_FROM_TASK_AS_ALERT: - if (!gSavedSettings.getBOOL("AntiSpamAlerts")) return false; + if (!alerts) + { + return false; + } + if(use_xantispam) + { + return xantispam_check(from_id, "Alerts", from_name); + } break; case IM_LURE_USER: - if (!gSavedSettings.getBOOL("AntiSpamTeleports")) return false; + if (!teleport_offers) + { + return false; + } + if(use_xantispam) + { + return xantispam_check(from_id, "TeleportOffers", from_name); + } break; case IM_TELEPORT_REQUEST: - if (!gSavedSettings.getBOOL("AntiSpamTeleportRequests")) return false; + if (!teleport_requests) + { + return false; + } + if(use_xantispam) + { + return xantispam_check(from_id, "TeleportRequests", from_name); + } break; case IM_FRIENDSHIP_OFFERED: - if (!gSavedSettings.getBOOL("AntiSpamFriendshipOffers")) return false; + if (!friendship_offers) + { + return false; + } + if(use_xantispam) + { + return xantispam_check(from_id, "FriendshipOffers", from_name); + } break; case IM_COUNT: - // Bit of a hack, we should never get here unless we did this on purpose, though, doesn't matter because we'd do nothing anyway - if (!gSavedSettings.getBOOL("AntiSpamScripts")) return false; + // Bit of a hack, we should never get here unless we + // did this on purpose, though, doesn't matter because + // we'd do nothing anyway + // + // Ratany: Receiving a script dialog gets here. When + // an object tries to open the world map, there is an + // "ERROR: getData: Variable ObjectID not in message + // ScriptTeleportRequest block Data". There is no + // OwnerID, either. I kinda worked around that, but + // it needs to be changed. + // + // This case should not be so generic, but I didn't + // want to just add more entries to EInstantMessage in + // llinstantmessage.h. + // + // /Ratany + if (!scripts) + { + return false; + } + if(use_xantispam) + { + return xantispam_check(from_id, "DialogsFromTask", from_name); + } break; default: + // LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", "Something has passed filtering without your knowledge.")); return false; } - // Third, possibly filtered, check the filter bypasses - static LLCachedControl antispam_not_mine(gSavedSettings,"AntiSpamNotMine"); - if (antispam_not_mine && is_owned_by_me) - return false; - - static LLCachedControl antispam_not_friend(gSavedSettings,"AntiSpamNotFriend"); - if (antispam_not_friend && is_friend) - return false; - // Last, definitely filter return true; } -void inventory_offer_handler(LLOfferInfo* info) + +void script_msg_api(const std::string& msg); +bool inventory_offer_handler_answer_available(LLOfferInfo *info) { // NaCl - Antispam Registry if (NACLAntiSpamRegistry::checkQueue((U32)NACLAntiSpamRegistry::QUEUE_INVENTORY,info->mFromID)) { delete info; - return; - } - // NaCl End - //If muted, don't even go through the messaging stuff. Just curtail the offer here. + return true; + } // NaCl End + // If muted, don't even go through the messaging stuff. Just curtail the offer here. if (LLMuteList::getInstance()->isMuted(info->mFromID, info->mFromName)) { info->forceResponse(IOR_MUTE); - return; + return true; } if (!info->mFromGroup) script_msg_api(info->mFromID.asString() + ", 1"); @@ -1803,32 +3689,31 @@ void inventory_offer_handler(LLOfferInfo* info) if (gSavedSettings.getBOOL("AutoAcceptAllNewInventory")) { info->forceResponse(IOR_ACCEPT); - return; + return true; } // Avoid the Accept/Discard dialog if the user so desires. JC - if (gSavedSettings.getBOOL("AutoAcceptNewInventory") - && (info->mType == LLAssetType::AT_NOTECARD - || info->mType == LLAssetType::AT_LANDMARK - || info->mType == LLAssetType::AT_TEXTURE)) + if (gSavedSettings.getBOOL("AutoAcceptNewInventory") && (info->mType == LLAssetType::AT_NOTECARD || info->mType == LLAssetType::AT_LANDMARK || info->mType == LLAssetType::AT_TEXTURE)) { - // For certain types, just accept the items into the inventory, - // and possibly open them on receipt depending upon "ShowNewInventory". info->forceResponse(IOR_ACCEPT); - return; + return true; } if (gAgent.isDoNotDisturb() && info->mIM != IM_TASK_INVENTORY_OFFERED) // busy mode must not affect interaction with objects (STORM-565) { - // Until throttling is implemented, busy mode should reject inventory instead of silently - // accepting it. SEE SL-39554 info->forceResponse(IOR_DECLINE); - return; + return true; } + return false; +} + + +std::string inventory_offer_handler_strip_url(const LLOfferInfo *info) +{ // Strip any SLURL from the message display. (DEV-2754) std::string msg = info->mDesc; - int indx = msg.find(" ( http://slurl.com/secondlife/"); + std::size_t indx = msg.find(" ( http://slurl.com/secondlife/"); if(indx == std::string::npos) { // try to find new slurl host @@ -1838,86 +3723,131 @@ void inventory_offer_handler(LLOfferInfo* info) { LLStringUtil::truncate(msg, indx); } + return msg; +} - LLSD args; - args["[OBJECTNAME]"] = msg; - - LLSD payload; +bool inventory_offer_handler_get_asset_type(const LLOfferInfo *info, std::string& assettype) +{ // must protect against a NULL return from lookupHumanReadable() - std::string typestr = ll_safe_string(LLAssetType::lookupHumanReadable(info->mType)); - if (!typestr.empty()) + assettype = ll_safe_string(LLAssetType::lookupHumanReadable(info->mType)); + if (!assettype.empty()) { // human readable matches string name from strings.xml // lets get asset type localized name - args["OBJECTTYPE"] = LLTrans::getString(typestr); + assettype = LLTrans::getString(assettype); + return false; } - else + + LL_WARNS("Messaging") << "LLAssetType::lookupHumanReadable() returned NULL - probably bad asset type: " << info->mType << LL_ENDL; + // This seems safest, rather than propagating bogosity + LL_WARNS("Messaging") << "Forcing an inventory-decline for probably-bad asset type." << LL_ENDL; + return true; +} + + +void inventory_offer_handler(LLOfferInfo* info) +{ + // see if current settings provide a response + if(inventory_offer_handler_answer_available(info)) { - LL_WARNS("Messaging") << "LLAssetType::lookupHumanReadable() returned NULL - probably bad asset type: " << info->mType << LL_ENDL; - args["OBJECTTYPE"] = ""; + return; + } + + // get name of asset given + std::string asset_name = inventory_offer_handler_strip_url(info); - // This seems safest, rather than propagating bogosity - LL_WARNS("Messaging") << "Forcing an inventory-decline for probably-bad asset type." << LL_ENDL; + // get type of asset + std::string assettype; + if(inventory_offer_handler_get_asset_type(info, assettype)) + { + // return on bogus asset type info->forceResponse(IOR_DECLINE); return; } - // Name cache callbacks don't store userdata, so can't save - // off the LLOfferInfo. Argh. - BOOL name_found = FALSE; - payload["from_id"] = info->mFromID; - args["OBJECTFROMNAME"] = info->mFromName; - args["NAME"] = info->mFromName; - if (info->mFromGroup) + // figure out the origin of the offer + enum org { + GROUP, + AGENT, + TASK + } origin; + if(info->mFromObject) { - std::string group_name; - if (gCacheName->getGroupName(info->mFromID, group_name)) - { - args["NAME"] = group_name; - name_found = TRUE; - } + origin = TASK; } else { - std::string full_name; - if (gCacheName->getFullName(info->mFromID, full_name)) + origin = info->mFromGroup ? GROUP : AGENT; + } + + // find the name of the origin + std::string origin_name; + bool origin_name_found = false; + switch(origin) + { + case GROUP: + if(gCacheName->getGroupName(info->mFromID, origin_name)) + { + origin_name_found = true; + } + break; + case AGENT: + if (gCacheName->getFullName(info->mFromID, origin_name)) { // [RLVa:KB] - Checked: 2010-11-02 (RLVa-1.2.2a) | Modified: RLVa-1.2.2a - // Only filter if the object owner is a nearby agent + // Only filter if the object owner is a nearby agent if ( (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) && (RlvUtil::isNearbyAgent(info->mFromID)) ) { - full_name = RlvStrings::getAnonym(full_name); + origin_name = RlvStrings::getAnonym(origin_name); } // [/RLVa:KB] - args["NAME"] = full_name; - name_found = TRUE; + origin_name_found = TRUE; } + break; + case TASK: + break; } + // xantispam ... + std::string xa_origin_string = (origin == AGENT) ? info->mFromID.asString() : (origin_name_found ? origin_name : (origin == GROUP) ? "mFromID.asString() + ")>" : "mFromID.asString() + ")>"); + xa_origin_string.erase(std::remove_if(xa_origin_string.begin(), xa_origin_string.end(), boost::algorithm::is_any_of("?:")), xa_origin_string.end()); - LLNotification::Params p("ObjectGiveItem"); - p.substitutions(args).payload(payload).functor(boost::bind(&LLOfferInfo::inventory_offer_callback, info, _1, _2)); + if(xantispam_check(xa_origin_string, "&-InventoryHandleDistinctly", assettype)) + { // not handled by xa, do as usual + // set up a notification + LLSD args; + args["OBJECTFROMNAME"] = info->mFromName; + args["OBJECTTYPE"] = assettype; + args["[OBJECTNAME]"] = asset_name; + args["NAME"] = origin_name_found ? origin_name : info->mFromName; - // Object -> Agent Inventory Offer - if (info->mFromObject) - { - p.name = name_found ? "ObjectGiveItem" : "ObjectGiveItemUnknownUser"; + LLSD payload; + payload["from_id"] = info->mFromID; + + LLNotification::Params p("ObjectGiveItem"); + p.substitutions(args).payload(payload).functor(boost::bind(&LLOfferInfo::inventory_offer_callback, info, _1, _2)); + p.name = (origin == AGENT) ? "UserGiveItem" : (origin_name_found ? "ObjectGiveItem" : "ObjectGiveItemUnknownUser"); + + // fire the notification + LLNotifications::instance().add(p); } - else // Agent -> Agent Inventory Offer - { -// [RLVa:KB] - Checked: 2010-11-02 (RLVa-1.2.2a) | Modified: RLVa-1.2.2a - // Only filter if the offer is from a nearby agent and if there's no open IM session (doesn't necessarily have to be focused) - if ( (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) && (RlvUtil::isNearbyAgent(info->mFromID)) && - (!RlvUIEnabler::hasOpenIM(info->mFromID)) ) + else + { // handled by xa + // ... so check for particular rule + std::string xa_asset_type = assettype; + xa_asset_type.erase(std::remove_if(xa_asset_type.begin(), xa_asset_type.end(), isspace), xa_asset_type.end()); + xa_asset_type.erase(std::remove_if(xa_asset_type.begin(), xa_asset_type.end(), boost::algorithm::is_any_of("?:")), xa_asset_type.end()); + + if(xantispam_check(xa_origin_string, "&-InventoryHandleAccept?AcceptInventory?" + xa_asset_type, asset_name)) { - args["NAME"] = RlvStrings::getAnonym(info->mFromName); + info->forceResponse(IOR_DECLINE); + } + else + { + info->forceResponse(IOR_ACCEPT); } -// [/RLVa:KB] - p.name = "UserGiveItem"; } - - LLNotifications::instance().add(p); } @@ -2304,12 +4234,13 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) chat.mFromID = from_id; chat.mFromName = name; chat.mSourceType = (from_id.isNull() || (name == std::string(SYSTEM_FROM))) ? CHAT_SOURCE_SYSTEM : CHAT_SOURCE_AGENT; - - if(chat.mSourceType == CHAT_SOURCE_AGENT) - { - LLSD args; - args["NAME"] = name; - } + +// Ratany: This does nothing? /Ratany +// if(chat.mSourceType == CHAT_SOURCE_AGENT) +// { +// LLSD args; +// args["NAME"] = name; +// } LLViewerObject *source = gObjectList.findObject(session_id); //Session ID is probably the wrong thing. if (source || (source = gObjectList.findObject(from_id))) @@ -2318,7 +4249,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) } // NaCl - Antispam - if (is_spam_filtered(dialog, is_friend, is_owned_by_me)) return; + if (is_spam_filtered(dialog, is_friend, is_owned_by_me, from_id.asString(), original_name)) return; // NaCl End std::string separator_string(": "); @@ -2447,7 +4378,9 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) // return a standard "busy" message, but only do it to online IM // (i.e. not other auto responses and not store-and-forward IM) - if (send_autoresponse) + // [Ratany:] excempt a particular resident from being sent + // autoresponses, according to rules [/Ratany] + if (send_autoresponse && xantispam_check(from_id.asString(), "&-IMSendNoAutoresponses", name)) { // if there is not a panel for this conversation (i.e. it is a new IM conversation // initiated by the other party) then... @@ -3432,6 +5365,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) args["[MESSAGE]"] = message; LLNotificationsUtil::add("OfferFriendship", args, payload); } + make_ui_sound("UISndRtyFriendOffer"); } } break; @@ -3451,6 +5385,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) LLSD payload; payload["from_id"] = from_id; LLAvatarNameCache::get(from_id, boost::bind(¬ification_display_name_callback, _1, _2, "FriendshipAccepted", args, payload)); + make_ui_sound("UISndRtyFriendOffer"); } break; @@ -3550,10 +5485,6 @@ static LLNotificationFunctorRegistration callingcard_offer_cb_reg("OfferCallingC void process_offer_callingcard(LLMessageSystem* msg, void**) { - // NaCl - Antispam - if (is_spam_filtered(IM_FRIENDSHIP_OFFERED, false, false)) - return; - // NaCl End // someone has offered to form a friendship LL_DEBUGS("Messaging") << "callingcard offer" << LL_ENDL; @@ -3587,6 +5518,11 @@ void process_offer_callingcard(LLMessageSystem* msg, void**) } } + // NaCl - Antispam + if (is_spam_filtered(IM_FRIENDSHIP_OFFERED, false, false, source_id.asString(), source_name)) + return; + // NaCl End + if(!source_name.empty()) { if (gAgent.isDoNotDisturb() @@ -4314,7 +6250,10 @@ void process_teleport_start(LLMessageSystem *msg, void**) { gTeleportDisplay = TRUE; gAgent.setTeleportState( LLAgent::TELEPORT_START ); - make_ui_sound("UISndTeleportOut"); + if(gSavedSettings.getBOOL("OptionPlayTpSound")) + { + make_ui_sound("UISndTeleportOut"); + } LL_INFOS("Messaging") << "Teleport initiated by remote TeleportStart message with TeleportFlags: " << teleport_flags << LL_ENDL; @@ -4946,10 +6885,10 @@ void send_agent_update(BOOL force_send, BOOL send_reliable) U32 control_flags = gAgent.getControlFlags(); // - if(gSavedSettings.getBOOL("Nimble")) - { + // if(gSavedSettings.getBOOL("Nimble")) + // { control_flags |= AGENT_CONTROL_FINISH_ANIM; - } + // } // MASK key_mask = gKeyboard->currentMask(TRUE); @@ -7299,7 +9238,7 @@ void process_script_question(LLMessageSystem *msg, void **user_data) std::string self_name; LLAgentUI::buildFullname( self_name ); // NaCl - Antispam - if (is_spam_filtered(IM_COUNT, false, owner_name == self_name)) return; + if (is_spam_filtered(IM_COUNT, false, owner_name == self_name, taskid.asString(), object_name)) return; // NaCl End if( owner_name == self_name ) { @@ -7690,6 +9629,27 @@ void send_simple_im(const LLUUID& to_id, EMPTY_BINARY_BUCKET_SIZE); } + +void send_nothing_im(const LLUUID& to_id, const std::string& message) +{ + std::string my_name; + LLAgentUI::buildFullname(my_name); + + LLUUID null; + null.setNull(); + + send_improved_im(to_id, + my_name, + message, + IM_ONLINE, + IM_NOTHING_SPECIAL, + null, + NO_TIMESTAMP, + (U8*)EMPTY_BINARY_BUCKET, + EMPTY_BINARY_BUCKET_SIZE); +} + + void send_group_notice(const LLUUID& group_id, const std::string& subject, const std::string& message, @@ -8083,6 +10043,7 @@ void process_script_dialog(LLMessageSystem* msg, void**) LLSD payload; LLUUID object_id; + msg->getUUID("Data", "ObjectID", object_id); // NaCl - Antispam Registry @@ -8102,10 +10063,6 @@ void process_script_dialog(LLMessageSystem* msg, void**) // NaCl End } - // NaCl - Antispam - if (owner_id.isNull() ? is_spam_filtered(IM_COUNT, LLAvatarActions::isFriend(object_id), object_id == gAgentID) : is_spam_filtered(IM_COUNT, LLAvatarActions::isFriend(owner_id), owner_id == gAgentID)) return; - // NaCl End - if (LLMuteList::getInstance()->isMuted(object_id) || LLMuteList::getInstance()->isMuted(owner_id)) { return; @@ -8120,6 +10077,12 @@ void process_script_dialog(LLMessageSystem* msg, void**) msg->getString("Data", "FirstName", first_name); msg->getString("Data", "LastName", last_name); msg->getString("Data", "ObjectName", object_name); + + // NaCl - Antispam + // Lirusaito: Objects can be friends? Should be 'false' instead of 'LLAvatarActions::isFriend(object_id)'? + if (owner_id.isNull() ? is_spam_filtered(IM_COUNT, LLAvatarActions::isFriend(object_id), object_id == gAgentID, object_id.asString(), object_name + " (owned by " + first_name + " " + last_name + ")") : is_spam_filtered(IM_COUNT, LLAvatarActions::isFriend(owner_id), owner_id == gAgentID, object_id.asString(), object_name + " (owned by " + first_name + " " + last_name + ")")) return; + // NaCl End + msg->getString("Data", "Message", message); msg->getS32("Data", "ChatChannel", chat_channel); @@ -8266,7 +10229,7 @@ void process_load_url(LLMessageSystem* msg, void**) msg->getUUID( "Data", "OwnerID", owner_id); // NaCl - Antispam - if (owner_id.isNull() ? is_spam_filtered(IM_COUNT, LLAvatarActions::isFriend(object_id), object_id == gAgentID) : is_spam_filtered(IM_COUNT, LLAvatarActions::isFriend(owner_id), owner_id == gAgentID)) return; + if (owner_id.isNull() ? is_spam_filtered(IM_COUNT, LLAvatarActions::isFriend(object_id), object_id == gAgentID, object_id.asString(), object_name) : is_spam_filtered(IM_COUNT, LLAvatarActions::isFriend(owner_id), owner_id == gAgentID, owner_id.asString(), object_name)) return; // NaCl End // NaCl - Antispam Registry @@ -8349,10 +10312,6 @@ void process_initiate_download(LLMessageSystem* msg, void**) void process_script_teleport_request(LLMessageSystem* msg, void**) { if (!gSavedSettings.getBOOL("ScriptsCanShowUI")) return; - - // NaCl - Antispam - if (is_spam_filtered(IM_COUNT, false, false)) return; - // NaCl End std::string object_name; std::string sim_name; @@ -8362,6 +10321,17 @@ void process_script_teleport_request(LLMessageSystem* msg, void**) msg->getString("Data", "ObjectName", object_name); msg->getString("Data", "SimName", sim_name); msg->getVector3("Data", "SimPosition", pos); + + // NaCl - Antispam + { + LLUUID null; + null.setNull(); + std::string info = ": Script teleport request to " + sim_name + " (" + boost::lexical_cast(pos.mV[VX]) + ", " + boost::lexical_cast(pos.mV[VY]) + ", " + boost::lexical_cast(pos.mV[VZ]) + ")"; + if (is_spam_filtered(IM_COUNT, false, false, null.asString(), object_name + info)) return; + } + // NaCl End + + msg->getVector3("Data", "LookAt", look_at); gFloaterWorldMap->trackURL(sim_name, (S32)pos.mV[VX], (S32)pos.mV[VY], (S32)pos.mV[VZ]); @@ -8370,7 +10340,6 @@ void process_script_teleport_request(LLMessageSystem* msg, void**) // remove above two lines and replace with below line // to re-enable parcel browser for llMapDestination() // LLURLDispatcher::dispatch(LLSLURL::buildSLURL(sim_name, (S32)pos.mV[VX], (S32)pos.mV[VY], (S32)pos.mV[VZ]), FALSE); - } void process_covenant_reply(LLMessageSystem* msg, void**) diff --git a/indra/newview/llviewermessage.h b/indra/newview/llviewermessage.h index ea3275cd7b..fac4e9bc7f 100644 --- a/indra/newview/llviewermessage.h +++ b/indra/newview/llviewermessage.h @@ -154,6 +154,8 @@ void send_simple_im(const LLUUID& to_id, EInstantMessage dialog = IM_NOTHING_SPECIAL, const LLUUID& id = LLUUID::null); +void send_nothing_im(const LLUUID& to_id, const std::string& message); + void send_group_notice(const LLUUID& group_id, const std::string& subject, const std::string& message, diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index f5b2a8abe1..be461b3ac3 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -1,21 +1,21 @@ -/** +/** * @file llviewerparcelmgr.cpp * @brief Viewer-side representation of owned land * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -910,6 +910,7 @@ void LLViewerParcelMgr::renderParcelCollision() if (mCollisionTimer.getElapsedTimeF32() > PARCEL_COLLISION_DRAW_SECS) { mRenderCollision = FALSE; + return; } static const LLCachedControl ShowBanLines("ShowBanLines"); @@ -936,16 +937,16 @@ void LLViewerParcelMgr::sendParcelAccessListRequest(U32 flags) if (!region) return; LLMessageSystem *msg = gMessageSystem; - - if (flags & AL_BAN) + + if (flags & AL_BAN) { mCurrentParcel->mBanList.clear(); } - if (flags & AL_ACCESS) + if (flags & AL_ACCESS) { mCurrentParcel->mAccessList.clear(); - } + } // Only the headers differ msg->newMessageFast(_PREHASH_ParcelAccessListRequest); @@ -1080,7 +1081,7 @@ void LLViewerParcelMgr::sendParcelRelease() { if (!mSelected) { - LLNotificationsUtil::add("CannotReleaseLandNothingSelected"); + LLNotificationsUtil::add("CannotReleaseLandNothingSelected"); return; } @@ -1119,7 +1120,7 @@ class LLViewerParcelMgr::ParcelBuyInfo BOOL mRemoveContribution; BOOL mIsClaim; LLHost mHost; - + // for parcel buys S32 mParcelID; S32 mPrice; @@ -1152,7 +1153,7 @@ LLViewerParcelMgr::ParcelBuyInfo* LLViewerParcelMgr::setupParcelBuy( LLNotificationsUtil::add("CannotBuyLandNoRegion"); return NULL; } - + if (is_claim) { LL_INFOS() << "Claiming " << mWestSouth << " to " << mEastNorth << LL_ENDL; @@ -1171,10 +1172,10 @@ LLViewerParcelMgr::ParcelBuyInfo* LLViewerParcelMgr::setupParcelBuy( return NULL; } } - - + + ParcelBuyInfo* info = new ParcelBuyInfo; - + info->mAgent = agent_id; info->mSession = session_id; info->mGroup = group_id; @@ -1184,7 +1185,7 @@ LLViewerParcelMgr::ParcelBuyInfo* LLViewerParcelMgr::setupParcelBuy( info->mHost = region->getHost(); info->mPrice = mCurrentParcel->getSalePrice(); info->mArea = mCurrentParcel->getArea(); - + if (!is_claim) { info->mParcelID = mCurrentParcel->getLocalID(); @@ -1194,13 +1195,13 @@ LLViewerParcelMgr::ParcelBuyInfo* LLViewerParcelMgr::setupParcelBuy( // BUG: Make work for cross-region selections LLVector3 west_south_bottom_region = region->getPosRegionFromGlobal( mWestSouth ); LLVector3 east_north_top_region = region->getPosRegionFromGlobal( mEastNorth ); - + info->mWest = west_south_bottom_region.mV[VX]; info->mSouth = west_south_bottom_region.mV[VY]; info->mEast = east_north_top_region.mV[VX]; info->mNorth = east_north_top_region.mV[VY]; } - + return info; } @@ -1373,12 +1374,12 @@ void LLViewerParcelMgr::setHoverParcel(const LLVector3d& pos) // last parcel grid step U32 west_parcel_step = (U32) floor( pos.mdV[VX] / PARCEL_GRID_STEP_METERS ); U32 south_parcel_step = (U32) floor( pos.mdV[VY] / PARCEL_GRID_STEP_METERS ); - + if ((west_parcel_step == last_west) && (south_parcel_step == last_south)) { return; } - else + else { last_west = west_parcel_step; last_south = south_parcel_step; @@ -1744,7 +1745,7 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use { parcel_mgr.mCollisionBanned = BA_NOT_IN_GROUP; } - else + else { parcel_mgr.mCollisionBanned = BA_NOT_ON_LIST; @@ -1869,7 +1870,7 @@ void optionally_start_music(LLParcel* parcel) return; } } - gAudiop->startInternetStream(LLStringUtil::null); + gAudiop->startInternetStream(LLStringUtil::null); } } @@ -1956,7 +1957,7 @@ void LLViewerParcelMgr::sendParcelAccessListUpdate(U32 which) if (!parcel) return; if (which & AL_ACCESS) - { + { S32 count = parcel->mAccessList.size(); S32 num_sections = (S32) ceil(count/PARCEL_MAX_ENTRIES_PER_PACKET); S32 sequence_id = 1; @@ -1965,9 +1966,9 @@ void LLViewerParcelMgr::sendParcelAccessListUpdate(U32 which) access_map_const_iterator cit = parcel->mAccessList.begin(); access_map_const_iterator end = parcel->mAccessList.end(); - while ( (cit != end) || initial ) - { - if (start_message) + while ( (cit != end) || initial ) + { + if (start_message) { msg->newMessageFast(_PREHASH_ParcelAccessListUpdate); msg->nextBlockFast(_PREHASH_AgentData); @@ -1994,12 +1995,12 @@ void LLViewerParcelMgr::sendParcelAccessListUpdate(U32 which) sequence_id++; } - - while ( (cit != end) && (msg->getCurrentSendTotal() < MTUBYTES)) + + while ( (cit != end) && (msg->getCurrentSendTotal() < MTUBYTES)) { const LLAccessEntry& entry = (*cit).second; - + msg->nextBlockFast(_PREHASH_List); msg->addUUIDFast(_PREHASH_ID, entry.mID ); msg->addS32Fast(_PREHASH_Time, entry.mTime ); @@ -2013,7 +2014,7 @@ void LLViewerParcelMgr::sendParcelAccessListUpdate(U32 which) } if (which & AL_BAN) - { + { S32 count = parcel->mBanList.size(); S32 num_sections = (S32) ceil(count/PARCEL_MAX_ENTRIES_PER_PACKET); S32 sequence_id = 1; @@ -2022,9 +2023,9 @@ void LLViewerParcelMgr::sendParcelAccessListUpdate(U32 which) access_map_const_iterator cit = parcel->mBanList.begin(); access_map_const_iterator end = parcel->mBanList.end(); - while ( (cit != end) || initial ) + while ( (cit != end) || initial ) { - if (start_message) + if (start_message) { msg->newMessageFast(_PREHASH_ParcelAccessListUpdate); msg->nextBlockFast(_PREHASH_AgentData); @@ -2051,11 +2052,11 @@ void LLViewerParcelMgr::sendParcelAccessListUpdate(U32 which) sequence_id++; } - - while ( (cit != end) && (msg->getCurrentSendTotal() < MTUBYTES)) + + while ( (cit != end) && (msg->getCurrentSendTotal() < MTUBYTES)) { const LLAccessEntry& entry = (*cit).second; - + msg->nextBlockFast(_PREHASH_List); msg->addUUIDFast(_PREHASH_ID, entry.mID ); msg->addS32Fast(_PREHASH_Time, entry.mTime ); @@ -2170,7 +2171,7 @@ bool LLViewerParcelMgr::canAgentBuyParcel(LLParcel* parcel, bool forGroup) const { return false; } - + if (mSelected && parcel == mCurrentParcel) { if (mRequestResult == PARCEL_RESULT_NO_DATA) @@ -2178,7 +2179,7 @@ bool LLViewerParcelMgr::canAgentBuyParcel(LLParcel* parcel, bool forGroup) const return false; } } - + const LLUUID& parcelOwner = parcel->getOwnerID(); const LLUUID& authorizeBuyer = parcel->getAuthorizedBuyerID(); @@ -2186,7 +2187,7 @@ bool LLViewerParcelMgr::canAgentBuyParcel(LLParcel* parcel, bool forGroup) const { return true; // change this if want to make it gods only } - + LLViewerRegion* regionp = LLViewerParcelMgr::getInstance()->getSelectionRegion(); if (regionp) { @@ -2204,23 +2205,23 @@ bool LLViewerParcelMgr::canAgentBuyParcel(LLParcel* parcel, bool forGroup) const { return false; } - } - + } + bool isForSale = parcel->getForSale() && ((parcel->getSalePrice() > 0) || (authorizeBuyer.notNull())); - + bool isEmpowered = forGroup ? gAgent.hasPowerInActiveGroup(GP_LAND_DEED) == TRUE : true; - + bool isOwner = parcelOwner == (forGroup ? gAgent.getGroupID() : gAgent.getID()); - + bool isAuthorized = (authorizeBuyer.isNull() || (gAgent.getID() == authorizeBuyer) || (gAgent.hasPowerInGroup(authorizeBuyer,GP_LAND_DEED) && gAgent.hasPowerInGroup(authorizeBuyer,GP_LAND_SET_SALE_INFO))); - + return isForSale && !isOwner && isAuthorized && isEmpowered; } @@ -2455,11 +2456,11 @@ void LLViewerParcelMgr::buyPass() } //Tells whether we are allowed to buy a pass or not -BOOL LLViewerParcelMgr::isCollisionBanned() -{ +BOOL LLViewerParcelMgr::isCollisionBanned() +{ if ((mCollisionBanned == BA_ALLOWED) || (mCollisionBanned == BA_NOT_ON_LIST) || (mCollisionBanned == BA_NOT_IN_GROUP)) return FALSE; - else + else return TRUE; } diff --git a/indra/newview/llviewerpluginmanager.h b/indra/newview/llviewerpluginmanager.h index e10a567287..6cc11b3ba6 100644 --- a/indra/newview/llviewerpluginmanager.h +++ b/indra/newview/llviewerpluginmanager.h @@ -39,7 +39,10 @@ #include "lldir.h" #include "llfile.h" #include "llviewercontrol.h" +#ifndef LL_LLPLUGINCLASSBASIC_H #include "llpluginclassbasic.h" +#endif + class LLViewerPluginManager : public LLRefCount { diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 3ff7a33e0d..f23a5a2806 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -443,9 +443,9 @@ void reset_statistics() void output_statistics(void*) { - S32 global_raw_memory; + U64 global_raw_memory; { - global_raw_memory = *AIAccess(LLImageRaw::sGlobalRawMemory); + global_raw_memory = *AIAccess(LLImageRaw::sGlobalRawMemory); } LL_INFOS() << "Number of orphans: " << gObjectList.getOrphanCount() << LL_ENDL; LL_INFOS() << "Number of dead objects: " << gObjectList.mNumDeadObjects << LL_ENDL; diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp index 32d2f70af8..94acb7cfc1 100644 --- a/indra/newview/llviewertexteditor.cpp +++ b/indra/newview/llviewertexteditor.cpp @@ -1289,19 +1289,26 @@ std::string LLViewerTextEditor::appendTime(bool prepend_newline) // There's only one internal tm buffer. struct tm* timep; - // Convert to Pacific, based on server's opinion of whether - // it's daylight savings time there. - timep = utc_to_pacific_time(utc_time, gPacificDaylightTime); + // PDT/PDS is totally irrelevant + static const LLCachedControl use_local_time("RtyChatUsesLocalTime"); - std::string format = ""; - if (gSavedSettings.getBOOL("SecondsInChatAndIMs")) + if(use_local_time) { - format = gSavedSettings.getString("LongTimeFormat"); + // use local time + timep = std::localtime(&utc_time); } else { - format = gSavedSettings.getString("ShortTimeFormat"); + // Convert to Pacific, based on server's opinion of whether + // it's daylight savings time there. + timep = utc_to_pacific_time(utc_time, gPacificDaylightTime); } + + static const LLCachedControl show_seconds("SecondsInChatAndIMs"); + static const LLCachedControl format_long("LongTimeFormat"); + static const LLCachedControl format_short("ShortTimeFormat"); + std::string format = show_seconds ? format_long : format_short; + std::string text; timeStructToFormattedString(timep, format, text); text = "[" + text + "] "; diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 11b05813c2..68918b5f92 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -86,8 +86,8 @@ F32 LLViewerTexture::sDesiredDiscardScale = 1.1f; S32 LLViewerTexture::sBoundTextureMemoryInBytes = 0; S32 LLViewerTexture::sTotalTextureMemoryInBytes = 0; S32 LLViewerTexture::sMaxBoundTextureMemInMegaBytes = 0; -S32 LLViewerTexture::sMaxTotalTextureMemInMegaBytes = 0; -S32 LLViewerTexture::sMaxDesiredTextureMemInBytes = 0 ; +S64 LLViewerTexture::sMaxTotalTextureMemInMegaBytes = 0; +S64 LLViewerTexture::sMaxDesiredTextureMemInBytes = 0 ; S8 LLViewerTexture::sCameraMovingDiscardBias = 0 ; F32 LLViewerTexture::sCameraMovingBias = 0.0f ; S32 LLViewerTexture::sMaxSculptRez = 128 ; //max sculpt image size @@ -493,6 +493,9 @@ static LLFastTimer::DeclareTimer FTM_TEXTURE_UPDATE_MEDIA("Media"); #if 0 static LLFastTimer::DeclareTimer FTM_TEXTURE_UPDATE_TEST("Test"); #endif +// default is 0.75 with max 512MB --- use less reduction with max 2048MB +// to free the same amount of memory (128MB) +#define TX_REDUCTION_FACTOR 0.9375f //static void LLViewerTexture::updateClass(const F32 velocity, const F32 angular_velocity) { @@ -521,8 +524,9 @@ void LLViewerTexture::updateClass(const F32 velocity, const F32 angular_velocity BYTES_TO_MEGA_BYTES(sTotalTextureMemoryInBytes) >= sMaxTotalTextureMemInMegaBytes) { //when texture memory overflows, lower down the threashold to release the textures more aggressively. - sMaxDesiredTextureMemInBytes = llmin((S32)(sMaxDesiredTextureMemInBytes * 0.75f) , MEGA_BYTES_TO_BYTES(MAX_VIDEO_RAM_IN_MEGA_BYTES)) ;//512 MB - + // sMaxDesiredTextureMemInBytes = llmin((S32)(sMaxDesiredTextureMemInBytes * TX_REDUCTION_FACTOR) , MEGA_BYTES_TO_BYTES(MAX_VIDEO_RAM_IN_MEGA_BYTES)); + // RTY mark (there is no floating point math here) + // If we are using more texture memory than we should, // scale up the desired discard level if (sEvaluationTimer.getElapsedTimeF32() > discard_delta_time) @@ -550,15 +554,16 @@ void LLViewerTexture::updateClass(const F32 velocity, const F32 angular_velocity } sDesiredDiscardBias = llclamp(sDesiredDiscardBias, desired_discard_bias_min, desired_discard_bias_max); //LLViewerTexture::sUseTextureAtlas = gSavedSettings.getBOOL("EnableTextureAtlas") ; - + F32 camera_moving_speed = LLViewerCamera::getInstance()->getAverageSpeed() ; F32 camera_angular_speed = LLViewerCamera::getInstance()->getAverageAngularSpeed(); sCameraMovingBias = llmax(0.2f * camera_moving_speed, 2.0f * camera_angular_speed - 1); sCameraMovingDiscardBias = (S8)(sCameraMovingBias); - LLViewerTexture::sFreezeImageScalingDown = (BYTES_TO_MEGA_BYTES(sBoundTextureMemoryInBytes) < 0.75f * sMaxBoundTextureMemInMegaBytes * texmem_middle_bound_scale) && - (BYTES_TO_MEGA_BYTES(sTotalTextureMemoryInBytes) < 0.75f * sMaxTotalTextureMemInMegaBytes * texmem_middle_bound_scale) ; + LLViewerTexture::sFreezeImageScalingDown = (BYTES_TO_MEGA_BYTES(sBoundTextureMemoryInBytes) < TX_REDUCTION_FACTOR * sMaxBoundTextureMemInMegaBytes * texmem_middle_bound_scale) && + (BYTES_TO_MEGA_BYTES(sTotalTextureMemoryInBytes) < TX_REDUCTION_FACTOR * sMaxTotalTextureMemInMegaBytes * texmem_middle_bound_scale) ; } +#undef TX_REDUCTION_FACTOR //end of static functions //------------------------------------------------------------------------------------------- @@ -1160,11 +1165,13 @@ void LLViewerFetchedTexture::destroyTexture() { return ; } + if (mNeedsCreateTexture)//return if in the process of generating a new texture. { return ; } - + + // llinfos << "RTY destroying tex, texmem: " << BYTES_TO_MEGA_BYTES(LLImageGL::sGlobalTextureMemoryInBytes) << " MB used of " << BYTES_TO_MEGA_BYTES(sMaxDesiredTextureMemInBytes) << llendl; destroyGLTexture() ; mFullyLoaded = FALSE ; } @@ -1549,7 +1556,8 @@ F32 LLViewerFetchedTexture::calcDecodePriority() //Note: //to give small, low-priority textures some chance to be fetched, //cut the priority in half if the texture size is larger than 256 * 256 and has a 64*64 ready. - priority *= 0.5f ; + //priority *= 0.5f ; + priority *= gFrameIntervalSeconds; } pixel_priority = llclamp(pixel_priority, 0.0f, MAX_PRIORITY_PIXEL); @@ -1585,7 +1593,8 @@ F32 LLViewerFetchedTexture::calcDecodePriority() //Note: //to give small, low-priority textures some chance to be fetched, //cut the additional priority to a quarter if the texture size is larger than 256 * 256 and has a 64*64 ready. - additional *= 0.25f ; + //additional *= 0.25f ; + additional *= gFrameIntervalSeconds; } priority += additional; } @@ -1609,7 +1618,7 @@ F32 LLViewerFetchedTexture::maxDecodePriority() void LLViewerFetchedTexture::setDecodePriority(F32 priority) { - llassert(!mInImageList); + llassert(!mInImageList); mDecodePriority = priority; @@ -2400,7 +2409,7 @@ bool LLViewerFetchedTexture::doLoadedCallbacks() // Done with any raw image data at this point (will be re-created if we still have callbacks) destroyRawImage(); - + return res; } diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index 94eeaf19ea..f3fa6e7c18 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -42,7 +42,8 @@ #include #define MIN_VIDEO_RAM_IN_MEGA_BYTES 32 -#define MAX_VIDEO_RAM_IN_MEGA_BYTES 512 // 512MB max for performance reasons. +//#define MAX_VIDEO_RAM_IN_MEGA_BYTES 512 // 512MB max for performance reasons. +#define MAX_VIDEO_RAM_IN_MEGA_BYTES 2048 // why keep performance low? class LLImageGL ; class LLImageRaw; @@ -213,8 +214,8 @@ class LLViewerTexture : public LLGLTexture static S32 sBoundTextureMemoryInBytes; static S32 sTotalTextureMemoryInBytes; static S32 sMaxBoundTextureMemInMegaBytes; - static S32 sMaxTotalTextureMemInMegaBytes; - static S32 sMaxDesiredTextureMemInBytes ; + static S64 sMaxTotalTextureMemInMegaBytes; + static S64 sMaxDesiredTextureMemInBytes ; static S8 sCameraMovingDiscardBias; static F32 sCameraMovingBias; static S32 sMaxSculptRez ; diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index a5ec4c9a7f..7c91a0a9b9 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -1,11 +1,11 @@ -/** +/** * @file llviewertexturelist.cpp * @brief Object for managing the list of images within a region * * $LicenseInfo:firstyear=2000&license=viewergpl$ - * + * * Copyright (c) 2000-2010, Linden Research, Inc. - * + * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 @@ -13,22 +13,22 @@ * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlife.com/developers/opensource/gplv2 - * + * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at * http://secondlife.com/developers/opensource/flossexception - * + * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. - * + * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ - * + * */ #include "llviewerprecompiledheaders.h" @@ -81,7 +81,7 @@ static LLFastTimer::DeclareTimer FTM_PROCESS_IMAGES("Process Images"); /////////////////////////////////////////////////////////////////////////////// -LLViewerTextureList::LLViewerTextureList() +LLViewerTextureList::LLViewerTextureList() : mForceResetTextureStats(FALSE), mUpdateStats(FALSE), mMaxResidentTexMemInMegaBytes(0), @@ -102,10 +102,10 @@ void LLViewerTextureList::init() // Don't initialize GL stuff if we're not rendering. return; } - + // Update how much texture RAM we're allowed to use. updateMaxResidentTexMem(0); // 0 = use current - + doPreloadImages(); } @@ -113,14 +113,14 @@ void LLViewerTextureList::init() void LLViewerTextureList::doPreloadImages() { LL_DEBUGS("ViewerImages") << "Preloading images..." << LL_ENDL; - + llassert_always(mInitialized) ; llassert_always(mImageList.empty()) ; llassert_always(mUUIDMap.empty()) ; // Set the "missing asset" image LLViewerFetchedTexture::sMissingAssetImagep = LLViewerTextureManager::getFetchedTextureFromFile("missing_asset.tga", MIPMAP_NO, LLViewerFetchedTexture::BOOST_UI); - + // Set the "white" image LLViewerFetchedTexture::sWhiteImagep = LLViewerTextureManager::getFetchedTextureFromFile("white.tga", MIPMAP_NO, LLViewerFetchedTexture::BOOST_UI); LLTexUnit::sWhiteTexture = LLViewerFetchedTexture::sWhiteImagep->getTexName(); @@ -128,7 +128,7 @@ void LLViewerTextureList::doPreloadImages() LLViewerFetchedTexture::sFlatNormalImagep = LLViewerTextureManager::getFetchedTextureFromFile("flatnormal.tga", MIPMAP_NO, LLViewerFetchedTexture::BOOST_BUMP); image_list->initFromFile(); - + // turn off clamping and bilinear filtering for uv picking images //LLViewerFetchedTexture* uv_test = preloadUIImage("uv_test1.tga", LLUUID::null, FALSE); //uv_test->setClamp(FALSE, FALSE); @@ -141,32 +141,32 @@ void LLViewerTextureList::doPreloadImages() LLViewerTextureManager::getFetchedTexture(IMG_SHOT, TRUE); LLViewerTextureManager::getFetchedTexture(IMG_SMOKE_POOF, TRUE); LLViewerFetchedTexture* image = LLViewerTextureManager::getFetchedTextureFromFile("silhouette.j2c", MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI); - if (image) + if (image) { image->setAddressMode(LLTexUnit::TAM_WRAP); mImagePreloads.insert(image); } image = LLViewerTextureManager::getFetchedTextureFromFile("noentrylines.j2c"/*"world/NoEntryLines.png"*/, MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI); - if (image) + if (image) { - image->setAddressMode(LLTexUnit::TAM_WRAP); + image->setAddressMode(LLTexUnit::TAM_WRAP); mImagePreloads.insert(image); } image = LLViewerTextureManager::getFetchedTextureFromFile("noentrypasslines.j2c"/*"world/NoEntryPassLines.png"*/, MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI); - if (image) + if (image) { image->setAddressMode(LLTexUnit::TAM_WRAP); mImagePreloads.insert(image); } image = LLViewerTextureManager::getFetchedTexture(DEFAULT_WATER_NORMAL, MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI); - if (image) + if (image) { - image->setAddressMode(LLTexUnit::TAM_WRAP); + image->setAddressMode(LLTexUnit::TAM_WRAP); mImagePreloads.insert(image); } image = LLViewerTextureManager::getFetchedTextureFromFile("transparent.j2c", MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI, LLViewerTexture::FETCHED_TEXTURE, 0,0,LLUUID("8dcd4a48-2d37-4909-9f78-f7a9eb4ef903")); - if (image) + if (image) { image->setAddressMode(LLTexUnit::TAM_WRAP); mImagePreloads.insert(image); @@ -181,7 +181,7 @@ void LLViewerTextureList::doPreloadImages() tex->setFilteringOption(LLTexUnit::TFO_POINT); } } - + } static std::string get_texture_list_name() @@ -196,7 +196,7 @@ void LLViewerTextureList::doPrefetchImages() // cache was purged, no point return; } - + // Pre-fetch textures from last logout LLSD imagelist; std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, get_texture_list_name()); @@ -263,7 +263,7 @@ void LLViewerTextureList::shutdown() image_area_list.insert(std::make_pair(pixel_area, image)); } } - + LLSD imagelist; const S32 max_count = 1000; S32 count = 0; @@ -279,7 +279,7 @@ void LLViewerTextureList::shutdown() if (++count >= max_count) break; } - + if (count > 0 && !gDirUtilp->getLindenUserDir(true).empty()) { std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, get_texture_list_name()); @@ -287,18 +287,18 @@ void LLViewerTextureList::shutdown() file.open(filename); LLSDSerialize::toPrettyXML(imagelist, file); } - + // // Clean up "loaded" callbacks. // mCallbackList.clear(); - + // Flush all of the references mLoadingStreamList.clear(); mCreateTextureList.clear(); - + mUUIDMap.clear(); - + mImageList.clear(); mInitialized = FALSE ; //prevent loading textures again. @@ -334,19 +334,19 @@ void LLViewerTextureList::restoreGL() /* Vertical tab container button image IDs Seem to not decode when running app in debug. - + const LLUUID BAD_IMG_ONE("1097dcb3-aef9-8152-f471-431d840ea89e"); const LLUUID BAD_IMG_TWO("bea77041-5835-1661-f298-47e2d32b7a70"); */ /////////////////////////////////////////////////////////////////////////////// -LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string& filename, +LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string& filename, BOOL usemipmaps, LLViewerTexture::EBoostLevel boost_priority, S8 texture_type, LLGLint internal_format, - LLGLenum primary_format, + LLGLenum primary_format, const LLUUID& force_id) { if(!mInitialized) @@ -371,10 +371,10 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string& LLViewerTexture::EBoostLevel boost_priority, S8 texture_type, LLGLint internal_format, - LLGLenum primary_format, + LLGLenum primary_format, const LLUUID& force_id) { - + if(!mInitialized) { return NULL ; @@ -414,7 +414,7 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string& << " already exists with a different url, requested: " << url << " current: " << texture->getUrl() << LL_ENDL; } - + } if (imagep.isNull()) { @@ -436,7 +436,7 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string& } addImage(imagep); - + if (boost_priority != 0) { if (boost_priority == LLViewerFetchedTexture::BOOST_UI || @@ -454,7 +454,7 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string& } -LLViewerFetchedTexture* LLViewerTextureList::getImage(const LLUUID &image_id, +LLViewerFetchedTexture* LLViewerTextureList::getImage(const LLUUID &image_id, BOOL usemipmaps, LLViewerTexture::EBoostLevel boost_priority, S8 texture_type, @@ -470,12 +470,12 @@ LLViewerFetchedTexture* LLViewerTextureList::getImage(const LLUUID &image_id, // Return the image with ID image_id // If the image is not found, creates new image and // enqueues a request for transmission - + if ((&image_id == NULL) || image_id.isNull()) { return (LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, TRUE, LLGLTexture::BOOST_UI)); } - + LLPointer imagep = findImage(image_id); if (!imagep.isNull()) { @@ -492,7 +492,7 @@ LLViewerFetchedTexture* LLViewerTextureList::getImage(const LLUUID &image_id, LL_WARNS() << "Requested texture " << image_id << " already exists with a different target host, requested: " << request_from_host << " current: " << texture->getTargetHost() << LL_ENDL; } - + } if (imagep.isNull()) { @@ -500,12 +500,12 @@ LLViewerFetchedTexture* LLViewerTextureList::getImage(const LLUUID &image_id, } imagep->setGLTextureCreated(true); - + return imagep; } //when this function is called, there is no such texture in the gTextureList with image_id. -LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id, +LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id, BOOL usemipmaps, LLViewerTexture::EBoostLevel boost_priority, S8 texture_type, @@ -525,14 +525,14 @@ LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id, default: LL_ERRS() << "Invalid texture type " << texture_type << LL_ENDL ; } - + if (internal_format && primary_format) { imagep->setExplicitFormat(internal_format, primary_format); } - + addImage(imagep); - + if (boost_priority != 0) { if (boost_priority == LLViewerFetchedTexture::BOOST_UI || @@ -570,11 +570,11 @@ void LLViewerTextureList::addImageToList(LLViewerFetchedTexture *image) { LL_ERRS() << "LLViewerTextureList::addImageToList - Image already in list" << LL_ENDL; } - if((mImageList.insert(image)).second != true) + if((mImageList.insert(image)).second != true) { LL_ERRS() << "Error happens when insert image to mImageList!" << LL_ENDL ; } - + image->setInImageList(TRUE) ; } @@ -595,12 +595,12 @@ void LLViewerTextureList::removeImageFromList(LLViewerFetchedTexture *image) } S32 count = mImageList.erase(image) ; - if(count != 1) + if(count != 1) { LL_INFOS() << image->getID() << LL_ENDL ; LL_ERRS() << "Error happens when remove image from mImageList: " << count << LL_ENDL ; } - + image->setInImageList(FALSE) ; } @@ -612,14 +612,14 @@ void LLViewerTextureList::addImage(LLViewerFetchedTexture *new_image) return; } LLUUID image_id = new_image->getID(); - + LLViewerFetchedTexture *image = findImage(image_id); if (image) { LL_WARNS() << "Image with ID " << image_id << " already in list" << LL_ENDL; } sNumImages++; - + addImageToList(new_image); mUUIDMap[image_id] = new_image; } @@ -627,7 +627,7 @@ void LLViewerTextureList::addImage(LLViewerFetchedTexture *new_image) void LLViewerTextureList::deleteImage(LLViewerFetchedTexture *image) { - if( image) + if(image && findImage(image->getID())) { if (image->hasCallbacks()) { @@ -636,6 +636,7 @@ void LLViewerTextureList::deleteImage(LLViewerFetchedTexture *image) llverify(mUUIDMap.erase(image->getID()) == 1); sNumImages--; + // llinfos << "deleting image: " << image->getID() << llendl; removeImageFromList(image); } } @@ -686,15 +687,16 @@ void LLViewerTextureList::updateImages(F32 max_time) } cleared = FALSE; - S32 global_raw_memory; + U64 global_raw_memory; { - global_raw_memory = *AIAccess(LLImageRaw::sGlobalRawMemory); + global_raw_memory = *AIAccess(LLImageRaw::sGlobalRawMemory); } LLViewerStats::getInstance()->mNumImagesStat.addValue(sNumImages); LLViewerStats::getInstance()->mNumRawImagesStat.addValue(LLImageRaw::sRawImageCount); LLViewerStats::getInstance()->mGLTexMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageGL::sGlobalTextureMemoryInBytes)); LLViewerStats::getInstance()->mGLBoundMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageGL::sBoundTextureMemoryInBytes)); - LLViewerStats::getInstance()->mRawMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(global_raw_memory)); + // LLViewerStats::getInstance()->mRawMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(global_raw_memory)); + LLViewerStats::getInstance()->mRawMemStat.addValue((F32)((F64)(global_raw_memory / 1024.f / 1024.f / 1024.f))); LLViewerStats::getInstance()->mFormattedMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageFormatted::sGlobalFormattedMemory)); @@ -709,13 +711,13 @@ void LLViewerTextureList::updateImages(F32 max_time) LLFastTimer t(FTM_IMAGE_FETCH); max_time -= updateImagesFetchTextures(max_time); } - + { LLFastTimer t(FTM_IMAGE_CREATE); max_time = llmax(max_time, total_max_time*.50f); // at least 50% of max_time max_time -= updateImagesCreateTextures(max_time); } - + if (!mDirtyTextureList.empty()) { LLFastTimer t(FTM_IMAGE_MARK_DIRTY); @@ -770,17 +772,23 @@ void LLViewerTextureList::clearFetchingRequests() void LLViewerTextureList::updateImagesDecodePriorities() { // Update the decode priority for N images each frame - { - static const S32 MAX_PRIO_UPDATES = gSavedSettings.getS32("TextureFetchUpdatePriorities"); // default: 32 - const size_t max_update_count = llmin((S32) (MAX_PRIO_UPDATES*MAX_PRIO_UPDATES*gFrameIntervalSeconds) + 1, MAX_PRIO_UPDATES); - S32 update_counter = llmin(max_update_count, mUUIDMap.size()); - uuid_map_t::iterator iter = mUUIDMap.upper_bound(mLastUpdateUUID); - while ((update_counter-- > 0) && !mUUIDMap.empty()) + + // static const S32 MAX_PRIO_UPDATES = gSavedSettings.getS32("TextureFetchUpdatePriorities"); // default: 32 + // const size_t max_update_count = llmin((S32) (MAX_PRIO_UPDATES*MAX_PRIO_UPDATES*gFrameIntervalSeconds) + 1, MAX_PRIO_UPDATES); + + static LLCachedControl MAX_PRIO_UPDATES(gSavedSettings, "TextureFetchUpdatePriorities"); + size_t max_update_count = MAX_PRIO_UPDATES * (1.0f / gFrameIntervalSeconds); + S32 update_counter = llmin(max_update_count, mUUIDMap.size()); + // llinfos << "mapsize: " << mUUIDMap.size() << " update: " << update_counter << llendl; + + uuid_map_t::iterator iter = mUUIDMap.upper_bound(mLastUpdateUUID); + while ((update_counter-- > 0) && !mUUIDMap.empty()) { if (iter == mUUIDMap.end()) - { - iter = mUUIDMap.begin(); - } + { + iter = mUUIDMap.begin(); + } + mLastUpdateUUID = iter->first; LLPointer imagep = iter->second; ++iter; // safe to incrament now @@ -788,76 +796,83 @@ void LLViewerTextureList::updateImagesDecodePriorities() // // Flush formatted images using a lazy flush // - const F32 LAZY_FLUSH_TIMEOUT = 30.f; // stop decoding - const F32 MAX_INACTIVE_TIME = 50.f; // actually delete - S32 min_refs = 3; // 1 for mImageList, 1 for mUUIDMap, 1 for local reference - + // const F32 LAZY_FLUSH_TIMEOUT = 30.f; // stop decoding + // const F32 MAX_INACTIVE_TIME = 50.f; // actually delete + const F32 LAZY_FLUSH_TIMEOUT = 45.0f; // stop decoding + const F32 MAX_INACTIVE_TIME = 90.0f; // actually delete + + const S32 min_refs = 3; // 1 for mImageList, 1 for mUUIDMap, 1 for local reference + S32 num_refs = imagep->getNumRefs(); if (num_refs == min_refs) - { - if (imagep->getLastReferencedTimer()->getElapsedTimeF32() > LAZY_FLUSH_TIMEOUT) { - // Remove the unused image from the image list - deleteImage(imagep); - imagep = NULL; // should destroy the image - } - continue; - } - else - { - if(imagep->hasSavedRawImage()) - { - if(imagep->getElapsedLastReferencedSavedRawImageTime() > MAX_INACTIVE_TIME) - { - imagep->destroySavedRawImage() ; - } - } - - if(imagep->isDeleted()) - { - continue ; - } - else if(imagep->isDeletionCandidate()) - { - imagep->destroyTexture() ; - continue ; - } - else if(imagep->isInactive()) - { - if (imagep->getLastReferencedTimer()->getElapsedTimeF32() > MAX_INACTIVE_TIME) - { - imagep->setDeletionCandidate() ; - } - continue ; + if (imagep->getLastReferencedTimer()->getElapsedTimeF32() > LAZY_FLUSH_TIMEOUT) + { + // Remove the unused image from the image list + deleteImage(imagep); + imagep = NULL; // should destroy the image + } + continue; } - else + // else { + if(imagep->hasSavedRawImage()) + { + if(imagep->getElapsedLastReferencedSavedRawImageTime() > MAX_INACTIVE_TIME) + { + imagep->destroySavedRawImage() ; + } + } + + if(imagep->isDeleted()) + { + continue ; + } + // else + if(imagep->isDeletionCandidate()) + { + imagep->destroyTexture() ; + continue ; + } + // else + if(imagep->isInactive()) + { + if (imagep->getLastReferencedTimer()->getElapsedTimeF32() > MAX_INACTIVE_TIME) + { + imagep->setDeletionCandidate() ; + } + continue ; + } + // else + // { imagep->getLastReferencedTimer()->reset(); //reset texture state. - imagep->setInactive() ; + imagep->setInactive() ; + // } } - } - + if (!imagep->isInImageList()) - { - continue; - } + { + continue; + } + imagep->processTextureStats(); F32 old_priority = imagep->getDecodePriority(); F32 old_priority_test = llmax(old_priority, 0.0f); F32 decode_priority = imagep->calcDecodePriority(); F32 decode_priority_test = llmax(decode_priority, 0.0f); // Ignore < 20% difference - if ((decode_priority_test < old_priority_test * .8f) || - (decode_priority_test > old_priority_test * 1.25f)) - { - removeImageFromList(imagep); - imagep->setDecodePriority(decode_priority); - addImageToList(imagep); - } + //if ((decode_priority_test < old_priority_test * .8f) || + // (decode_priority_test > old_priority_test * 1.2f)) + if ((decode_priority_test < old_priority_test * .4f) || (decode_priority_test > old_priority_test * 1.6f)) + + { + removeImageFromList(imagep); + imagep->setDecodePriority(decode_priority); + addImageToList(imagep); + } } - } } /* @@ -865,13 +880,13 @@ void LLViewerTextureList::updateImagesDecodePriorities() { // Having a target host implies this is a baked image. I don't // believe that boost level has been set at this point. JC - U8 type_from_host = (target_host.isOk() - ? LLImageBase::TYPE_AVATAR_BAKE + U8 type_from_host = (target_host.isOk() + ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL); S32 boost_level = imagep->getBoostLevel(); - U8 type_from_boost = ( (boost_level == LLViewerFetchedTexture::BOOST_AVATAR_BAKED + U8 type_from_boost = ( (boost_level == LLViewerFetchedTexture::BOOST_AVATAR_BAKED || boost_level == LLViewerFetchedTexture::BOOST_AVATAR_BAKED_SELF) - ? LLImageBase::TYPE_AVATAR_BAKE + ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL); if (type_from_host == LLImageBase::TYPE_NORMAL && type_from_boost == LLImageBase::TYPE_AVATAR_BAKE) @@ -890,13 +905,13 @@ void LLViewerTextureList::updateImagesDecodePriorities() F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time) { if (gNoRender || gGLManager.mIsDisabled) return 0.0f; - + // // Create GL textures for all textures that need them (images which have been // decoded, but haven't been pushed into GL). // LLFastTimer t(FTM_IMAGE_CREATE); - + LLTimer create_timer; image_list_t::iterator enditer = mCreateTextureList.begin(); for (image_list_t::iterator iter = mCreateTextureList.begin(); @@ -930,14 +945,14 @@ void LLViewerTextureList::forceImmediateUpdate(LLViewerFetchedTexture* imagep) F32 decode_priority = LLViewerFetchedTexture::maxDecodePriority() ; imagep->setDecodePriority(decode_priority); addImageToList(imagep); - + return ; } F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time) { LLTimer image_op_timer; - + // Update fetch for N images each frame static const S32 MAX_HIGH_PRIO_COUNT = gSavedSettings.getS32("TextureFetchUpdateHighPriority"); // default: 32 static const S32 MAX_UPDATE_COUNT = gSavedSettings.getS32("TextureFetchUpdateMaxMediumPriority"); // default: 256 @@ -948,12 +963,12 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time) size_t max_priority_count = llmin((S32) (MAX_HIGH_PRIO_COUNT*MAX_HIGH_PRIO_COUNT*gFrameIntervalSeconds)+1, MAX_HIGH_PRIO_COUNT); //Old: size_t max_priority_count = llmin((S32) (MAX_HIGH_PRIO_COUNT*40.f*gFrameIntervalSeconds)+1, MAX_HIGH_PRIO_COUNT); max_priority_count = llmin(max_priority_count, mImageList.size()); - + size_t total_update_count = mUUIDMap.size(); size_t max_update_count = llmin((S32) (MAX_UPDATE_COUNT*MAX_UPDATE_COUNT*gFrameIntervalSeconds)+1, MAX_UPDATE_COUNT); //Old: size_t max_priority_count = llmin((S32) (MAX_UPDATE_COUNT*40.f*gFrameIntervalSeconds)+1, MAX_UPDATE_COUNT); - max_update_count = llmin(max_update_count, total_update_count); - + max_update_count = llmin(max_update_count, total_update_count); + // MAX_HIGH_PRIO_COUNT high priority entries typedef std::vector entries_list_t; entries_list_t entries; @@ -962,13 +977,13 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time) while(update_counter > 0) { entries.push_back(*iter1); - + ++iter1; update_counter--; } - + // MAX_UPDATE_COUNT cycled entries - update_counter = max_update_count; + update_counter = max_update_count; if(update_counter > 0) { uuid_map_t::iterator iter2 = mUUIDMap.upper_bound(mLastFetchUUID); @@ -979,18 +994,18 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time) iter2 = mUUIDMap.begin(); } LLViewerFetchedTexture* imagep = iter2->second; - // Skip the textures where there's really nothing to do so to give some times to others. Also skip the texture if it's already in the high prio set. - if (!SKIP_LOW_PRIO || (SKIP_LOW_PRIO && ((imagep->getDecodePriority() > MIN_PRIORITY_THRESHOLD) || imagep->hasFetcher()))) - { - entries.push_back(imagep); - update_counter--; - } + // Skip the textures where there's really nothing to do so to give some times to others. Also skip the texture if it's already in the high prio set. + if (!SKIP_LOW_PRIO || (SKIP_LOW_PRIO && ((imagep->getDecodePriority() > MIN_PRIORITY_THRESHOLD) || imagep->hasFetcher()))) + { + entries.push_back(imagep); + update_counter--; + } iter2++; total_update_count--; } } - + S32 fetch_count = 0; size_t min_update_count = llmin(MIN_UPDATE_COUNT,(S32)(entries.size()-max_priority_count)); S32 min_count = max_priority_count + min_update_count; @@ -1003,11 +1018,13 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time) { mLastFetchUUID = imagep->getID(); } - if ((min_count-- <= 0) && (image_op_timer.getElapsedTimeF32() > max_time)) + + if ((min_count-- <= 0) || (image_op_timer.getElapsedTimeF32() > max_time)) { break; } } + return image_op_timer.getElapsedTimeF32(); } @@ -1030,7 +1047,7 @@ void LLViewerTextureList::decodeAllImages(F32 max_time) { LLTimer timer; if(gNoRender) return; - + // Update texture stats and priorities std::vector > image_list; for (image_priority_list_t::iterator iter = mImageList.begin(); @@ -1053,7 +1070,7 @@ void LLViewerTextureList::decodeAllImages(F32 max_time) addImageToList(imagep); } image_list.clear(); - + // Update fetch (decode) for (image_priority_list_t::iterator iter = mImageList.begin(); iter != mImageList.end(); ) @@ -1083,8 +1100,8 @@ void LLViewerTextureList::decodeAllImages(F32 max_time) max_time -= timer.getElapsedTimeF32(); max_time = llmax(max_time, .001f); F32 create_time = updateImagesCreateTextures(max_time); - - LL_DEBUGS("ViewerImages") << "decodeAllImages() took " << timer.getElapsedTimeF32() << " seconds. " + + LL_DEBUGS("ViewerImages") << "decodeAllImages() took " << timer.getElapsedTimeF32() << " seconds. " << " fetch_pending " << fetch_pending << " create_time " << create_time << LL_ENDL; @@ -1101,7 +1118,7 @@ BOOL LLViewerTextureList::createUploadFile(const std::string& filename, { LLImage::setLastError("Couldn't open the image to be uploaded."); return FALSE; - } + } if (!image->load(filename)) { LLImage::setLastError("Couldn't load the image to be uploaded."); @@ -1121,7 +1138,7 @@ BOOL LLViewerTextureList::createUploadFile(const std::string& filename, return FALSE; } // Convert to j2c (JPEG2000) and save the file locally - LLPointer compressedImage = convertToUploadFile(raw_image); + LLPointer compressedImage = convertToUploadFile(raw_image); if (compressedImage.isNull()) { LLImage::setLastError("Couldn't convert the image to jpeg2000."); @@ -1175,11 +1192,11 @@ LLPointer LLViewerTextureList::convertToUploadFile(LLPointerbiasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); LLPointer compressedImage = new LLImageJ2C(); compressedImage->setRate(0.f); - + if (gSavedSettings.getBOOL("LosslessJ2CUpload") && (raw_image->getWidth() * raw_image->getHeight() <= LL_IMAGE_REZ_LOSSLESS_CUTOFF * LL_IMAGE_REZ_LOSSLESS_CUTOFF)) compressedImage->setReversible(TRUE); - + /* if (gSavedSettings.getBOOL("Jpeg2000AdvancedCompression")) { @@ -1192,22 +1209,24 @@ LLPointer LLViewerTextureList::convertToUploadFile(LLPointer 2000) + U64 system_ram = (U64)BYTES_TO_MEGA_BYTES(gSysMemory.getPhysicalMemoryClamped()); + if(system_ram > 6000) + return 512; + else if (system_ram > 2000) return 128; else if (system_ram > 1000) return 64; @@ -1226,6 +1245,7 @@ S32 LLViewerTextureList::getMaxVideoRamSetting(bool get_recommended) } #endif S32 max_texmem; + S32 min_vr = getMinVideoRamSetting(); if (gGLManager.mVRAM != 0) { // Treat any card with < 32 MB (shudder) as having 32 MB @@ -1245,73 +1265,46 @@ S32 LLViewerTextureList::getMaxVideoRamSetting(bool get_recommended) LL_WARNS() << "VRAM amount not detected, defaulting to " << max_texmem << " MB" << LL_ENDL; } - S32 system_ram = (S32)BYTES_TO_MEGA_BYTES(gSysMemory.getPhysicalMemoryClamped()); // In MB + U64 system_ram = (U64)BYTES_TO_MEGA_BYTES(gSysMemory.getPhysicalMemoryClamped()); // In MB //LL_INFOS() << "*** DETECTED " << system_ram << " MB of system memory." << LL_ENDL; if (get_recommended) - max_texmem = llmin(max_texmem, (S32)(system_ram/2)); + { + if((U64)max_texmem > system_ram / 2) max_texmem = system_ram / 2; + } else - max_texmem = llmin(max_texmem, (S32)(system_ram)); - - max_texmem = llclamp(max_texmem, getMinVideoRamSetting(), MAX_VIDEO_RAM_IN_MEGA_BYTES); - + { + if((U64)max_texmem > system_ram) max_texmem = system_ram; + } + + if(max_texmem > MAX_VIDEO_RAM_IN_MEGA_BYTES) return MAX_VIDEO_RAM_IN_MEGA_BYTES; + if(max_texmem < min_vr) return (S32)min_vr; return max_texmem; } -const S32 VIDEO_CARD_FRAMEBUFFER_MEM = 12; // MB -const S32 MIN_MEM_FOR_NON_TEXTURE = 512 ; //MB -void LLViewerTextureList::updateMaxResidentTexMem(S32 mem) + +void LLViewerTextureList::updateMaxResidentTexMem(S32 unused) { // Initialize the image pipeline VRAM settings - S32 cur_mem = gSavedSettings.getS32("TextureMemory"); - F32 mem_multiplier = gSavedSettings.getF32("RenderTextureMemoryMultiple"); - S32 default_mem = getMaxVideoRamSetting(true); // recommended default - if (mem == 0) - { - mem = cur_mem > 0 ? cur_mem : default_mem; - } - else if (mem < 0) - { - mem = default_mem; - } + // gSavedSettings.getF32("RenderTextureMemoryMultiple"); was involved here - // limit the texture memory to a multiple of the default if we've found some cards to behave poorly otherwise - mem = llmin(mem, (S32) (mem_multiplier * (F32) default_mem)); - - mem = llclamp(mem, getMinVideoRamSetting(), getMaxVideoRamSetting()); - if (mem != cur_mem) - { - gSavedSettings.setS32("TextureMemory", mem); - return; //listener will re-enter this function - } + U64 video_ram = (U64)getMaxVideoRamSetting(0); - // TODO: set available resident texture mem based on use by other subsystems - // currently max(12MB, VRAM/4) assumed... - - S32 vb_mem = mem; - S32 fb_mem = llmax(VIDEO_CARD_FRAMEBUFFER_MEM, vb_mem/4); - mMaxResidentTexMemInMegaBytes = (vb_mem - fb_mem) ; //in MB - - mMaxTotalTextureMemInMegaBytes = mMaxResidentTexMemInMegaBytes * 2; - if (mMaxResidentTexMemInMegaBytes > 640) - { - mMaxTotalTextureMemInMegaBytes -= (mMaxResidentTexMemInMegaBytes >> 2); - } + // reserve 1/4 of the actual video ram for frame buffer + // this might be what's called "bound mem" + U64 quarter_video_ram = (video_ram >> 2); + mMaxResidentTexMemInMegaBytes = quarter_video_ram; - //system mem - S32 system_ram = (S32)BYTES_TO_MEGA_BYTES(gSysMemory.getPhysicalMemoryClamped()); // In MB + // This is what's displayed as "GL Mem" in the stats floater. + // Setting this low will show blurred textures and lead to destroying more textures than otherwise. + U64 three_quarter_video_ram = video_ram - quarter_video_ram; + mMaxTotalTextureMemInMegaBytes = three_quarter_video_ram; - //minimum memory reserved for non-texture use. - //if system_raw >= 1GB, reserve at least 512MB for non-texture use; - //otherwise reserve half of the system_ram for non-texture use. - S32 min_non_texture_mem = llmin(system_ram / 2, MIN_MEM_FOR_NON_TEXTURE) ; + // this is NOT limited to max 4095 anymore: + // U64 system_ram = (U64)BYTES_TO_MEGA_BYTES(gSysMemory.getPhysicalMemoryClamped()); - if (mMaxTotalTextureMemInMegaBytes > system_ram - min_non_texture_mem) - { - mMaxTotalTextureMemInMegaBytes = system_ram - min_non_texture_mem ; - } - - LL_INFOS() << "Total Video Memory set to: " << vb_mem << " MB" << LL_ENDL; - LL_INFOS() << "Available Texture Memory set to: " << (vb_mem - fb_mem) << " MB" << LL_ENDL; + // llinfos << "system_ram: " << system_ram << "MB" << llendl; + LL_INFOS() << "mMaxTotalTextureMemInMegaBytes: " << mMaxTotalTextureMemInMegaBytes << llendl; + LL_INFOS() << "mMaxResidentTexMemInMegaBytes: " << mMaxResidentTexMemInMegaBytes << llendl; } /////////////////////////////////////////////////////////////////////////////// @@ -1322,28 +1315,28 @@ void LLViewerTextureList::receiveImageHeader(LLMessageSystem *msg, void **user_d static LLCachedControl log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ; LLFastTimer t(FTM_PROCESS_IMAGES); - - // Receive image header, copy into image object and decompresses - // if this is a one-packet image. - + + // Receive image header, copy into image object and decompresses + // if this is a one-packet image. + LLUUID id; - + char ip_string[256]; u32_to_ip_string(msg->getSenderIP(),ip_string); - + U32 received_size ; if (msg->getReceiveCompressedSize()) { - received_size = msg->getReceiveCompressedSize() ; + received_size = msg->getReceiveCompressedSize() ; } else { - received_size = msg->getReceiveSize() ; + received_size = msg->getReceiveSize() ; } // Only used for statistics and texture console. gTextureList.sTextureBits += received_size * 8; gTextureList.sTexturePackets++; - + U8 codec; U16 packets; U32 totalbytes; @@ -1351,8 +1344,8 @@ void LLViewerTextureList::receiveImageHeader(LLMessageSystem *msg, void **user_d msg->getU8Fast(_PREHASH_ImageID, _PREHASH_Codec, codec); msg->getU16Fast(_PREHASH_ImageID, _PREHASH_Packets, packets); msg->getU32Fast(_PREHASH_ImageID, _PREHASH_Size, totalbytes); - - S32 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data); + + S32 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data); if (!data_size) { return; @@ -1365,11 +1358,11 @@ void LLViewerTextureList::receiveImageHeader(LLMessageSystem *msg, void **user_d << data_size << LL_ENDL; return; } - + // this buffer gets saved off in the packet list U8 *data = new U8[data_size]; msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size); - + LLViewerFetchedTexture *image = LLViewerTextureManager::getFetchedTexture(id, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); if (!image) { @@ -1395,16 +1388,16 @@ void LLViewerTextureList::receiveImagePacket(LLMessageSystem *msg, void **user_d static LLCachedControl log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ; LLFastTimer t(FTM_PROCESS_IMAGES); - + // Receives image packet, copy into image object, - // checks if all packets received, decompresses if so. - + // checks if all packets received, decompresses if so. + LLUUID id; U16 packet_num; - + char ip_string[256]; u32_to_ip_string(msg->getSenderIP(),ip_string); - + U32 received_size ; if (msg->getReceiveCompressedSize()) { @@ -1412,16 +1405,16 @@ void LLViewerTextureList::receiveImagePacket(LLMessageSystem *msg, void **user_d } else { - received_size = msg->getReceiveSize() ; + received_size = msg->getReceiveSize() ; } gTextureList.sTextureBits += received_size * 8; gTextureList.sTexturePackets++; - + //llprintline("Start decode, image header..."); msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id); msg->getU16Fast(_PREHASH_ImageID, _PREHASH_Packet, packet_num); - S32 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data); - + S32 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data); + if (!data_size) { return; @@ -1441,7 +1434,7 @@ void LLViewerTextureList::receiveImagePacket(LLMessageSystem *msg, void **user_d } U8 *data = new U8[data_size]; msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size); - + LLViewerFetchedTexture *image = LLViewerTextureManager::getFetchedTexture(id, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); if (!image) { @@ -1469,7 +1462,7 @@ void LLViewerTextureList::processImageNotInDatabase(LLMessageSystem *msg,void ** LLFastTimer t(FTM_PROCESS_IMAGES); LLUUID image_id; msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, image_id); - + LLViewerFetchedTexture* image = gTextureList.findImage( image_id ); if( image ) { @@ -1480,21 +1473,6 @@ void LLViewerTextureList::processImageNotInDatabase(LLMessageSystem *msg,void ** /////////////////////////////////////////////////////////////////////////////// -//static -const U32 SIXTEEN_MEG = 0x1000000; -S32 LLViewerTextureList::calcMaxTextureRAM() -{ - // Decide the maximum amount of RAM we should allow the user to allocate to texture cache - LLMemoryInfo memory_info; - U32 available_memory = memory_info.getPhysicalMemoryClamped(); - - clamp_rescale((F32)available_memory, - (F32)(SIXTEEN_MEG * 16), - (F32)U32_MAX, - (F32)(SIXTEEN_MEG * 4), - (F32)(U32_MAX >> 1)); - return available_memory; -} /////////////////////////////////////////////////////////////////////////////// @@ -1606,10 +1584,10 @@ LLUIImagePtr LLUIImageList::preloadUIImage(const std::string& name, const std::s return loadUIImageByName(name, filename, use_mips, scale_rect, clip_rect); } -//static +//static void LLUIImageList::onUIImageLoaded( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* user_data ) { - if(!success || !user_data) + if(!success || !user_data) { return; } @@ -1714,8 +1692,8 @@ bool LLUIImageList::initFromFile() parser.readXUI(root, images, base_file_path); // add components defined in current skin - std::string skin_update_path = gDirUtilp->getSkinDir() - + gDirUtilp->getDirDelimiter() + std::string skin_update_path = gDirUtilp->getSkinDir() + + gDirUtilp->getDirDelimiter() + "textures" + gDirUtilp->getDirDelimiter() + "textures.xml"; @@ -1727,8 +1705,8 @@ bool LLUIImageList::initFromFile() } // add components defined in user override of current skin - skin_update_path = gDirUtilp->getUserSkinDir() - + gDirUtilp->getDirDelimiter() + skin_update_path = gDirUtilp->getUserSkinDir() + + gDirUtilp->getDirDelimiter() + "textures" + gDirUtilp->getDirDelimiter() + "textures.xml"; @@ -1801,14 +1779,14 @@ bool LLUIImageList::initFromFile() std::vector paths; // path to current selected skin - paths.push_back(gDirUtilp->getSkinDir() - + gDirUtilp->getDirDelimiter() + paths.push_back(gDirUtilp->getSkinDir() + + gDirUtilp->getDirDelimiter() + "textures" + gDirUtilp->getDirDelimiter() + "textures.xml"); // path to user overrides on current skin - paths.push_back(gDirUtilp->getUserSkinDir() - + gDirUtilp->getDirDelimiter() + paths.push_back(gDirUtilp->getUserSkinDir() + + gDirUtilp->getDirDelimiter() + "textures" + gDirUtilp->getDirDelimiter() + "textures.xml"); @@ -1854,7 +1832,7 @@ bool LLUIImageList::initFromFile() // load high priority textures on first pass (to kick off decode) if (preload) { - if (pass == PASS_DECODE_LATER) + if (pass == PASS_DECODE_LATER) { child_nodep = child_nodep->getNextSibling(); continue; @@ -1876,9 +1854,9 @@ bool LLUIImageList::initFromFile() child_nodep->getAttributeS32("scale_right", scale_rect.mRight); child_nodep->getAttributeS32("scale_bottom", scale_rect.mBottom); child_nodep->getAttributeS32("scale_top", scale_rect.mTop); - + preloadUIImage(image_name, file_name, use_mip_maps, scale_rect, LLRectBase::null); - + child_nodep = child_nodep->getNextSibling(); } @@ -1889,5 +1867,3 @@ bool LLUIImageList::initFromFile() } return true; } - - diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h index 57d6bc91de..ab388638eb 100644 --- a/indra/newview/llviewertexturelist.h +++ b/indra/newview/llviewertexturelist.h @@ -71,7 +71,7 @@ class LLViewerTextureList static BOOL verifyUploadFile(const std::string& out_filename, const U8 codec); static LLPointer convertToUploadFile(LLPointer raw_image); static void processImageNotInDatabase( LLMessageSystem *msg, void **user_data ); - static S32 calcMaxTextureRAM(); + static void receiveImageHeader(LLMessageSystem *msg, void **user_data); static void receiveImagePacket(LLMessageSystem *msg, void **user_data); @@ -102,10 +102,10 @@ class LLViewerTextureList void setUpdateStats(BOOL b) { mUpdateStats = b; } S32 getMaxResidentTexMem() const { return mMaxResidentTexMemInMegaBytes; } - S32 getMaxTotalTextureMem() const { return mMaxTotalTextureMemInMegaBytes;} + S64 getMaxTotalTextureMem() const { return mMaxTotalTextureMemInMegaBytes;} S32 getNumImages() { return mImageList.size(); } - void updateMaxResidentTexMem(S32 mem); + void updateMaxResidentTexMem(S32 unused); void doPreloadImages(); void doPrefetchImages(); @@ -195,8 +195,8 @@ class LLViewerTextureList BOOL mInitialized ; BOOL mUpdateStats; - S32 mMaxResidentTexMemInMegaBytes; - S32 mMaxTotalTextureMemInMegaBytes; + U64 mMaxResidentTexMemInMegaBytes; + U64 mMaxTotalTextureMemInMegaBytes; LLFrameTimer mForceDecodeTimer; public: diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 4a896285ff..edda7d842a 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -313,7 +313,8 @@ class LLDebugText static const LLCachedControl slb_show_fps("SLBShowFPS"); if (slb_show_fps) { - addText(xpos+280, ypos+5, llformat("FPS %3.1f", LLViewerStats::getInstance()->mFPSStat.getMeanPerSec())); + // addText(xpos+280, ypos+5, llformat("FPS %3.1f", LLViewerStats::getInstance()->mFPSStat.getMeanPerSec())); + addText(xpos+200, ypos+5, llformat("FIS %10.6f", gFrameIntervalSeconds)); ypos += y_inc; } @@ -5964,7 +5965,7 @@ void LLPickInfo::fetchResults() mObjectID = objectp->mID; mObjectFace = (te_offset == NO_FACE) ? -1 : (S32)te_offset; - + mPosGlobal = gAgent.getPosGlobalFromAgent(v_intersection); diff --git a/indra/newview/skins/default/xui/de/notifications.xml b/indra/newview/skins/default/xui/de/notifications.xml index 5213514f79..18e9507e3c 100644 --- a/indra/newview/skins/default/xui/de/notifications.xml +++ b/indra/newview/skins/default/xui/de/notifications.xml @@ -70,17 +70,19 @@ Der Besitzer der Parzelle möchte Ihren Viewer anweisen die folgende [TYPE] URL zu laden:[URL]Sie können die korrespondierende Domäne oder In-Welt Objekt-Skript-Server erlauben oder ablehnen."Erlauben" und "Verweigern" gelten nur für die aktuelle Sitzung, während "Immer sperren" bzw. "Immer erlauben" dann immer gelten.
-