From 5611cb6d476540e6a1c654c1f9acdce2787b3505 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 23 Apr 2012 16:19:39 -0400 Subject: Okay, imported the core-http library and got it compiling suspiciously easily. The unit/integration tests don't work yet as I'm still battling cmake/autobuild as usual but first milestone passed. --- indra/CMakeLists.txt | 1 + indra/llcorehttp/CMakeLists.txt | 112 ++++++ indra/llcorehttp/_assert.h | 39 ++ indra/llcorehttp/_httplibcurl.cpp | 214 ++++++++++ indra/llcorehttp/_httplibcurl.h | 89 +++++ indra/llcorehttp/_httpoperation.cpp | 203 ++++++++++ indra/llcorehttp/_httpoperation.h | 164 ++++++++ indra/llcorehttp/_httpoprequest.cpp | 473 +++++++++++++++++++++++ indra/llcorehttp/_httpoprequest.h | 127 ++++++ indra/llcorehttp/_httppolicy.cpp | 76 ++++ indra/llcorehttp/_httppolicy.h | 68 ++++ indra/llcorehttp/_httpreplyqueue.cpp | 87 +++++ indra/llcorehttp/_httpreplyqueue.h | 106 +++++ indra/llcorehttp/_httprequestqueue.cpp | 132 +++++++ indra/llcorehttp/_httprequestqueue.h | 104 +++++ indra/llcorehttp/_httpservice.cpp | 209 ++++++++++ indra/llcorehttp/_httpservice.h | 162 ++++++++ indra/llcorehttp/_mutex.h | 55 +++ indra/llcorehttp/_refcounted.cpp | 39 ++ indra/llcorehttp/_refcounted.h | 141 +++++++ indra/llcorehttp/_thread.h | 106 +++++ indra/llcorehttp/bufferarray.cpp | 281 ++++++++++++++ indra/llcorehttp/bufferarray.h | 168 ++++++++ indra/llcorehttp/httpcommon.cpp | 76 ++++ indra/llcorehttp/httpcommon.h | 204 ++++++++++ indra/llcorehttp/httphandler.h | 88 +++++ indra/llcorehttp/httpheaders.cpp | 44 +++ indra/llcorehttp/httpheaders.h | 86 +++++ indra/llcorehttp/httpoptions.cpp | 50 +++ indra/llcorehttp/httpoptions.h | 80 ++++ indra/llcorehttp/httprequest.cpp | 295 ++++++++++++++ indra/llcorehttp/httprequest.h | 316 +++++++++++++++ indra/llcorehttp/httpresponse.cpp | 89 +++++ indra/llcorehttp/httpresponse.h | 137 +++++++ indra/llcorehttp/tests/all_test.cpp | 64 +++ indra/llcorehttp/tests/test_allocator.cpp | 178 +++++++++ indra/llcorehttp/tests/test_allocator.h | 47 +++ indra/llcorehttp/tests/test_bufferarray.hpp | 456 ++++++++++++++++++++++ indra/llcorehttp/tests/test_httpheaders.hpp | 108 ++++++ indra/llcorehttp/tests/test_httpoperation.hpp | 128 ++++++ indra/llcorehttp/tests/test_httprequest.hpp | 437 +++++++++++++++++++++ indra/llcorehttp/tests/test_httprequestqueue.hpp | 186 +++++++++ indra/llcorehttp/tests/test_httpstatus.hpp | 163 ++++++++ indra/llcorehttp/tests/test_refcounted.hpp | 156 ++++++++ 44 files changed, 6544 insertions(+) create mode 100644 indra/llcorehttp/CMakeLists.txt create mode 100644 indra/llcorehttp/_assert.h create mode 100644 indra/llcorehttp/_httplibcurl.cpp create mode 100644 indra/llcorehttp/_httplibcurl.h create mode 100644 indra/llcorehttp/_httpoperation.cpp create mode 100644 indra/llcorehttp/_httpoperation.h create mode 100644 indra/llcorehttp/_httpoprequest.cpp create mode 100644 indra/llcorehttp/_httpoprequest.h create mode 100644 indra/llcorehttp/_httppolicy.cpp create mode 100644 indra/llcorehttp/_httppolicy.h create mode 100644 indra/llcorehttp/_httpreplyqueue.cpp create mode 100644 indra/llcorehttp/_httpreplyqueue.h create mode 100644 indra/llcorehttp/_httprequestqueue.cpp create mode 100644 indra/llcorehttp/_httprequestqueue.h create mode 100644 indra/llcorehttp/_httpservice.cpp create mode 100644 indra/llcorehttp/_httpservice.h create mode 100644 indra/llcorehttp/_mutex.h create mode 100644 indra/llcorehttp/_refcounted.cpp create mode 100644 indra/llcorehttp/_refcounted.h create mode 100644 indra/llcorehttp/_thread.h create mode 100644 indra/llcorehttp/bufferarray.cpp create mode 100644 indra/llcorehttp/bufferarray.h create mode 100644 indra/llcorehttp/httpcommon.cpp create mode 100644 indra/llcorehttp/httpcommon.h create mode 100644 indra/llcorehttp/httphandler.h create mode 100644 indra/llcorehttp/httpheaders.cpp create mode 100644 indra/llcorehttp/httpheaders.h create mode 100644 indra/llcorehttp/httpoptions.cpp create mode 100644 indra/llcorehttp/httpoptions.h create mode 100644 indra/llcorehttp/httprequest.cpp create mode 100644 indra/llcorehttp/httprequest.h create mode 100644 indra/llcorehttp/httpresponse.cpp create mode 100644 indra/llcorehttp/httpresponse.h create mode 100644 indra/llcorehttp/tests/all_test.cpp create mode 100644 indra/llcorehttp/tests/test_allocator.cpp create mode 100644 indra/llcorehttp/tests/test_allocator.h create mode 100644 indra/llcorehttp/tests/test_bufferarray.hpp create mode 100644 indra/llcorehttp/tests/test_httpheaders.hpp create mode 100644 indra/llcorehttp/tests/test_httpoperation.hpp create mode 100644 indra/llcorehttp/tests/test_httprequest.hpp create mode 100644 indra/llcorehttp/tests/test_httprequestqueue.hpp create mode 100644 indra/llcorehttp/tests/test_httpstatus.hpp create mode 100644 indra/llcorehttp/tests/test_refcounted.hpp diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 4b1bf49d07..031ffb26db 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -43,6 +43,7 @@ add_subdirectory(cmake) add_subdirectory(${LIBS_OPEN_PREFIX}llaudio) add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter) add_subdirectory(${LIBS_OPEN_PREFIX}llcommon) +add_subdirectory(${LIBS_OPEN_PREFIX}llcorehttp) add_subdirectory(${LIBS_OPEN_PREFIX}llimage) add_subdirectory(${LIBS_OPEN_PREFIX}llkdu) add_subdirectory(${LIBS_OPEN_PREFIX}llimagej2coj) diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt new file mode 100644 index 0000000000..8d21007ca0 --- /dev/null +++ b/indra/llcorehttp/CMakeLists.txt @@ -0,0 +1,112 @@ +# -*- cmake -*- + +project(llcorehttp) + +include(00-Common) +include(LLCoreHttp) +include(GoogleMock) +include(LLAddBuildTest) +include(LLCommon) +include(Tut) + +include_directories (${CMAKE_CURRENT_SOURCE_DIR}) + +include_directories( + ${LLCOMMON_INCLUDE_DIRS} + ${LLCOREHTTP_INCLUDE_DIRS} + ) + +set(llcorehttp_SOURCE_FILES + bufferarray.cpp + httpcommon.cpp + httprequest.cpp + httpresponse.cpp + httpoptions.cpp + httpheaders.cpp + _httprequestqueue.cpp + _httpoperation.cpp + _httpoprequest.cpp + _httpreplyqueue.cpp + _httppolicy.cpp + _httplibcurl.cpp + _httpservice.cpp + _refcounted.cpp + ) + +set(llcorehttp_HEADER_FILES + CMakeLists.txt + + bufferarray.h + httpcommon.h + httphandler.h + httpheaders.h + httpoptions.h + httprequest.h + httpresponse.h + _httpoperation.h + _httpoprequest.h + _httprequestqueue.h + _httpreplyqueue.h + _httpservice.h + _httppolicy.h + _httplibcurl.h + _assert.h + _refcounted.h + _mutex.h + _thread.h + ) + +set_source_files_properties(${llcorehttp_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND llcorehttp_SOURCE_FILES ${llcorehttp_HEADER_FILES}) + +add_library (llcorehttp ${llcorehttp_SOURCE_FILES}) +target_link_libraries( + llcorehttp + ${CURL_LIBRARIES} + ${CARES_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} + ) + +# tests +#if (LL_TESTS) +if (LL_TESTS AND 0) + SET(llcorehttp_TEST_SOURCE_FILES + test_allocator.cpp + ) + + set(llcorehttp_TEST_HEADER_FILS + test_httpstatus.hpp + test_refcounted.hpp + test_httpoperation.hpp + test_httprequest.hpp + test_httprequestqueue.hpp + test_httpheaders.hpp + test_bufferarray.hpp + ) + + set_source_files_properties(${llcorehttp_TEST_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + + list(APPEND llcorehttp_TEST_SOURCE_FILES ${llcorehttp_TEST_HEADER_FILES}) + + # LL_ADD_PROJECT_UNIT_TESTS(llcorehttp "${llcorehttp_TEST_SOURCE_FILES}") + + # set(TEST_DEBUG on) + set(test_libs + ${LLCOREHTTP_LIBRARIES} + ${WINDOWS_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${GOOGLEMOCK_LIBRARIES} + ) + + LL_ADD_INTEGRATION_TEST(all + "${llcorehttp_TEST_SOURCE_FILES}" + "${test_libs}" + ) + +#endif (LL_TESTS) +endif (LL_TESTS AND 0) + diff --git a/indra/llcorehttp/_assert.h b/indra/llcorehttp/_assert.h new file mode 100644 index 0000000000..054f23ef32 --- /dev/null +++ b/indra/llcorehttp/_assert.h @@ -0,0 +1,39 @@ +/** + * @file _assert + * @brief assert abstraction + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCOREINT__ASSERT_H_ +#define LLCOREINT__ASSERT_H_ + +#ifdef DEBUG_ASSERT +#include +#define LLINT_ASSERT(x) assert(x) +#else +#define LLINT_ASSERT(x) +#endif + +#endif // LLCOREINT__ASSERT_H_ + + diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp new file mode 100644 index 0000000000..15be977adf --- /dev/null +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -0,0 +1,214 @@ +/** + * @file _httplibcurl.cpp + * @brief Internal definitions of the Http libcurl thread + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httplibcurl.h" + +#include "httpheaders.h" + +#include "_httpoprequest.h" +#include "_httpservice.h" + + +namespace LLCore +{ + + +HttpLibcurl::HttpLibcurl(HttpService * service) + : mService(service) +{ + mMultiHandles[0] = curl_multi_init(); +} + + +HttpLibcurl::~HttpLibcurl() +{ + // *FIXME: need to cancel requests in this class, not in op class. + while (! mActiveOps.empty()) + { + active_set_t::iterator item(mActiveOps.begin()); + + (*item)->cancel(); + (*item)->release(); + } + + if (mMultiHandles[0]) + { + // *FIXME: Do some multi cleanup here first + + + curl_multi_cleanup(mMultiHandles[0]); + mMultiHandles[0] = NULL; + } + + mService = NULL; +} + + +void HttpLibcurl::init() +{} + + +void HttpLibcurl::term() +{} + + +void HttpLibcurl::processTransport() +{ + if (mMultiHandles[0]) + { + // Give libcurl some cycles to do I/O & callbacks + int running(0); + CURLMcode status(CURLM_CALL_MULTI_PERFORM); + do + { + running = 0; + status = curl_multi_perform(mMultiHandles[0], &running); + } + while (0 != running && CURLM_CALL_MULTI_PERFORM == status); + + // Run completion on anything done + CURLMsg * msg(NULL); + int msgs_in_queue(0); + while ((msg = curl_multi_info_read(mMultiHandles[0], &msgs_in_queue))) + { + if (CURLMSG_DONE == msg->msg) + { + CURL * handle(msg->easy_handle); + CURLcode result(msg->data.result); + + completeRequest(mMultiHandles[0], handle, result); + handle = NULL; // No longer valid on return + } + else if (CURLMSG_NONE == msg->msg) + { + // Ignore this... it shouldn't mean anything. + ; + } + else + { + // *FIXME: Issue a logging event for this. + ; + } + msgs_in_queue = 0; + } + } +} + + +void HttpLibcurl::addOp(HttpOpRequest * op) +{ + // Create standard handle + if (! op->prepareForGet(mService)) + { + // Couldn't issue request, fail with notification + // *FIXME: Need failure path + return; + } + + // Make the request live + curl_multi_add_handle(mMultiHandles[0], op->mCurlHandle); + op->mCurlActive = true; + + // On success, make operation active + mActiveOps.insert(op); +} + + +void HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status) +{ + HttpOpRequest * op(NULL); + curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); + // *FIXME: check the pointer + + if (handle != op->mCurlHandle || ! op->mCurlActive) + { + // *FIXME: This is a sanity check that needs validation/termination. + ; + } + + active_set_t::iterator it(mActiveOps.find(op)); + if (mActiveOps.end() == it) + { + // *FIXME: Fatal condition. This must be here. + ; + } + else + { + mActiveOps.erase(it); + } + + // Deactivate request + op->mCurlActive = false; + + // Set final status of request + if (op->mStatus) + { + // Only set if it hasn't failed by other mechanisms yet + op->mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, status); + } + if (op->mStatus) + { + int http_status(200); + + curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status); + op->mReplyStatus = http_status; + } + + // Detach from multi and recycle handle + curl_multi_remove_handle(multi_handle, handle); + curl_easy_cleanup(handle); + op->mCurlHandle = NULL; + + // Deliver to reply queue and release + op->stageFromActive(mService); + op->release(); +} + + +int HttpLibcurl::activeCount() const +{ + return mActiveOps.size(); +} + + +// --------------------------------------- +// Free functions +// --------------------------------------- + + +struct curl_slist * append_headers_to_slist(const HttpHeaders * headers, struct curl_slist * slist) +{ + for (HttpHeaders::container_t::const_iterator it(headers->mHeaders.begin()); + headers->mHeaders.end() != it; + ++it) + { + slist = curl_slist_append(slist, (*it).c_str()); + } + return slist; +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h new file mode 100644 index 0000000000..5ba244cee4 --- /dev/null +++ b/indra/llcorehttp/_httplibcurl.h @@ -0,0 +1,89 @@ +/** + * @file _httplibcurl.h + * @brief Declarations for internal class providing libcurl transport. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_LIBCURL_H_ +#define _LLCORE_HTTP_LIBCURL_H_ + + +#include +#include + +#include + + +namespace LLCore +{ + + +class HttpService; +class HttpPolicy; +class HttpOpRequest; +class HttpHeaders; + + +class HttpLibcurl +{ +public: + HttpLibcurl(HttpService * service); + virtual ~HttpLibcurl(); + +private: + HttpLibcurl(const HttpLibcurl &); // Not defined + void operator=(const HttpLibcurl &); // Not defined + +public: + static void init(); + static void term(); + + void processTransport(); + void addOp(HttpOpRequest * op); + + int activeCount() const; + +protected: + void completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status); + +protected: + typedef std::set active_set_t; + +protected: + HttpService * mService; + active_set_t mActiveOps; + CURLM * mMultiHandles[1]; +}; // end class HttpLibcurl + + +// --------------------------------------- +// Free functions +// --------------------------------------- + + +curl_slist * append_headers_to_slist(const HttpHeaders *, curl_slist * slist); + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_LIBCURL_H_ diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp new file mode 100644 index 0000000000..17c65b0379 --- /dev/null +++ b/indra/llcorehttp/_httpoperation.cpp @@ -0,0 +1,203 @@ +/** + * @file _httpoperation.cpp + * @brief Definitions for internal classes based on HttpOperation + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpoperation.h" + +#include "httphandler.h" +#include "httpresponse.h" +#include "httprequest.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" + + +namespace LLCore +{ + + +// ================================== +// HttpOperation +// ================================== + + +HttpOperation::HttpOperation() + : LLCoreInt::RefCounted(true), + mReplyQueue(NULL), + mLibraryHandler(NULL), + mUserHandler(NULL), + mReqPolicy(HttpRequest::DEFAULT_POLICY_ID), + mReqPriority(0.0f) +{ +} + + +HttpOperation::~HttpOperation() +{ + setHandlers(NULL, NULL, NULL); +} + + +void HttpOperation::setHandlers(HttpReplyQueue * reply_queue, + HttpHandler * lib_handler, + HttpHandler * user_handler) +{ + if (reply_queue != mReplyQueue) + { + if (mReplyQueue) + { + mReplyQueue->release(); + } + + if (reply_queue) + { + reply_queue->addRef(); + } + + mReplyQueue = reply_queue; + } + + // Not refcounted + mLibraryHandler = lib_handler; + + // Not refcounted + mUserHandler = user_handler; +} + + + +void HttpOperation::stageFromRequest(HttpService *) +{ + // *FIXME: Message this a better way later. + // Default implementation should never be called. This + // indicates an operation making a transition that isn't + // defined. + LLINT_ASSERT(false); +} + + +void HttpOperation::stageFromReady(HttpService *) +{ + // *FIXME: Message this a better way later. + // Default implementation should never be called. This + // indicates an operation making a transition that isn't + // defined. + LLINT_ASSERT(false); +} + + +void HttpOperation::stageFromActive(HttpService *) +{ + // *FIXME: Message this a better way later. + // Default implementation should never be called. This + // indicates an operation making a transition that isn't + // defined. + LLINT_ASSERT(false); +} + + +void HttpOperation::visitNotifier(HttpRequest *) +{ + if (mLibraryHandler) + { + HttpResponse * response = new HttpResponse(); + + mLibraryHandler->onCompleted(static_cast(this), response); + + response->release(); + } +} + + +HttpStatus HttpOperation::cancel() +{ + HttpStatus status; + + return status; +} + + +void HttpOperation::addAsReply() +{ + if (mReplyQueue && mLibraryHandler) + { + addRef(); + mReplyQueue->addOp(this); + } +} + + +// ================================== +// HttpOpStop +// ================================== + + +HttpOpStop::HttpOpStop() + : HttpOperation() +{} + + +HttpOpStop::~HttpOpStop() +{} + + +void HttpOpStop::stageFromRequest(HttpService * service) +{ + // Do operations + service->stopRequested(); + + // Prepare response if needed + addAsReply(); +} + + +// ================================== +// HttpOpNull +// ================================== + + +HttpOpNull::HttpOpNull() + : HttpOperation() +{} + + +HttpOpNull::~HttpOpNull() +{} + + +void HttpOpNull::stageFromRequest(HttpService * service) +{ + // Perform op + // Nothing to perform. This doesn't fall into the libcurl + // ready/active queues, it just bounces over to the reply + // queue directly. + + // Prepare response if needed + addAsReply(); +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h new file mode 100644 index 0000000000..d04961c47b --- /dev/null +++ b/indra/llcorehttp/_httpoperation.h @@ -0,0 +1,164 @@ +/** + * @file _httpoperation.h + * @brief Internal declarations for HttpOperation and sub-classes + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_OPERATION_H_ +#define _LLCORE_HTTP_OPERATION_H_ + + +#include "httpcommon.h" + +#include "_refcounted.h" + + +namespace LLCore +{ + +class HttpReplyQueue; +class HttpHandler; +class HttpService; +class HttpRequest; + +/// HttpOperation is the base class for all request/reply +/// pairs. +/// +class HttpOperation : public LLCoreInt::RefCounted +{ +public: + HttpOperation(); + virtual ~HttpOperation(); + +private: + HttpOperation(const HttpOperation &); // Not defined + void operator=(const HttpOperation &); // Not defined + +public: + void setHandlers(HttpReplyQueue * reply_queue, + HttpHandler * lib_handler, + HttpHandler * user_handler); + + HttpHandler * getUserHandler() const + { + return mUserHandler; + } + + virtual void stageFromRequest(HttpService *); + virtual void stageFromReady(HttpService *); + virtual void stageFromActive(HttpService *); + + virtual void visitNotifier(HttpRequest *); + + virtual HttpStatus cancel(); + +protected: + void addAsReply(); + +protected: + HttpReplyQueue * mReplyQueue; // Have refcount + HttpHandler * mLibraryHandler; // Have refcount + HttpHandler * mUserHandler; // Have refcount + +public: + unsigned int mReqPolicy; + float mReqPriority; + +}; // end class HttpOperation + + +/// HttpOpCancel requests that a previously issued request +/// be canceled, if possible. Requests that have been made +/// active and are available for sending on the wire cannot +/// be canceled. + +class HttpOpCancel : public HttpOperation +{ +public: + HttpOpCancel(); + virtual ~HttpOpCancel(); + +private: + HttpOpCancel(const HttpOpCancel &); // Not defined + void operator=(const HttpOpCancel &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + virtual void stageFromReady(HttpService *); + virtual void stageFromActive(HttpService *); + +public: + HttpHandle mHandle; +}; // end class HttpOpCancel + + +/// HttpOpStop requests the servicing thread to shutdown +/// operations, cease pulling requests from the request +/// queue and release shared resources (particularly +/// those shared via reference count). The servicing +/// thread will then exit. The underlying thread object +/// remains so that another thread can join on the +/// servicing thread prior to final cleanup. The +/// request *does* generate a reply on the response +/// queue, if requested. + +class HttpOpStop : public HttpOperation +{ +public: + HttpOpStop(); + virtual ~HttpOpStop(); + +private: + HttpOpStop(const HttpOpStop &); // Not defined + void operator=(const HttpOpStop &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + +}; // end class HttpOpStop + + +/// HttpOpNull is a do-nothing operation used for testing via +/// a basic loopback pattern. It's executed immediately by +/// the servicing thread which bounces a reply back to the +/// caller without any further delay. + +class HttpOpNull : public HttpOperation +{ +public: + HttpOpNull(); + virtual ~HttpOpNull(); + +private: + HttpOpNull(const HttpOpNull &); // Not defined + void operator=(const HttpOpNull &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + +}; // end class HttpOpNull + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_OPERATION_H_ + diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp new file mode 100644 index 0000000000..d71ace5d57 --- /dev/null +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -0,0 +1,473 @@ +/** + * @file _httpoprequest.cpp + * @brief Definitions for internal class HttpOpRequest + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpoprequest.h" + +#include +#include + +#include "httpcommon.h" +#include "httphandler.h" +#include "httpresponse.h" +#include "bufferarray.h" +#include "httpheaders.h" +#include "httpoptions.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" +#include "_httppolicy.h" +#include "_httplibcurl.h" + + +namespace +{ + +// Attempts to parse a 'Content-Range:' header. Caller must already +// have verified that the header tag is present. The 'buffer' argument +// will be processed by strtok_r calls which will modify the buffer. +// +// @return -1 if invalid and response should be dropped, 0 if valid an +// correct, 1 if couldn't be parsed. If 0, the first, last, +// and length arguments are also written. 'length' may be +// 0 if the length wasn't available to the server. +// +int parse_content_range_header(char * buffer, + unsigned int * first, + unsigned int * last, + unsigned int * length); + +#if defined(WIN32) + +// Not available on windows where the legacy strtok interface +// is thread-safe. +char *strtok_r(char *str, const char *delim, char **saveptr); + +#endif + +} + + +namespace LLCore +{ + + +// ================================== +// HttpOpRequest +// ================================== + + +HttpOpRequest::HttpOpRequest() + : HttpOperation(), + mProcFlags(0U), + mReqMethod(HOR_GET), + mReqBody(NULL), + mReqOffset(0), + mReqLen(0), + mReqHeaders(NULL), + mReqOptions(NULL), + mCurlActive(false), + mCurlHandle(NULL), + mCurlHeaders(NULL), + mCurlService(NULL), + mReplyStatus(200), + mReplyBody(NULL), + mReplyOffset(0), + mReplyLen(0), + mReplyHeaders(NULL) +{} + + +HttpOpRequest::~HttpOpRequest() +{ + if (mCurlHandle) + { + curl_easy_cleanup(mCurlHandle); + mCurlHandle = NULL; + } + + if (mCurlHeaders) + { + curl_slist_free_all(mCurlHeaders); + mCurlHeaders = NULL; + } + + mCurlService = NULL; + + if (mReqBody) + { + mReqBody->release(); + mReqBody = NULL; + } + + if (mReqHeaders) + { + curl_slist_free_all(mReqHeaders); + mReqHeaders = NULL; + } + + if (mReqOptions) + { + mReqOptions->release(); + mReqOptions = NULL; + } + + if (mReplyBody) + { + mReplyBody->release(); + mReplyBody = NULL; + } + + if (mReplyHeaders) + { + mReplyHeaders->release(); + mReplyHeaders = NULL; + } +} + + +void HttpOpRequest::stageFromRequest(HttpService * service) +{ + addRef(); + service->getPolicy()->addOp(this); // transfers refcount +} + + +void HttpOpRequest::stageFromReady(HttpService * service) +{ + addRef(); + service->getTransport()->addOp(this); // transfers refcount +} + + +void HttpOpRequest::stageFromActive(HttpService * service) +{ + if (mReplyLen) + { + // If non-zero, we received and processed a Content-Range + // header with the response. Verify that what it says + // is consistent with the received data. + if (mReplyLen != mReplyBody->size()) + { + // Not as expected, fail the request + mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR); + } + } + + if (mReqHeaders) + { + // We take these headers out of the request now as they were + // allocated originally in this thread and the notifier doesn't + // need them. This eliminates one source of heap moving across + // threads. + + curl_slist_free_all(mReqHeaders); + mReqHeaders = NULL; + } + addAsReply(); +} + + +void HttpOpRequest::visitNotifier(HttpRequest * request) +{ + if (mLibraryHandler) + { + HttpResponse * response = new HttpResponse(); + + // *FIXME: add http status, offset, length + response->setStatus(mStatus); + response->setReplyStatus(mReplyStatus); + response->setBody(mReplyBody); + response->setHeaders(mReplyHeaders); + mLibraryHandler->onCompleted(static_cast(this), response); + + response->release(); + } +} + + +HttpStatus HttpOpRequest::cancel() +{ + mStatus = HttpStatus(HttpStatus::LLCORE, HE_OP_CANCELED); + + addAsReply(); + + return HttpStatus(); +} + + +HttpStatus HttpOpRequest::setupGetByteRange(unsigned int policy_id, + float priority, + const std::string & url, + size_t offset, + size_t len, + HttpOptions * options, + HttpHeaders * headers) +{ + HttpStatus status; + + mProcFlags = 0; + mReqPolicy = policy_id; + mReqPriority = priority; + mReqMethod = HOR_GET; + mReqURL = url; + mReqOffset = offset; + mReqLen = len; + if (offset || len) + { + mProcFlags |= PF_SCAN_RANGE_HEADER; + } + if (headers && ! mReqHeaders) + { + mReqHeaders = append_headers_to_slist(headers, mReqHeaders); + } + if (options && ! mReqOptions) + { + mReqOptions = new HttpOptions(*options); + } + + return status; +} + + +HttpStatus HttpOpRequest::prepareForGet(HttpService * service) +{ + // *FIXME: better error handling later + HttpStatus status; + + mCurlHandle = curl_easy_init(); + curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str()); + curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this); + curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); + // curl_easy_setopt(handle, CURLOPT_PROXY, ""); + + mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); + curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); + curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders); + curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback); + curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, mCurlHandle); + + if (mReqOffset || mReqLen) + { + static const char * fmt1("Range: bytes=%d-%d"); + static const char * fmt2("Range: bytes=%d-"); + + char range_line[64]; + +#if defined(WIN32) + _snprintf_s(range_line, sizeof(range_line), sizeof(range_line) - 1, + (mReqLen ? fmt1 : fmt2), + mReqOffset, mReqOffset + mReqLen - 1); +#else + snprintf(range_line, sizeof(range_line), + (mReqLen ? fmt1 : fmt2), + mReqOffset, mReqOffset + mReqLen - 1); +#endif // defined(WIN32) + range_line[sizeof(range_line) - 1] = '\0'; + mCurlHeaders = curl_slist_append(mCurlHeaders, range_line); + } + + if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS)) + { + curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback); + curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, mCurlHandle); + } + + if (status) + { + mCurlService = service; + } + return status; +} + + +size_t HttpOpRequest::writeCallback(void * data, size_t size, size_t nmemb, void * userdata) +{ + CURL * handle(static_cast(userdata)); + HttpOpRequest * op(NULL); + curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); + // *FIXME: check the pointer + + if (! op->mReplyBody) + { + op->mReplyBody = new BufferArray(); + } + const size_t req_size(size * nmemb); + char * lump(op->mReplyBody->appendBufferAlloc(req_size)); + memcpy(lump, data, req_size); + + return req_size; +} + + +size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, void * userdata) +{ + static const char status_line[] = "HTTP/"; + static const size_t status_line_len = sizeof(status_line) - 1; + + static const char con_ran_line[] = "content-range:"; + static const size_t con_ran_line_len = sizeof(con_ran_line) - 1; + + CURL * handle(static_cast(userdata)); + HttpOpRequest * op(NULL); + curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); + // *FIXME: check the pointer + + const size_t hdr_size(size * nmemb); + const char * hdr_data(static_cast(data)); // Not null terminated + + if (hdr_size >= status_line_len && ! strncmp(status_line, hdr_data, status_line_len)) + { + // One of possibly several status lines. Reset what we know and start over + op->mReplyOffset = 0; + op->mReplyLen = 0; + op->mStatus = HttpStatus(); + } + else if (op->mProcFlags & PF_SCAN_RANGE_HEADER) + { + char hdr_buffer[128]; + size_t frag_size((std::min)(hdr_size, sizeof(hdr_buffer) - 1)); + + memcpy(hdr_buffer, hdr_data, frag_size); + hdr_buffer[frag_size] = '\0'; +#if defined(WIN32) + if (! _strnicmp(hdr_buffer, con_ran_line, (std::min)(frag_size, con_ran_line_len))) +#else + if (! strncasecmp(hdr_buffer, con_ran_line, (std::min)(frag_size, con_ran_line_len))) +#endif + { + unsigned int first(0), last(0), length(0); + int status; + + if (! (status = parse_content_range_header(hdr_buffer, &first, &last, &length))) + { + // Success, record the fragment position + op->mReplyOffset = first; + op->mReplyLen = last - first + 1; + } + else if (-1 == status) + { + // Response is badly formed and shouldn't be accepted + op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR); + } + else + { + // Ignore the unparsable. + // *FIXME: Maybe issue a warning into the log here + ; + } + } + } + + if (op->mProcFlags & PF_SAVE_HEADERS) + { + // Save headers in response + ; + + } + + return hdr_size; +} + +} // end namespace LLCore + + +// ======================================= +// Anonymous Namespace +// ======================================= + +namespace +{ + +int parse_content_range_header(char * buffer, + unsigned int * first, + unsigned int * last, + unsigned int * length) +{ + char * tok_state(NULL), * tok; + bool match(true); + + if (! strtok_r(buffer, ": \t", &tok_state)) + match = false; + if (match && (tok = strtok_r(NULL, " \t", &tok_state))) +#if defined(WIN32) + match = 0 == _stricmp("bytes", tok); +#else + match = 0 == strcasecmp("bytes", tok); +#endif + if (match && ! (tok = strtok_r(NULL, " \t", &tok_state))) + match = false; + if (match) + { + unsigned int lcl_first(0), lcl_last(0), lcl_len(0); + +#if defined(WIN32) + if (3 == sscanf_s(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len)) +#else + if (3 == sscanf(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len)) +#endif + { + if (lcl_first > lcl_last || lcl_last >= lcl_len) + return -1; + *first = lcl_first; + *last = lcl_last; + *length = lcl_len; + return 0; + } +#if defined(WIN32) + if (2 == sscanf_s(tok, "%u-%u/*", &lcl_first, &lcl_last)) +#else + if (2 == sscanf(tok, "%u-%u/*", &lcl_first, &lcl_last)) +#endif + { + if (lcl_first > lcl_last) + return -1; + *first = lcl_first; + *last = lcl_last; + *length = 0; + return 0; + } + } + + // Header is there but badly/unexpectedly formed, try to ignore it. + return 1; +} + +#if defined(WIN32) + +char *strtok_r(char *str, const char *delim, char ** savestate) +{ + return strtok_s(str, delim, savestate); +} + +#endif + +} // end anonymous namespace + + diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h new file mode 100644 index 0000000000..e912851aed --- /dev/null +++ b/indra/llcorehttp/_httpoprequest.h @@ -0,0 +1,127 @@ +/** + * @file _httpoprequest.h + * @brief Internal declarations for the HttpOpRequest subclass + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_OPREQUEST_H_ +#define _LLCORE_HTTP_OPREQUEST_H_ + + +#include "httpcommon.h" + +#include + +#include "_httpoperation.h" +#include "_refcounted.h" + + +namespace LLCore +{ + + +class BufferArray; +class HttpHeaders; +class HttpOptions; + + +/// HttpOpRequest requests a supported HTTP method invocation with +/// option and header overrides. + +class HttpOpRequest : public HttpOperation +{ +public: + HttpOpRequest(); + virtual ~HttpOpRequest(); + +private: + HttpOpRequest(const HttpOpRequest &); // Not defined + void operator=(const HttpOpRequest &); // Not defined + +public: + enum EMethod + { + HOR_GET, + HOR_POST, + HOR_PUT + }; + + virtual void stageFromRequest(HttpService *); + virtual void stageFromReady(HttpService *); + virtual void stageFromActive(HttpService *); + + virtual void visitNotifier(HttpRequest * request); + +public: + // Setup Methods + HttpStatus setupGetByteRange(unsigned int policy_id, + float priority, + const std::string & url, + size_t offset, + size_t len, + HttpOptions * options, + HttpHeaders * headers); + + HttpStatus prepareForGet(HttpService * service); + + virtual HttpStatus cancel(); + +protected: + static size_t writeCallback(void * data, size_t size, size_t nmemb, void * userdata); + static size_t headerCallback(void * data, size_t size, size_t nmemb, void * userdata); + +protected: + unsigned int mProcFlags; + static const unsigned int PF_SCAN_RANGE_HEADER = 0x00000001U; + static const unsigned int PF_SAVE_HEADERS = 0x00000002U; + +public: + // Request data + EMethod mReqMethod; + std::string mReqURL; + BufferArray * mReqBody; + off_t mReqOffset; + size_t mReqLen; + curl_slist * mReqHeaders; + HttpOptions * mReqOptions; + + // Transport data + bool mCurlActive; + CURL * mCurlHandle; + curl_slist * mCurlHeaders; + HttpService * mCurlService; + + // Result data + HttpStatus mStatus; + int mReplyStatus; + BufferArray * mReplyBody; + off_t mReplyOffset; + size_t mReplyLen; + HttpHeaders * mReplyHeaders; +}; // end class HttpOpRequest + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_OPREQUEST_H_ + diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp new file mode 100644 index 0000000000..d965a6cf3a --- /dev/null +++ b/indra/llcorehttp/_httppolicy.cpp @@ -0,0 +1,76 @@ +/** + * @file _httppolicy.cpp + * @brief Internal definitions of the Http policy thread + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httppolicy.h" + +#include "_httpoprequest.h" +#include "_httpservice.h" + + +namespace LLCore +{ + + +HttpPolicy::HttpPolicy(HttpService * service) + : mService(service) +{} + + +HttpPolicy::~HttpPolicy() +{ + for (ready_queue_t::reverse_iterator i(mReadyQueue.rbegin()); + mReadyQueue.rend() != i;) + { + ready_queue_t::reverse_iterator cur(i++); + + (*cur)->cancel(); + (*cur)->release(); + } + + mService = NULL; +} + + +void HttpPolicy::addOp(HttpOpRequest * op) +{ + mReadyQueue.push_back(op); +} + + +void HttpPolicy::processReadyQueue() +{ + while (! mReadyQueue.empty()) + { + HttpOpRequest * op(mReadyQueue.front()); + mReadyQueue.erase(mReadyQueue.begin()); + + op->stageFromReady(mService); + op->release(); + } +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h new file mode 100644 index 0000000000..28aea27f38 --- /dev/null +++ b/indra/llcorehttp/_httppolicy.h @@ -0,0 +1,68 @@ +/** + * @file _httppolicy.h + * @brief Declarations for internal class enforcing policy decisions. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_POLICY_H_ +#define _LLCORE_HTTP_POLICY_H_ + + +#include + + +namespace LLCore +{ + + +class HttpService; +class HttpOpRequest; + + +class HttpPolicy +{ +public: + HttpPolicy(HttpService *); + virtual ~HttpPolicy(); + +private: + HttpPolicy(const HttpPolicy &); // Not defined + void operator=(const HttpPolicy &); // Not defined + +public: + void processReadyQueue(); + + void addOp(HttpOpRequest *); + +protected: + typedef std::vector ready_queue_t; + +protected: + HttpService * mService; // Naked pointer, not refcounted + ready_queue_t mReadyQueue; + +}; // end class HttpPolicy + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_POLICY_H_ diff --git a/indra/llcorehttp/_httpreplyqueue.cpp b/indra/llcorehttp/_httpreplyqueue.cpp new file mode 100644 index 0000000000..a354ed7e10 --- /dev/null +++ b/indra/llcorehttp/_httpreplyqueue.cpp @@ -0,0 +1,87 @@ +/** + * @file _httpreplyqueue.cpp + * @brief Internal definitions for the operation reply queue + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpreplyqueue.h" + + +#include "_mutex.h" +#include "_thread.h" +#include "_httpoperation.h" + +using namespace LLCoreInt; + + +namespace LLCore +{ + + +HttpReplyQueue::HttpReplyQueue() + : RefCounted(true) +{ +} + + +HttpReplyQueue::~HttpReplyQueue() +{ + while (! mQueue.empty()) + { + HttpOperation * op = mQueue.back(); + mQueue.pop_back(); + op->release(); + } +} + + +void HttpReplyQueue::addOp(HttpOperation * op) +{ + { + HttpScopedLock lock(mQueueMutex); + + mQueue.push_back(op); + } + mQueueCV.notify_all(); +} + + +HttpOperation * HttpReplyQueue::fetchOp() +{ + HttpOperation * result(NULL); + + { + HttpScopedLock lock(mQueueMutex); + + if (mQueue.empty()) + return NULL; + + result = mQueue.front(); + mQueue.erase(mQueue.begin()); + } + + // Caller also acquires the reference count + return result; +} + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httpreplyqueue.h b/indra/llcorehttp/_httpreplyqueue.h new file mode 100644 index 0000000000..56dadec87c --- /dev/null +++ b/indra/llcorehttp/_httpreplyqueue.h @@ -0,0 +1,106 @@ +/** + * @file _httpreplyqueue.h + * @brief Internal declarations for the operation reply queue. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_REPLY_QUEUE_H_ +#define _LLCORE_HTTP_REPLY_QUEUE_H_ + + +#include "_refcounted.h" +#include "_mutex.h" + + +namespace LLCore +{ + + +class HttpOperation; + + +/// Almost identical to the HttpRequestQueue class but +/// whereas that class is a singleton and is known to the +/// HttpService object, this queue is 1:1 with HttpRequest +/// instances and isn't explicitly referenced by the +/// service object. Instead, HttpOperation objects that +/// want to generate replies back to their creators also +/// keep references to the corresponding HttpReplyQueue. +/// The HttpService plumbing then simply delivers replies +/// to the requested reply queue. +/// +/// One result of that is that the fetch operations do +/// not have a wait forever option. The service object +/// doesn't keep handles on everything it would need to +/// notify so it can't wake up sleepers should it need to +/// shutdown. So only non-blocking or timed-blocking modes +/// are anticipated. These are how most application consumers +/// will be coded anyway so it shouldn't be too much of a +/// burden. + +class HttpReplyQueue: public LLCoreInt::RefCounted +{ +public: + /// Caller acquires a Refcount on construction + HttpReplyQueue(); + virtual ~HttpReplyQueue(); + +private: + HttpReplyQueue(const HttpReplyQueue &); // Not defined + void operator=(const HttpReplyQueue &); // Not defined + +public: + typedef std::vector OpContainer; + + /// Insert an object at the back of the reply queue. + /// + /// Library also takes possession of one reference count to pass + /// through the queue. + /// + /// Threading: callable by any thread. + void addOp(HttpOperation * op); + + /// Fetch an operation from the head of the queue. Returns + /// NULL if none exists. + /// + /// Caller acquires reference count on returned operation. + /// + /// Threading: callable by any thread. + HttpOperation * fetchOp(); + + /// Caller acquires reference count on each returned operation + /// + /// Threading: callable by any thread. + void fetchAll(OpContainer & ops); + +protected: + OpContainer mQueue; + LLCoreInt::HttpMutex mQueueMutex; + LLCoreInt::HttpConditionVariable mQueueCV; + +}; // end class HttpReplyQueue + +} // end namespace LLCore + + +#endif // _LLCORE_HTTP_REPLY_QUEUE_H_ diff --git a/indra/llcorehttp/_httprequestqueue.cpp b/indra/llcorehttp/_httprequestqueue.cpp new file mode 100644 index 0000000000..c36814aee3 --- /dev/null +++ b/indra/llcorehttp/_httprequestqueue.cpp @@ -0,0 +1,132 @@ +/** + * @file _httprequestqueue.cpp + * @brief + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httprequestqueue.h" + +#include "_httpoperation.h" +#include "_mutex.h" + + +using namespace LLCoreInt; + +namespace LLCore +{ + +HttpRequestQueue * HttpRequestQueue::sInstance(NULL); + + +HttpRequestQueue::HttpRequestQueue() + : RefCounted(true) +{ +} + + +HttpRequestQueue::~HttpRequestQueue() +{ + while (! mQueue.empty()) + { + HttpOperation * op = mQueue.back(); + mQueue.pop_back(); + op->release(); + } +} + + +void HttpRequestQueue::init() +{ + LLINT_ASSERT(! sInstance); + sInstance = new HttpRequestQueue(); +} + + +void HttpRequestQueue::term() +{ + if (sInstance) + { + sInstance->release(); + sInstance = NULL; + } +} + + +void HttpRequestQueue::addOp(HttpOperation * op) +{ + { + HttpScopedLock lock(mQueueMutex); + + mQueue.push_back(op); + } + mQueueCV.notify_all(); +} + + +HttpOperation * HttpRequestQueue::fetchOp(bool wait) +{ + HttpOperation * result(NULL); + + { + HttpScopedLock lock(mQueueMutex); + + while (mQueue.empty()) + { + if (! wait) + return NULL; + mQueueCV.wait(lock); + } + + result = mQueue.front(); + mQueue.erase(mQueue.begin()); + } + + // Caller also acquires the reference count + return result; +} + + +void HttpRequestQueue::fetchAll(bool wait, OpContainer & ops) +{ + // Note: Should probably test whether we're empty or not here. + // A target passed in with entries is likely also carrying + // reference counts and we're going to leak something. + ops.clear(); + { + HttpScopedLock lock(mQueueMutex); + + while (mQueue.empty()) + { + if (! wait) + return; + mQueueCV.wait(lock); + } + + mQueue.swap(ops); + } + + // Caller also acquires the reference counts on each op. + return; +} + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httprequestqueue.h b/indra/llcorehttp/_httprequestqueue.h new file mode 100644 index 0000000000..3a9ce0c3c6 --- /dev/null +++ b/indra/llcorehttp/_httprequestqueue.h @@ -0,0 +1,104 @@ +/** + * @file _httprequestqueue.h + * @brief Internal declaration for the operation request queue + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_REQUEST_QUEUE_H_ +#define _LLCORE_HTTP_REQUEST_QUEUE_H_ + + +#include + +#include "_refcounted.h" +#include "_mutex.h" + + +namespace LLCore +{ + + +class HttpOperation; + + +/// Thread-safe queue of HttpOperation objects. Just +/// a simple queue that handles the transfer of operation +/// requests from all HttpRequest instances into the +/// singleton HttpService instance. + +class HttpRequestQueue: public LLCoreInt::RefCounted +{ +protected: + /// Caller acquires a Refcount on construction + HttpRequestQueue(); + virtual ~HttpRequestQueue(); + +private: + HttpRequestQueue(const HttpRequestQueue &); // Not defined + void operator=(const HttpRequestQueue &); // Not defined + +public: + static void init(); + static void term(); + + /// Threading: callable by any thread once inited. + inline static HttpRequestQueue * instanceOf() + { + return sInstance; + } + +public: + typedef std::vector OpContainer; + + /// Insert an object at the back of the reply queue. + /// + /// Caller my provide one refcount to the Library which takes + /// possession of the count. + /// + /// Threading: callable by any thread. + void addOp(HttpOperation * op); + + /// Caller acquires reference count on returned operation + /// + /// Threading: callable by any thread. + HttpOperation * fetchOp(bool wait); + + /// Caller acquires reference count on each returned operation + /// + /// Threading: callable by any thread. + void fetchAll(bool wait, OpContainer & ops); + +protected: + static HttpRequestQueue * sInstance; + +protected: + OpContainer mQueue; + LLCoreInt::HttpMutex mQueueMutex; + LLCoreInt::HttpConditionVariable mQueueCV; + +}; // end class HttpRequestQueue + +} // end namespace LLCore + + +#endif // _LLCORE_HTTP_REQUEST_QUEUE_H_ diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp new file mode 100644 index 0000000000..6ebc0ec6cb --- /dev/null +++ b/indra/llcorehttp/_httpservice.cpp @@ -0,0 +1,209 @@ +/** + * @file _httpservice.cpp + * @brief Internal definitions of the Http service thread + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpservice.h" + +#include +#include + +#include "_httpoperation.h" +#include "_httprequestqueue.h" +#include "_httppolicy.h" +#include "_httplibcurl.h" +#include "_thread.h" + + +namespace LLCore +{ + +HttpService * HttpService::sInstance(NULL); +volatile HttpService::EState HttpService::sState(NOT_INITIALIZED); + +HttpService::HttpService() + : mRequestQueue(NULL), + mExitRequested(false), + mThread(NULL), + mPolicy(NULL), + mTransport(NULL) +{ +} + + +HttpService::~HttpService() +{ + if (mRequestQueue) + { + mRequestQueue->release(); + mRequestQueue = NULL; + } + + if (mPolicy) + { + // *TODO: need a finalization here + ; + } + + if (mTransport) + { + // *TODO: need a finalization here + delete mTransport; + mTransport = NULL; + } + + if (mPolicy) + { + delete mPolicy; + mPolicy = NULL; + } + + if (mThread) + { + mThread->release(); + mThread = NULL; + } +} + + +void HttpService::init(HttpRequestQueue * queue) +{ + LLINT_ASSERT(! sInstance); + LLINT_ASSERT(NOT_INITIALIZED == sState); + sInstance = new HttpService(); + + queue->addRef(); + sInstance->mRequestQueue = queue; + sInstance->mPolicy = new HttpPolicy(sInstance); + sInstance->mTransport = new HttpLibcurl(sInstance); + sState = INITIALIZED; +} + + +void HttpService::term() +{ + LLINT_ASSERT(RUNNING != sState); + if (sInstance) + { + delete sInstance; + sInstance = NULL; + } + sState = NOT_INITIALIZED; +} + + +bool HttpService::isStopped() +{ + // What is really wanted here is something like: + // + // HttpService * service = instanceOf(); + // return STOPPED == sState && (! service || ! service->mThread || ! service->mThread->joinable()); + // + // But boost::thread is not giving me a consistent story on joinability + // of a thread after it returns. Debug and non-debug builds are showing + // different behavior on Linux/Etch so we do a weaker test that may + // not be globally correct (i.e. thread *is* stopping, may not have + // stopped but will very soon): + + return STOPPED == sState; +} + + +void HttpService::startThread() +{ + LLINT_ASSERT(! mThread || STOPPED == sState); + LLINT_ASSERT(INITIALIZED == sState || STOPPED == sState); + + if (mThread) + { + mThread->release(); + } + mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1)); + mThread->addRef(); // Need an explicit reference, implicit one is used internally + sState = RUNNING; +} + + +void HttpService::stopRequested() +{ + mExitRequested = true; +} + + +void HttpService::shutdown() +{ + // *FIXME: Run down everything.... +} + + +void HttpService::threadRun(LLCoreInt::HttpThread * thread) +{ + boost::this_thread::disable_interruption di; + + while (! mExitRequested) + { + processRequestQueue(); + + // Process ready queue issuing new requests as needed + mPolicy->processReadyQueue(); + + // Give libcurl some cycles + mTransport->processTransport(); + + // Determine whether to spin, sleep briefly or sleep for next request + // *FIXME: For now, do this +#if defined(WIN32) + Sleep(50); +#else + usleep(5000); +#endif + } + shutdown(); + sState = STOPPED; +} + + +void HttpService::processRequestQueue() +{ + HttpRequestQueue::OpContainer ops; + + mRequestQueue->fetchAll(false, ops); + while (! ops.empty()) + { + HttpOperation * op(ops.front()); + ops.erase(ops.begin()); + + // Process operation + if (! mExitRequested) + { + op->stageFromRequest(this); + } + + // Done with operation + op->release(); + } +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h new file mode 100644 index 0000000000..c052e35452 --- /dev/null +++ b/indra/llcorehttp/_httpservice.h @@ -0,0 +1,162 @@ +/** + * @file _httpservice.h + * @brief Declarations for internal class providing HTTP service. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_SERVICE_H_ +#define _LLCORE_HTTP_SERVICE_H_ + + +namespace LLCoreInt +{ + +class HttpThread; + +} + + +namespace LLCore +{ + + +class HttpRequestQueue; +class HttpPolicy; +class HttpLibcurl; + + +/// The HttpService class does the work behind the request queue. It +/// oversees the HTTP workflow carrying out a number of tasks: +/// - Pulling requests from the global request queue +/// - Executing 'immediate' requests directly +/// - Prioritizing and re-queuing on internal queues the slower requests +/// - Providing cpu cycles to the libcurl plumbing +/// - Overseeing retry operations +/// +/// Note that the service object doesn't have a pointer to any +/// reply queue. These are kept by HttpRequest and HttpOperation +/// only. +/// +/// Service, Policy and Transport +/// +/// HttpService could have been a monolithic class combining a request +/// queue servicer, request policy manager and network transport. +/// Instead, to prevent monolithic growth and allow for easier +/// replacement, it was developed as three separate classes: HttpService, +/// HttpPolicy and HttpLibcurl (transport). These always exist in a +/// 1:1:1 relationship with HttpService managing instances of the other +/// two. So, these classes do not use reference counting to refer +/// to one-another, their lifecycles are always managed together. + +class HttpService +{ +protected: + HttpService(); + virtual ~HttpService(); + +private: + HttpService(const HttpService &); // Not defined + void operator=(const HttpService &); // Not defined + +public: + enum EState + { + NOT_INITIALIZED = -1, + INITIALIZED, ///< init() has been called + RUNNING, ///< thread created and running + STOPPED ///< thread has committed to exiting + }; + + static void init(HttpRequestQueue *); + static void term(); + + /// Threading: callable by any thread once inited. + inline static HttpService * instanceOf() + { + return sInstance; + } + + /// Return the state of the worker thread. Note that the + /// transition from RUNNING to STOPPED is performed by the + /// worker thread itself. This has two weaknesses: + /// - race where the thread hasn't really stopped but will + /// - data ordering between threads where a non-worker thread + /// may see a stale RUNNING status. + /// + /// This transition is generally of interest only to unit tests + /// and these weaknesses shouldn't be any real burden. + /// + /// Threading: callable by any thread with above exceptions. + static EState getState() + { + return sState; + } + + /// Threading: callable by any thread but uses @see getState() and + /// acquires its weaknesses. + static bool isStopped(); + + /// Threading: callable by application thread *once*. + void startThread(); + + /// Threading: callable by worker thread. + void stopRequested(); + + /// Threading: callable by worker thread. + void shutdown(); + + HttpPolicy * getPolicy() + { + return mPolicy; + } + + HttpLibcurl * getTransport() + { + return mTransport; + } + +protected: + void threadRun(LLCoreInt::HttpThread * thread); + + void processRequestQueue(); + +protected: + static HttpService * sInstance; + + // === shared data === + static volatile EState sState; + HttpRequestQueue * mRequestQueue; + volatile bool mExitRequested; + + // === calling-thread-only data === + LLCoreInt::HttpThread * mThread; + + // === working-thread-only data === + HttpPolicy * mPolicy; + HttpLibcurl * mTransport; + +}; // end class HttpService + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_SERVICE_H_ diff --git a/indra/llcorehttp/_mutex.h b/indra/llcorehttp/_mutex.h new file mode 100644 index 0000000000..4be4d016d4 --- /dev/null +++ b/indra/llcorehttp/_mutex.h @@ -0,0 +1,55 @@ +/** + * @file _mutex.hpp + * @brief mutex type abstraction + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCOREINT_MUTEX_H_ +#define LLCOREINT_MUTEX_H_ + + +#include + + +namespace LLCoreInt +{ + +// MUTEX TYPES + +// unique mutex type +typedef boost::mutex HttpMutex; + +// CONDITION VARIABLES + +// standard condition variable +typedef boost::condition_variable HttpConditionVariable; + +// LOCKS AND FENCES + +// scoped unique lock +typedef boost::unique_lock HttpScopedLock; + +} + +#endif // LLCOREINT_MUTEX_H + diff --git a/indra/llcorehttp/_refcounted.cpp b/indra/llcorehttp/_refcounted.cpp new file mode 100644 index 0000000000..2c132e1b36 --- /dev/null +++ b/indra/llcorehttp/_refcounted.cpp @@ -0,0 +1,39 @@ +/** + * @file _refcounted.cpp + * @brief Atomic, thread-safe ref counting and destruction mixin class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_refcounted.h" + + +namespace LLCoreInt +{ + +RefCounted::~RefCounted() +{} + + +} // end namespace LLCoreInt + + diff --git a/indra/llcorehttp/_refcounted.h b/indra/llcorehttp/_refcounted.h new file mode 100644 index 0000000000..4a6ce8420a --- /dev/null +++ b/indra/llcorehttp/_refcounted.h @@ -0,0 +1,141 @@ +/** + * @file _refcounted.h + * @brief Atomic, thread-safe ref counting and destruction mixin class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCOREINT__REFCOUNTED_H_ +#define LLCOREINT__REFCOUNTED_H_ + + +#include + +#include "_assert.h" + + +namespace LLCoreInt +{ + + +class RefCounted +{ +private: + RefCounted(); // Not defined - may not be default constructed + void operator=(const RefCounted &); // Not defined + +public: + explicit RefCounted(bool const implicit) + : mRefCount(implicit) + {} + + // ref-count interface + void addRef() const; + void release() const; + bool isLastRef() const; + int getRefCount() const; + void noRef() const; + + static const int NOT_REF_COUNTED = -1; + +protected: + virtual ~RefCounted(); + virtual void destroySelf(); + +private: + mutable int mRefCount; + mutable boost::mutex mRefLock; + +}; // end class RefCounted + + +inline void RefCounted::addRef() const +{ + boost::mutex::scoped_lock lock(mRefLock); + LLINT_ASSERT(mRefCount >= 0); + ++mRefCount; +} + + +inline void RefCounted::release() const +{ + int count(0); + { + // CRITICAL SECTION + boost::mutex::scoped_lock lock(mRefLock); + LLINT_ASSERT(mRefCount != NOT_REF_COUNTED); + LLINT_ASSERT(mRefCount > 0); + count = --mRefCount; + // CRITICAL SECTION + } + + + // clean ourselves up if that was the last reference + if (0 == count) + { + const_cast(this)->destroySelf(); + } +} + + +inline bool RefCounted::isLastRef() const +{ + int count(0); + { + // CRITICAL SECTION + boost::mutex::scoped_lock lock(mRefLock); + + LLINT_ASSERT(mRefCount != NOT_REF_COUNTED); + LLINT_ASSERT(mRefCount >= 1); + count = mRefCount; + // CRITICAL SECTION + } + + return (1 == count); +} + + +inline int RefCounted::getRefCount() const +{ + boost::mutex::scoped_lock lock(mRefLock); + const int result(mRefCount); + return result; +} + + +inline void RefCounted::noRef() const +{ + boost::mutex::scoped_lock lock(mRefLock); + LLINT_ASSERT(mRefCount <= 1); + mRefCount = NOT_REF_COUNTED; +} + + +inline void RefCounted::destroySelf() +{ + delete this; +} + +} // end namespace LLCoreInt + +#endif // LLCOREINT__REFCOUNTED_H_ + diff --git a/indra/llcorehttp/_thread.h b/indra/llcorehttp/_thread.h new file mode 100644 index 0000000000..5960f0dcdb --- /dev/null +++ b/indra/llcorehttp/_thread.h @@ -0,0 +1,106 @@ +/** + * @file _thread.h + * @brief thread type abstraction + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCOREINT_THREAD_H_ +#define LLCOREINT_THREAD_H_ + + +#include "_refcounted.h" + + +namespace LLCoreInt +{ + +class HttpThread : public RefCounted +{ +private: + HttpThread(); // Not defined + void operator=(const HttpThread &); // Not defined + + void at_exit() + { + // the thread function has exited so we need to release our reference + // to ourself so that we will be automagically cleaned up. + release(); + } + + void run() + { // THREAD CONTEXT + + // The implicit reference to this object is taken for the at_exit + // function so that the HttpThread instance doesn't disappear out + // from underneath it. Other holders of the object may want to + // take a reference as well. + boost::this_thread::at_thread_exit(boost::bind(&HttpThread::at_exit, this)); + + // run the thread function + mThreadFunc(this); + + } // THREAD CONTEXT + +public: + /// Constructs a thread object for concurrent execution but does + /// not start running. Unlike other classes that mixin RefCounted, + /// this does take out a reference but it is used internally for + /// final cleanup during at_exit processing. Callers needing to + /// keep a reference must increment it themselves. + /// + explicit HttpThread(boost::function threadFunc) + : RefCounted(true), // implicit reference + mThreadFunc(threadFunc) + { + // this creates a boost thread that will call HttpThread::run on this instance + // and pass it the threadfunc callable... + boost::function f = boost::bind(&HttpThread::run, this); + + mThread = new boost::thread(f); + } + + virtual ~HttpThread() + { + delete mThread; + } + + inline void join() + { + mThread->join(); + } + + inline bool joinable() const + { + mThread->joinable(); + } + +private: + boost::function mThreadFunc; + boost::thread * mThread; +}; // end class HttpThread + +} // end namespace LLCoreInt + +#endif // LLCOREINT_THREAD_H_ + + diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp new file mode 100644 index 0000000000..4c20350b13 --- /dev/null +++ b/indra/llcorehttp/bufferarray.cpp @@ -0,0 +1,281 @@ +/** + * @file bufferarray.cpp + * @brief Implements the BufferArray scatter/gather buffer + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "bufferarray.h" + + +namespace LLCore +{ + + +// ================================== +// BufferArray::Block Declaration +// ================================== + +class BufferArray::Block +{ +public: + ~Block(); + + void operator delete(void *); + void operator delete(void *, size_t len); + +protected: + Block(size_t len); + + Block(const Block &); // Not defined + void operator=(const Block &); // Not defined + + // Allocate the block with the additional space for the + // buffered data at the end of the object. + void * operator new(size_t len, size_t addl_len); + +public: + // Only public entry to get a block. + static Block * alloc(size_t len); + +public: + size_t mLen; + + // *NOTE: Must be last member of the object. We'll + // overallocate as requested via operator new and index + // into the array at will. + char mData[1]; +}; + + +// ================================== +// BufferArray Definitions +// ================================== + + +BufferArray::BufferArray() + : LLCoreInt::RefCounted(true), + mPos(0), + mLen(0) +{} + + +BufferArray::~BufferArray() +{ + for (container_t::iterator it(mBlocks.begin()); + it != mBlocks.end(); + ++it) + { + delete *it; + *it = NULL; + } + mBlocks.clear(); +} + + +size_t BufferArray::append(const char * src, size_t len) +{ + if (len) + { + if (mBlocks.size() >= mBlocks.capacity()) + { + mBlocks.reserve(mBlocks.size() + 5); + } + Block * block = Block::alloc(len); + memcpy(block->mData, src, len); + mBlocks.push_back(block); + mLen += len; + mPos = mLen; + } + return len; +} + + +char * BufferArray::appendBufferAlloc(size_t len) +{ + // If someone asks for zero-length, we give them a valid pointer. + if (mBlocks.size() >= mBlocks.capacity()) + { + mBlocks.reserve(mBlocks.size() + 5); + } + Block * block = Block::alloc(len); + mBlocks.push_back(block); + mLen += len; + mPos = mLen; + return block->mData; +} + + +size_t BufferArray::seek(size_t pos) +{ + if (pos > mLen) + pos = mLen; + mPos = pos; + return mPos; +} + + +size_t BufferArray::read(char * dst, size_t len) +{ + size_t result(0), offset(0); + size_t len_limit(mLen - mPos); + len = std::min(len, len_limit); + + if (mPos >= mLen || 0 == len) + return 0; + + const int block_limit(mBlocks.size()); + int block_start(findBlock(mPos, &offset)); + if (block_start < 0) + return 0; + + do + { + Block & block(*mBlocks[block_start]); + size_t block_limit(block.mLen - offset); + size_t block_len(std::min(block_limit, len)); + + memcpy(dst, &block.mData[offset], block_len); + result += block_len; + len -= block_len; + dst += block_len; + offset = 0; + ++block_start; + } + while (len && block_start < block_limit); + + mPos += result; + return result; +} + + +size_t BufferArray::write(const char * src, size_t len) +{ + size_t result(0), offset(0); + if (mPos > mLen || 0 == len) + return 0; + + const int block_limit(mBlocks.size()); + int block_start(findBlock(mPos, &offset)); + + if (block_start >= 0) + { + // Some or all of the write will be on top of + // existing data. + do + { + Block & block(*mBlocks[block_start]); + size_t block_limit(block.mLen - offset); + size_t block_len(std::min(block_limit, len)); + + memcpy(&block.mData[offset], src, block_len); + result += block_len; + len -= block_len; + src += block_len; + offset = 0; + ++block_start; + } + while (len && block_start < block_limit); + } + mPos += result; + + if (len) + { + // Some or all of the remaining write data will + // be an append. + result += append(src, len); + } + + return result; +} + + +int BufferArray::findBlock(size_t pos, size_t * ret_offset) +{ + *ret_offset = 0; + if (pos >= mLen) + return -1; // Doesn't exist + + const int block_limit(mBlocks.size()); + for (int i(0); i < block_limit; ++i) + { + if (pos < mBlocks[i]->mLen) + { + *ret_offset = pos; + return i; + } + pos -= mBlocks[i]->mLen; + } + + // Shouldn't get here but... + return -1; +} + + +// ================================== +// BufferArray::Block Definitions +// ================================== + + +BufferArray::Block::Block(size_t len) + : mLen(len) +{ + memset(mData, 0, len); +} + + +BufferArray::Block::~Block() +{ + mLen = 0; +} + + +void * BufferArray::Block::operator new(size_t len, size_t addl_len) +{ + void * mem = new char[len + addl_len + sizeof(void *)]; + return mem; +} + + +void BufferArray::Block::operator delete(void * mem) +{ + char * cmem = static_cast(mem); + delete [] cmem; +} + + +void BufferArray::Block::operator delete(void * mem, size_t) +{ + operator delete(mem); +} + + +BufferArray::Block * BufferArray::Block::alloc(size_t len) +{ + Block * block = new (len) Block(len); + return block; +} + + +} // end namespace LLCore + + diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h new file mode 100644 index 0000000000..b26ad1b297 --- /dev/null +++ b/indra/llcorehttp/bufferarray.h @@ -0,0 +1,168 @@ +/** + * @file bufferarray.h + * @brief Public-facing declaration for the BufferArray scatter/gather class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_BUFFER_ARRAY_H_ +#define _LLCORE_BUFFER_ARRAY_H_ + + +#include +#include + +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// A very simple scatter/gather type map for bulk data. The motivation +/// for this class is the writedata callback used by libcurl. Response +/// bodies are delivered to the caller in a sequence of sequential write +/// operations and this class captures them without having to reallocate +/// and move data. +/// +/// The interface looks a little like a unix file descriptor but only +/// just. There is a notion of a current position, starting from 0, +/// which is used as the position in the data when performing read and +/// write operations. The position also moves after various operations: +/// - seek(...) +/// - read(...) +/// - write(...) +/// - append(...) +/// - appendBufferAlloc(...) +/// The object also keeps a total length value which is updated after +/// write and append operations and beyond which the current position +/// cannot be set. +/// +/// Threading: not thread-safe +/// +/// Allocation: Refcounted, heap only. Caller of the constructor +/// is given a single refcount. +/// +class BufferArray : public LLCoreInt::RefCounted +{ +public: + BufferArray(); + virtual ~BufferArray(); + +private: + BufferArray(const BufferArray &); // Not defined + void operator=(const BufferArray &); // Not defined + +public: + /// Appends the indicated data to the BufferArray + /// modifying current position and total size. New + /// position is one beyond the final byte of the buffer. + /// + /// @return Count of bytes copied to BufferArray + size_t append(const char * src, size_t len); + + /// Similar to @see append(), this call guarantees a + /// contiguous block of memory of requested size placed + /// at the current end of the BufferArray. On return, + /// the data in the memory is considered valid whether + /// the caller writes to it or not. + /// + /// @return Pointer to contiguous region at end + /// of BufferArray of 'len' size. + char * appendBufferAlloc(size_t len); + + /// Current count of bytes in BufferArray instance. + size_t size() const + { + return mLen; + } + + /// Set the current position for subsequent read and + /// write operations. 'pos' values before the beginning + /// or greater than the size of the buffer are coerced + /// to a value within the buffer. + /// + /// @return Actual current position after seek. + size_t seek(size_t pos); + + /// Copies data from the current position in the instance + /// to the caller's buffer. Will return a short count of + /// bytes copied if the 'len' extends beyond the data. + size_t read(char * dst, size_t len); + + /// Copies data from the caller's buffer to the instance + /// at the current position. May overwrite existing data, + /// append data when current position is equal to the + /// size of the instance or do a mix of both. + size_t write(const char * src, size_t len); + +protected: + int findBlock(size_t pos, size_t * ret_offset); + +protected: + class Block; + typedef std::vector container_t; + + container_t mBlocks; + size_t mPos; + size_t mLen; +}; // end class BufferArray + + +#if 0 + +// Conceptual for now. Another possibility is going with +// something like Boost::asio's buffers interface. They're +// trying to achieve the same thing above and below.... + +class BufferStream : public std::streambuf +{ +public: + BufferStream(BufferArray * buffer); + virtual ~BufferStream(); + +private: + BufferStream(const BufferStream &); // Not defined + void operator=(const BufferStream &); // Not defined + +public: + // Types + typedef std::streambuf::pos_type pos_type; + typedef std::streambuf::off_type off_type; + + virtual int underflow(); + + virtual int overflow(int c); + + virtual int sync(); + + virtual pos_type seekoff(off_type off, std::ios::seekdir way, std::ios::openmode which); + +protected: + BufferArray * mBufferArray; +}; // end class BufferStream + +#endif // 0 + +} // end namespace LLCore + +#endif // _LLCORE_BUFFER_ARRAY_H_ diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp new file mode 100644 index 0000000000..c37d081150 --- /dev/null +++ b/indra/llcorehttp/httpcommon.cpp @@ -0,0 +1,76 @@ +/** + * @file httpcommon.cpp + * @brief + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "httpcommon.h" + +#include +#include + + +namespace LLCore +{ + +HttpStatus::type_enum_t EXT_CURL_EASY; +HttpStatus::type_enum_t EXT_CURL_MULTI; +HttpStatus::type_enum_t LLCORE; + +std::string HttpStatus::toString() const +{ + static const char * llcore_errors[] = + { + "", + "Services shutting down", + "Operation canceled", + "Invalid Content-Range header encountered" + }; + static const int llcore_errors_count(sizeof(llcore_errors) / sizeof(llcore_errors[0])); + + if (*this) + { + return std::string(""); + } + + switch (mType) + { + case EXT_CURL_EASY: + return std::string(curl_easy_strerror(CURLcode(mStatus))); + + case EXT_CURL_MULTI: + return std::string(curl_multi_strerror(CURLMcode(mStatus))); + + case LLCORE: + if (mStatus >= 0 && mStatus < llcore_errors_count) + { + return std::string(llcore_errors[mStatus]); + } + break; + + } + return std::string("Unknown error"); +} + +} // end namespace LLCore + diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h new file mode 100644 index 0000000000..617286fb38 --- /dev/null +++ b/indra/llcorehttp/httpcommon.h @@ -0,0 +1,204 @@ +/** + * @file httpcommon.h + * @brief Public-facing declarations and definitions of common types + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_COMMON_H_ +#define _LLCORE_HTTP_COMMON_H_ + +/// @package LLCore::HTTP +/// +/// This library implements a high-level, Indra-code-free client interface to +/// HTTP services based on actual patterns found in the viewer and simulator. +/// Interfaces are similar to those supplied by the legacy classes +/// LLCurlRequest and LLHTTPClient. To that is added a policy scheme that +/// allows an application to specify connection behaviors: limits on +/// connections, HTTP keepalive, HTTP pipelining, retry-on-error limits, etc. +/// +/// Features of the library include: +/// - Single, private working thread where all transport and processing occurs. +/// - Support for multiple consumers running in multiple threads. +/// - Scatter/gather (a.k.a. buffer array) model for bulk data movement. +/// - Reference counting used for many object instance lifetimes. +/// - Minimal data sharing across threads for correctness and low latency. +/// +/// The public interface is declared in a few key header files: +/// - +/// - +/// - +/// - +/// - +/// - +/// - +/// +/// The library is still under early development and particular users +/// may need access to internal implementation details that are found +/// in the _*.h header files. But this is a crutch to be avoided if at +/// all possible and probably indicates some interface work is neeeded. +/// +/// Using the library is fairly easy. Global setup needs a few +/// steps: +/// +/// - libcurl initialization with thread-safely callbacks for c-ares +/// DNS lookups. +/// - HttpRequest::createService() called to instantiate singletons +/// and support objects. +/// +/// An HTTP consumer in an application, and an application may have many +/// consumers, does a few things: +/// +/// - Instantiate and retain an object based on HttpRequest. This +/// object becomes the portal into runtime services for the consumer. +/// - Derive or mixin the HttpHandler class if you want notification +/// when requests succeed or fail. This object's onCompleted() +/// method is invoked and an instance can be shared across +/// requests. +/// +/// Issuing a request is straightforward: +/// - Construct a suitable URL. +/// - Configure HTTP options for the request. (optional) +/// - Build a list of additional headers. (optional) +/// - Invoke one of the requestXXXX() methods (requestGetByteRange, +/// requestPost, etc.) on the HttpRequest instance supplying the +/// above along with a policy class, a priority and an optional +/// pointer to an HttpHandler instance. Work is then queued to +/// the worker thread and occurs asynchronously. +/// - Periodically invoke the update() method on the HttpRequest +/// instance which performs completion notification to HttpHandler +/// objects. +/// - Do completion processing in your onCompletion() method. +/// +/// Code fragments: +/// +/// + +#include + + +namespace LLCore +{ + + +/// All queued requests are represented by an HttpHandle value. +/// The invalid value is returned when a request failed to queue. +/// The actual status for these failures is then fetched with +/// HttpRequest::getStatus(). +/// +/// The handle is valid only for the life of a request. On +/// return from any HttpHandler notification, the handle immediately +/// becomes invalid and may be recycled for other queued requests. + +typedef void * HttpHandle; +#define LLCORE_HTTP_HANDLE_INVALID (NULL) + + +/// Error codes defined by the library itself as distinct from +/// libcurl (or any other transport provider). +enum HttpError +{ + // Successful value compatible with the libcurl codes. + HE_SUCCESS = 0, + + // Service is shutting down and requested operation will + // not be queued or performed. + HE_SHUTTING_DOWN = 1, + + // Operation was canceled by request. + HE_OP_CANCELED = 2, + + // Invalid content range header received. + HE_INV_CONTENT_RANGE_HDR = 3 + +}; // end enum HttpError + + +/// HttpStatus encapsulates errors from libcurl (easy, multi) as well as +/// internal errors. The encapsulation isn't expected to completely +/// isolate the caller from libcurl but basic operational tests (success +/// or failure) are provided. +struct HttpStatus +{ + typedef unsigned short type_enum_t; + + HttpStatus() + : mType(LLCORE), + mStatus(HE_SUCCESS) + {} + + HttpStatus(type_enum_t type, short status) + : mType(type), + mStatus(status) + {} + + HttpStatus(const HttpStatus & rhs) + : mType(rhs.mType), + mStatus(rhs.mStatus) + {} + + HttpStatus & operator=(const HttpStatus & rhs) + { + // Don't care if lhs & rhs are the same object + + mType = rhs.mType; + mStatus = rhs.mStatus; + return *this; + } + + static const type_enum_t EXT_CURL_EASY = 0; + static const type_enum_t EXT_CURL_MULTI = 1; + static const type_enum_t LLCORE = 2; + + type_enum_t mType; + short mStatus; + + /// Test for successful status in the code regardless + /// of error source (internal, libcurl). + /// + /// @return 'true' when status is successful. + /// + operator bool() const + { + return 0 == mStatus; + } + + /// Inverse of previous operator. + /// + /// @return 'true' on any error condition + bool operator !() const + { + return 0 != mStatus; + } + + /// Convert status to a string representation. For + /// success, returns an empty string. For failure + /// statuses, a string as appropriate for the source of + /// the error code (libcurl easy, libcurl multi, or + /// LLCore itself). + std::string toString() const; + +}; // end struct HttpStatus + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_COMMON_H_ diff --git a/indra/llcorehttp/httphandler.h b/indra/llcorehttp/httphandler.h new file mode 100644 index 0000000000..9171e4e7b9 --- /dev/null +++ b/indra/llcorehttp/httphandler.h @@ -0,0 +1,88 @@ +/** + * @file httphandler.h + * @brief Public-facing declarations for the HttpHandler class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_HANDLER_H_ +#define _LLCORE_HTTP_HANDLER_H_ + + +#include "httpcommon.h" + + +namespace LLCore +{ + +class HttpResponse; + + +/// HttpHandler defines an interface used by the library to +/// notify library callers of significant events, currently +/// request completion. Callers must derive or mixin this class +/// then provide an implementation of the @see onCompleted +/// method to receive such notifications. An instance may +/// be shared by any number of requests and across instances +/// of HttpRequest running in the same thread. +/// +/// Threading: HttpHandler itself is pure interface and is +/// tread-compatible. Most derivations, however, will have +/// different constraints. +/// +/// Allocation: Not refcounted, may be stack allocated though +/// that is rarely a good idea. Queued requests and replies keep +/// a naked pointer to the handler and this can result in a +/// dangling pointer if lifetimes aren't managed correctly. + +class HttpHandler +{ +public: + virtual ~HttpHandler() + {} + + /// Method invoked during calls to @see update(). Each invocation + /// represents the completion of some requested operation. Caller + /// can identify the request from the handle and interrogate the + /// response argument for success/failure, data and other information. + /// + /// @param handle Identifier of the request generating + /// the notification. + /// @param response Supplies detailed information about + /// the request including status codes + /// (both programming and HTTP), HTTP body + /// data and encodings, headers, etc. + /// The response object is refcounted and + /// the called code may retain the object + /// by invoking @see addRef() on it. The + /// library itself drops all references to + /// to object on return and never touches + /// it again. + /// + virtual void onCompleted(HttpHandle handle, HttpResponse * response) = 0; + +}; // end class HttpHandler + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_HANDLER_H_ diff --git a/indra/llcorehttp/httpheaders.cpp b/indra/llcorehttp/httpheaders.cpp new file mode 100644 index 0000000000..2832696271 --- /dev/null +++ b/indra/llcorehttp/httpheaders.cpp @@ -0,0 +1,44 @@ +/** + * @file httpheaders.cpp + * @brief Implementation of the HTTPHeaders class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "httpheaders.h" + + +namespace LLCore +{ + + +HttpHeaders::HttpHeaders() + : RefCounted(true) +{} + + +HttpHeaders::~HttpHeaders() +{} + + +} // end namespace LLCore + diff --git a/indra/llcorehttp/httpheaders.h b/indra/llcorehttp/httpheaders.h new file mode 100644 index 0000000000..0b6d82561b --- /dev/null +++ b/indra/llcorehttp/httpheaders.h @@ -0,0 +1,86 @@ +/** + * @file httpheaders.h + * @brief Public-facing declarations for the HttpHeaders class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_HEADERS_H_ +#define _LLCORE_HTTP_HEADERS_H_ + + +#include + +#include "_refcounted.h" + + +namespace LLCore +{ + +/// +/// Maintains an ordered list of name/value pairs representing +/// HTTP header lines. This is used both to provide additional +/// headers when making HTTP requests and in responses when the +/// caller has asked that headers be returned (not the default +/// option). +/// +/// @note +/// This is a minimally-functional placeholder at the moment +/// to fill out the class hierarchy. The final class will be +/// something else, probably more pair-oriented. It's also +/// an area where shared values are desirable so refcounting is +/// already specced and a copy-on-write scheme imagined. +/// Expect changes here. +/// +/// Threading: Not intrinsically thread-safe. It *is* expected +/// that callers will build these objects and then share them +/// via reference counting with the worker thread. The implication +/// is that once an HttpHeader instance is handed to a request, +/// the object must be treated as read-only. +/// +/// Allocation: Refcounted, heap only. Caller of the +/// constructor is given a refcount. +/// + +class HttpHeaders : public LLCoreInt::RefCounted +{ +public: + /// @post In addition to the instance, caller has a refcount + /// to the instance. A call to @see release() will destroy + /// the instance. + HttpHeaders(); + ~HttpHeaders(); + +protected: + HttpHeaders(const HttpHeaders &); // Not defined + void operator=(const HttpHeaders &); // Not defined + +public: + typedef std::vector container_t; + container_t mHeaders; + +}; // end class HttpHeaders + +} // end namespace LLCore + + +#endif // _LLCORE_HTTP_HEADERS_H_ diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp new file mode 100644 index 0000000000..15b505f5bf --- /dev/null +++ b/indra/llcorehttp/httpoptions.cpp @@ -0,0 +1,50 @@ +/** + * @file httpoptions.cpp + * @brief Implementation of the HTTPOptions class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "httpoptions.h" + + +namespace LLCore +{ + + +HttpOptions::HttpOptions() + : RefCounted(true), + mWantHeaders(false) +{} + + +HttpOptions::HttpOptions(const HttpOptions & rhs) + : RefCounted(true), + mWantHeaders(rhs.mWantHeaders) +{} + + +HttpOptions::~HttpOptions() +{} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h new file mode 100644 index 0000000000..267a982dd5 --- /dev/null +++ b/indra/llcorehttp/httpoptions.h @@ -0,0 +1,80 @@ +/** + * @file httpoptions.h + * @brief Public-facing declarations for the HTTPOptions class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_OPTIONS_H_ +#define _LLCORE_HTTP_OPTIONS_H_ + + +#include "httpcommon.h" + +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// Really a struct in spirit, it provides options that +/// modify HTTP requests. +/// +/// Sharing instances across requests. It's intended that +/// these be shared across requests: caller can create one +/// of these, set it up as needed and then reference it +/// repeatedly in HTTP operations. But see the Threading +/// note about references. +/// +/// Threading: While this class does nothing to ensure thread +/// safety, it *is* intended to be shared between the application +/// thread and the worker thread. This means that once an instance +/// is delivered to the library in request operations, the +/// option data must not be written until all such requests +/// complete and relinquish their references. +/// +/// Allocation: Refcounted, heap only. Caller of the constructor +/// is given a refcount. +/// +class HttpOptions : public LLCoreInt::RefCounted +{ +public: + HttpOptions(); + HttpOptions(const HttpOptions &); + virtual ~HttpOptions(); + +protected: + void operator=(const HttpOptions &); // Not defined + +public: + +protected: + // *TODO: add some options + bool mWantHeaders; + +}; // end class HttpOptions + + +} // end namespace HttpOptions + +#endif // _LLCORE_HTTP_OPTIONS_H_ diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp new file mode 100644 index 0000000000..2a87f5231a --- /dev/null +++ b/indra/llcorehttp/httprequest.cpp @@ -0,0 +1,295 @@ +/** + * @file httprequest.cpp + * @brief Implementation of the HTTPRequest class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "httprequest.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" +#include "_httpoperation.h" +#include "_httpoprequest.h" + + +namespace +{ + +bool has_inited(false); + +} + +namespace LLCore +{ + +// ==================================== +// InternalHandler Implementation +// ==================================== + + +class HttpRequest::InternalHandler : public HttpHandler +{ +public: + InternalHandler(HttpRequest & request) + : mRequest(request) + {} + +protected: + InternalHandler(const InternalHandler &); // Not defined + void operator=(const InternalHandler &); // Not defined + +public: + void onCompleted(HttpHandle handle, HttpResponse * response) + { + HttpOperation * op(static_cast(handle)); + HttpHandler * user_handler(op->getUserHandler()); + if (user_handler) + { + user_handler->onCompleted(handle, response); + } + } + +protected: + HttpRequest & mRequest; + +}; // end class HttpRequest::InternalHandler + + +// ==================================== +// HttpRequest Implementation +// ==================================== + + +unsigned int HttpRequest::sNextPolicyID(1); + + +HttpRequest::HttpRequest() + : //HttpHandler(), + mReplyQueue(NULL), + mRequestQueue(NULL), + mSelfHandler(NULL) +{ + mRequestQueue = HttpRequestQueue::instanceOf(); + mRequestQueue->addRef(); + + mReplyQueue = new HttpReplyQueue(); + + mSelfHandler = new InternalHandler(*this); +} + + +HttpRequest::~HttpRequest() +{ + if (mRequestQueue) + { + mRequestQueue->release(); + mRequestQueue = NULL; + } + + if (mReplyQueue) + { + mReplyQueue->release(); + mReplyQueue = NULL; + } + + delete mSelfHandler; + mSelfHandler = NULL; +} + + +// ==================================== +// Policy Methods +// ==================================== + + +HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value) +{ + HttpStatus status; + + return status; +} + + +unsigned int HttpRequest::createPolicyClass() +{ + unsigned int policy_id = 1; + + return policy_id; +} + + +HttpStatus HttpRequest::setPolicyClassOption(unsigned int policy_id, + EClassPolicy opt, + long value) +{ + HttpStatus status; + + return status; +} + + +// ==================================== +// Request Methods +// ==================================== + + +HttpStatus HttpRequest::getStatus() const +{ + return mLastReqStatus; +} + + +HttpHandle HttpRequest::requestGetByteRange(unsigned int policy_id, + float priority, + const std::string & url, + size_t offset, + size_t len, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpRequest * op = new HttpOpRequest(); + if (! (status = op->setupGetByteRange(policy_id, priority, url, offset, len, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + mRequestQueue->addOp(op); // transfers refcount + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + + +HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpNull * op = new HttpOpNull(); + op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + mRequestQueue->addOp(op); // transfer refcount as well + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + + +HttpStatus HttpRequest::update(long millis) +{ + HttpStatus status; + + // *FIXME: need timer stuff + // long now(getNow()); + // long limit(now + millis); + + HttpOperation * op(NULL); + while ((op = mReplyQueue->fetchOp())) + { + // Process operation + op->visitNotifier(this); + + // We're done with the operation + op->release(); + } + + return status; +} + + + + +// ==================================== +// Request Management Methods +// ==================================== + + +// ==================================== +// Utility Methods +// ==================================== + +HttpStatus HttpRequest::createService() +{ + HttpStatus status; + + LLINT_ASSERT(! has_inited); + HttpRequestQueue::init(); + HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + HttpService::init(rq); + has_inited = true; + + return status; +} + + +HttpStatus HttpRequest::destroyService() +{ + HttpStatus status; + + LLINT_ASSERT(has_inited); + HttpService::term(); + HttpRequestQueue::term(); + has_inited = false; + + return status; +} + + +HttpStatus HttpRequest::startThread() +{ + HttpStatus status; + + HttpService::instanceOf()->startThread(); + + return status; +} + + +HttpHandle HttpRequest::requestStopThread(HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpStop * op = new HttpOpStop(); + op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + mRequestQueue->addOp(op); // transfer refcount as well + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + + +} // end namespace LLCore + diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h new file mode 100644 index 0000000000..4bbd13a13a --- /dev/null +++ b/indra/llcorehttp/httprequest.h @@ -0,0 +1,316 @@ +/** + * @file httprequest.h + * @brief Public-facing declarations for HttpRequest class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_REQUEST_H_ +#define _LLCORE_HTTP_REQUEST_H_ + + +#include "httpcommon.h" +#include "httphandler.h" + + +namespace LLCore +{ + +class HttpRequestQueue; +class HttpReplyQueue; +class HttpService; +class HttpOptions; +class HttpHeaders; +class HttpOperation; + +/// HttpRequest supplies the entry into the HTTP transport +/// services in the LLCore libraries. Services provided include: +/// +/// - Some, but not all, global initialization of libcurl. +/// - Starting asynchronous, threaded HTTP requests. +/// - Definition of policy classes affect request handling. +/// - Utilities to control request options and headers +/// +/// Requests +/// +/// The class supports the current HTTP request operations: +/// +/// - requestGetByteRange: GET with Range header for a single range of bytes +/// +/// Policy Classes +/// +/// +/// +/// Usage +/// +/// +/// +/// Threading: An instance may only be used by one application/ +/// consumer thread. But a thread may have as many instances of +/// this as it likes. +/// +/// Allocation: Not refcounted, may be stack allocated though that +/// hasn't been tested. Queued requests can still run and any +/// queued replies will keep refcounts to the reply queue leading +/// to memory leaks. +/// +/// @pre Before using this class (static or instances), some global +/// initialization is required. See @see httpcommon.h for more information. +/// +/// @nosubgrouping +/// + +class HttpRequest +{ +public: + HttpRequest(); + virtual ~HttpRequest(); + +private: + HttpRequest(const HttpRequest &); // Disallowed + void operator=(const HttpRequest &); // Disallowed + +public: + /// @name PolicyMethods + /// @{ + + /// Represents a default, catch-all policy class that guarantees + /// eventual service for any HTTP request. + static const int DEFAULT_POLICY_ID = 0; + + enum EGlobalPolicy + { + /// Maximum number of connections the library will use to + /// perform operations. This is somewhat soft as the underlying + /// transport will cache some connections (up to 5). + GLOBAL_CONNECTION_LIMIT + }; + + /// Set a parameter on a global policy option. Calls + /// made after the start of the servicing thread are + /// not honored and return an error status. + /// + /// @param opt Enum of option to be set. + /// @param value Desired value of option. + /// @return Standard status code. + HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value); + + /// Create a new policy class into which requests can be made. + /// + /// @return If positive, the policy_id used to reference + /// the class in other methods. If -1, an error + /// occurred and @see getStatus() may provide more + /// detail on the reason. + unsigned int createPolicyClass(); + + enum EClassPolicy + { + /// Limits the number of connections used for the class. + CLASS_CONNECTION_LIMIT, + + /// Limits the number of connections used for a single + /// literal address/port pair within the class. + PER_HOST_CONNECTION_LIMIT, + + /// Suitable requests are allowed to pipeline on their + /// connections when they ask for it. + ENABLE_PIPELINING + }; + + /// Set a parameter on a class-based policy option. Calls + /// made after the start of the servicing thread are + /// not honored and return an error status. + /// + /// @param policy_id ID of class as returned by @see createPolicyClass(). + /// @param opt Enum of option to be set. + /// @param value Desired value of option. + /// @return Standard status code. + HttpStatus setPolicyClassOption(unsigned int policy_id, + EClassPolicy opt, + long value); + + /// @} + + /// @name RequestMethods + /// + /// @{ + + /// Some calls expect to succeed as the normal part of operation and so + /// return a useful value rather than a status. When they do fail, the + /// status is saved and can be fetched with this method. + /// + /// @return Status of the failing method invocation. If the + /// preceding call succeeded or other HttpStatus + /// returning calls immediately preceded this method, + /// the returned value may not be reliable. + /// + HttpStatus getStatus() const; + + /// Queue a full HTTP GET request to be issued with a 'Range' header. + /// The request is queued and serviced by the working thread and + /// notification of completion delivered to the optional HttpHandler + /// argument during @see update() calls. + /// + /// With a valid handle returned, it can be used to reference the + /// request in other requests (like cancellation) and will be an + /// argument when any HttpHandler object is invoked. + /// + /// @param policy_id Default or user-defined policy class under + /// which this request is to be serviced. + /// @param priority Standard priority scheme inherited from + /// Indra code base. + /// @param url + /// @param offset + /// @param len + /// @param options (optional) + /// @param headers (optional) + /// @param handler (optional) + /// @return The handle of the request if successfully + /// queued or LLCORE_HTTP_HANDLE_INVALID if the + /// request could not be queued. In the latter + /// case, @see getStatus() will return more info. + /// + HttpHandle requestGetByteRange(unsigned int policy_id, + float priority, + const std::string & url, + size_t offset, + size_t len, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * handler); + + + /// Queue a NoOp request. + /// The request is queued and serviced by the working thread which + /// immediately processes it and returns the request to the reply + /// queue. + /// + /// @param handler (optional) + /// @return The handle of the request if successfully + /// queued or LLCORE_HTTP_HANDLE_INVALID if the + /// request could not be queued. In the latter + /// case, @see getStatus() will return more info. + /// + HttpHandle requestNoOp(HttpHandler * handler); + + /// While all the heavy work is done by the worker thread, notifications + /// must be performed in the context of the application thread. These + /// are done synchronously during calls to this method which gives the + /// library control so notification can be performed. Application handlers + /// are expected to return 'quickly' and do any significant processing + /// outside of the notification callback to onCompleted(). + /// + /// @param millis Maximum number of wallclock milliseconds to + /// spend in the call. As hinted at above, this + /// is partly a function of application code so it's + /// a soft limit. (And not currently implemented.) + /// + /// @return Standard status code. + HttpStatus update(long millis); + + /// @} + + /// @name RequestMgmtMethods + /// + /// @{ + + HttpHandle requestCancel(HttpHandle request, HttpHandler *); + + /// @} + + /// @name UtilityMethods + /// + /// @{ + + /// Initialization method that needs to be called before queueing any + /// requests. Doesn't start the worker thread and may be called befoer + /// or after policy setup. + static HttpStatus createService(); + + /// Mostly clean shutdown of services prior to exit. Caller is expected + /// to have stopped a running worker thread before calling this. + static HttpStatus destroyService(); + + /// Called once after @see createService() to start the worker thread. + /// Stopping the thread is achieved by requesting it via @see requestStopThread(). + /// May be called before or after requests are issued. + static HttpStatus startThread(); + + /// Queues a request to the worker thread to have it stop processing + /// and exit (without exiting the program). When the operation is + /// picked up by the worker thread, it immediately processes it and + /// begins detaching from refcounted resources like request and + /// reply queues and then returns to the host OS. It *does* queue a + /// reply to give the calling application thread a notification that + /// the operation has been performed. + /// + /// @param handler (optional) + /// @return The handle of the request if successfully + /// queued or LLCORE_HTTP_HANDLE_INVALID if the + /// request could not be queued. In the latter + /// case, @see getStatus() will return more info. + /// As the request cannot be cancelled, the handle + /// is generally not useful. + /// + HttpHandle requestStopThread(HttpHandler * handler); + + /// @} + +protected: + void generateNotification(HttpOperation * op); + + class InternalHandler; + friend class InternalHandler; + +private: + /// @name InstanceData + /// + /// @{ + HttpStatus mLastReqStatus; + HttpReplyQueue * mReplyQueue; + HttpRequestQueue * mRequestQueue; + InternalHandler * mSelfHandler; + + /// @} + + // ==================================== + /// @name GlobalState + /// + /// @{ + /// + /// Must be established before any threading is allowed to + /// start. + /// + static unsigned int sNextPolicyID; + + /// @} + // End Global State + // ==================================== + +}; // end class HttpRequest + + +} // end namespace LLCore + + + +#endif // _LLCORE_HTTP_REQUEST_H_ diff --git a/indra/llcorehttp/httpresponse.cpp b/indra/llcorehttp/httpresponse.cpp new file mode 100644 index 0000000000..9ac8276f05 --- /dev/null +++ b/indra/llcorehttp/httpresponse.cpp @@ -0,0 +1,89 @@ +/** + * @file httpresponse.cpp + * @brief + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "httpresponse.h" +#include "bufferarray.h" +#include "httpheaders.h" + + +namespace LLCore +{ + + +HttpResponse::HttpResponse() + : LLCoreInt::RefCounted(true), + mReplyStatus(0U), + mBufferArray(NULL), + mHeaders(NULL) +{} + + +HttpResponse::~HttpResponse() +{ + setBody(NULL); + setHeaders(NULL); +} + + +void HttpResponse::setBody(BufferArray * ba) +{ + if (mBufferArray == ba) + return; + + if (mBufferArray) + { + mBufferArray->release(); + } + + if (ba) + { + ba->addRef(); + } + + mBufferArray = ba; +} + + +void HttpResponse::setHeaders(HttpHeaders * headers) +{ + if (mHeaders == headers) + return; + + if (mHeaders) + { + mHeaders->release(); + } + + if (headers) + { + headers->addRef(); + } + + mHeaders = headers; +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h new file mode 100644 index 0000000000..a25e22aef6 --- /dev/null +++ b/indra/llcorehttp/httpresponse.h @@ -0,0 +1,137 @@ +/** + * @file httpresponse.h + * @brief Public-facing declarations for the HttpResponse class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_RESPONSE_H_ +#define _LLCORE_HTTP_RESPONSE_H_ + + +#include "httpcommon.h" + +#include "_refcounted.h" + + +namespace LLCore +{ + +class BufferArray; +class HttpHeaders; + +/// HttpResponse is instantiated by the library and handed to +/// the caller during callbacks to the handler. It supplies +/// all the status, header and HTTP body data the caller is +/// interested in. Methods provide simple getters to return +/// individual pieces of the response. +/// +/// Typical usage will have the caller interrogate the object +/// and return from the handler callback. Instances are refcounted +/// and callers can bump the count and retain the object as needed. +/// +/// Threading: Not intrinsically thread-safe. +/// +/// Allocation: Refcounted, heap only. Caller of the constructor +/// is given a refcount. +/// +class HttpResponse : public LLCoreInt::RefCounted +{ +public: + HttpResponse(); + virtual ~HttpResponse(); + +protected: + HttpResponse(const HttpResponse &); // Not defined + void operator=(const HttpResponse &); // Not defined + +public: + /// Returns the final status of the requested operation. + /// + // *FIXME: Haven't incorporated HTTP status into this yet. + // Will do soon. + HttpStatus getStatus() const + { + return mStatus; + } + + void setStatus(const HttpStatus & status) + { + mStatus = status; + } + + /// Fetch the HTTP reply status. This is only guaranteed to be + /// valid if the HttpStatus tests successful and was the result + /// of a completed HTTP request. + unsigned int getReplyStatus() const + { + return mReplyStatus; + } + + void setReplyStatus(unsigned int status) + { + mReplyStatus = status; + } + + /// Simple getter for the response body returned as a scatter/gather + /// buffer. If the operation doesn't produce data (such as the Null + /// or StopThread operations), this may be NULL. + /// + /// Caller can hold onto the response by incrementing the reference + /// count of the returned object. + BufferArray * getBody() const + { + return mBufferArray; + } + + /// Set the response data in the instance. Will drop the reference + /// count to any existing data and increment the count of that passed + /// in. It is legal to set the data to NULL. + void setBody(BufferArray * ba); + + /// And a getter for the headers. And as with @see getResponse(), + /// if headers aren't available because the operation doesn't produce + /// any or delivery of headers wasn't requested in the options, this + /// will be NULL. + /// + /// Caller can hold onto the headers by incrementing the reference + /// count of the returned object. + HttpHeaders * getHeaders() const + { + return mHeaders; + } + + /// Behaves like @see setResponse() but for header data. + void setHeaders(HttpHeaders * headers); + +protected: + // Response data here + HttpStatus mStatus; + unsigned int mReplyStatus; + BufferArray * mBufferArray; + HttpHeaders * mHeaders; +}; + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_RESPONSE_H_ diff --git a/indra/llcorehttp/tests/all_test.cpp b/indra/llcorehttp/tests/all_test.cpp new file mode 100644 index 0000000000..636f6f8c05 --- /dev/null +++ b/indra/llcorehttp/tests/all_test.cpp @@ -0,0 +1,64 @@ +/** + * @file test_all + * @brief Main test runner + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#include + +#include +#include + +#include + +// Pull in each of the test sets +#include "test_httpstatus.hpp" +#include "test_refcounted.hpp" +#include "test_httpoperation.hpp" +#include "test_httprequest.hpp" +#include "test_httpheaders.hpp" +#include "test_bufferarray.hpp" +#include "test_httprequestqueue.hpp" + + +namespace tut +{ + test_runner_singleton runner; +} + +int main() +{ + curl_global_init(CURL_GLOBAL_ALL); + + // *FIXME: Need threaded/SSL curl setup here. + + tut::reporter reporter; + + tut::runner.get().set_callback(&reporter); + tut::runner.get().run_tests(); + return !reporter.all_ok(); + + curl_global_cleanup(); +} + diff --git a/indra/llcorehttp/tests/test_allocator.cpp b/indra/llcorehttp/tests/test_allocator.cpp new file mode 100644 index 0000000000..926de0a208 --- /dev/null +++ b/indra/llcorehttp/tests/test_allocator.cpp @@ -0,0 +1,178 @@ +/** + * @file test_allocator.cpp + * @brief quick and dirty allocator for tracking memory allocations + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "test_allocator.h" + +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 +#include +#elif defined(_MSC_VER) +#include +#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ ) > 40100 +// atomic extensions are built into GCC on posix platforms +#endif + +#include +#include +#include +#include +#include +#include + +#include + + +#if defined(WIN32) +#define THROW_BAD_ALLOC() _THROW1(std::bad_alloc) +#define THROW_NOTHING() _THROW0() +#else +#define THROW_BAD_ALLOC() throw(std::bad_alloc) +#define THROW_NOTHING() throw() +#endif + + +struct BlockHeader +{ + struct Block * next; + std::size_t size; + bool in_use; +}; + +struct Block +{ + BlockHeader hdr; + unsigned char data[1]; +}; + +#define TRACE_MSG(val) std::cout << __FUNCTION__ << "(" << val << ") [" << __FILE__ << ":" << __LINE__ << "]" << std::endl; + +static unsigned char MemBuf[ 4096 * 1024 ]; +Block * pNext = static_cast(static_cast(MemBuf)); +volatile std::size_t MemTotal = 0; + +// cross-platform compare and swap operation +static bool CAS(void * volatile * ptr, void * expected, void * new_value) +{ +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 + return OSAtomicCompareAndSwapPtr( expected, new_value, ptr ); +#elif defined(_MSC_VER) + return expected == InterlockedCompareExchangePointer( ptr, new_value, expected ); +#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ ) > 40100 + return __sync_bool_compare_and_swap( ptr, expected, new_value ); +#endif +} + +static void * GetMem(std::size_t size) +{ + // TRACE_MSG(size); + volatile Block * pBlock = NULL; + volatile Block * pNewNext = NULL; + + // do a lock-free update of the global next pointer + do + { + pBlock = pNext; + pNewNext = (volatile Block *)(pBlock->data + size); + + } while(! CAS((void * volatile *) &pNext, (void *) pBlock, (void *) pNewNext)); + + // if we get here, we safely carved out a block of memory in the + // memory pool... + + // initialize our block + pBlock->hdr.next = (Block *)(pBlock->data + size); + pBlock->hdr.size = size; + pBlock->hdr.in_use = true; + memset((void *) pBlock->data, 0, pBlock->hdr.size); + + // do a lock-free update of the global memory total + volatile size_t total = 0; + volatile size_t new_total = 0; + do + { + total = MemTotal; + new_total = total + size; + + } while (! CAS((void * volatile *) &MemTotal, (void *) total, (void *) new_total)); + + return (void *) pBlock->data; +} + + +static void FreeMem(void * p) +{ + // get the pointer to the block record + Block * pBlock = (Block *)((unsigned char *) p - sizeof(BlockHeader)); + + // TRACE_MSG(pBlock->hdr.size); + bool * cur_in_use = &(pBlock->hdr.in_use); + volatile bool in_use = false; + bool new_in_use = false; + do + { + in_use = pBlock->hdr.in_use; + } while (! CAS((void * volatile *) cur_in_use, (void *) in_use, (void *) new_in_use)); + + // do a lock-free update of the global memory total + volatile size_t total = 0; + volatile size_t new_total = 0; + do + { + total = MemTotal; + new_total = total - pBlock->hdr.size; + } while (! CAS((void * volatile *)&MemTotal, (void *) total, (void *) new_total)); +} + + +std::size_t GetMemTotal() +{ + return MemTotal; +} + + +void * operator new(std::size_t size) THROW_BAD_ALLOC() +{ + return GetMem( size ); +} + + +void * operator new[](std::size_t size) THROW_BAD_ALLOC() +{ + return GetMem( size ); +} + + +void operator delete(void * p) THROW_NOTHING() +{ + FreeMem( p ); +} + + +void operator delete[](void * p) THROW_NOTHING() +{ + FreeMem( p ); +} + + diff --git a/indra/llcorehttp/tests/test_allocator.h b/indra/llcorehttp/tests/test_allocator.h new file mode 100644 index 0000000000..3572bbc5c5 --- /dev/null +++ b/indra/llcorehttp/tests/test_allocator.h @@ -0,0 +1,47 @@ +/** + * @file test_allocator.h + * @brief quick and dirty allocator for tracking memory allocations + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef TEST_ALLOCATOR_H +#define TEST_ALLOCATOR_H + +#include +#include + +size_t GetMemTotal(); +#if defined(WIN32) +void * operator new(std::size_t size) _THROW1(std::bad_alloc); +void * operator new[](std::size_t size) _THROW1(std::bad_alloc); +void operator delete(void * p) _THROW0(); +void operator delete[](void * p) _THROW0(); +#else +void * operator new(std::size_t size) throw (std::bad_alloc); +void * operator new[](std::size_t size) throw (std::bad_alloc); +void operator delete(void * p) throw (); +void operator delete[](void * p) throw (); +#endif + +#endif // TEST_ALLOCATOR_H + diff --git a/indra/llcorehttp/tests/test_bufferarray.hpp b/indra/llcorehttp/tests/test_bufferarray.hpp new file mode 100644 index 0000000000..4f5d0284a1 --- /dev/null +++ b/indra/llcorehttp/tests/test_bufferarray.hpp @@ -0,0 +1,456 @@ +/** + * @file test_bufferarray.hpp + * @brief unit tests for the LLCore::BufferArray class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_BUFFER_ARRAY_H_ +#define TEST_LLCORE_BUFFER_ARRAY_H_ + +#include + +#include + +#include "test_allocator.h" + + +using namespace LLCoreInt; + + + +namespace tut +{ + +struct BufferArrayTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; +}; + +typedef test_group BufferArrayTestGroupType; +typedef BufferArrayTestGroupType::object BufferArrayTestObjectType; +BufferArrayTestGroupType BufferArrayTestGroup("BufferArray Tests"); + +template <> template <> +void BufferArrayTestObjectType::test<1>() +{ + set_test_name("BufferArray construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + ensure("One ref on construction of BufferArray", ba->getRefCount() == 1); + ensure("Memory being used", mMemTotal < GetMemTotal()); + ensure("Nothing in BA", 0 == ba->size()); + + // Try to read + char buffer[20]; + size_t read_len(ba->read(buffer, sizeof(buffer))); + ensure("Read returns empty", 0 == read_len); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<2>() +{ + set_test_name("BufferArray single write"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + char buffer[256]; + + size_t len = ba->write(str1, strlen(str1)); + ensure("Wrote length correct", strlen(str1) == len); + ensure("Recorded size correct", strlen(str1) == ba->size()); + + // read some data back + len = ba->seek(2); + ensure("Seek worked", 2 == len); + + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(buffer, 2); + ensure("Read length correct", 2 == len); + ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]); + ensure("Read didn't overwrite", 'X' == buffer[2]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferArrayTestObjectType::test<3>() +{ + set_test_name("BufferArray multiple writes"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char buffer[256]; + + size_t len = ba->write(str1, str1_len); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", str1_len == ba->size()); + + // again... + len = ba->write(str1, strlen(str1)); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", (2 * str1_len) == ba->size()); + + // read some data back + len = ba->seek(8); + ensure("Seek worked", 8 == len); + + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(buffer, 4); + ensure("Read length correct", 4 == len); + ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]); + ensure("Read content correct", 'a' == buffer[2] && 'b' == buffer[3]); + ensure("Read didn't overwrite", 'X' == buffer[4]); + + // Read whole thing + len = ba->seek(0); + ensure("Seek worked (2)", 0 == len); + + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(buffer, sizeof(buffer)); + ensure("Read length correct", (2 * str1_len) == len); + ensure("Read content correct (3)", 0 == strncmp(buffer, str1, str1_len)); + ensure("Read content correct (4)", 0 == strncmp(&buffer[str1_len], str1, str1_len)); + ensure("Read didn't overwrite (5)", 'X' == buffer[2 * str1_len]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<4>() +{ + set_test_name("BufferArray overwriting"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char str2[] = "ABCDEFGHIJ"; + size_t str2_len(strlen(str2)); + char buffer[256]; + + size_t len = ba->write(str1, str1_len); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", str1_len == ba->size()); + + // again... + len = ba->write(str1, strlen(str1)); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", (2 * str1_len) == ba->size()); + + // reposition and overwrite + len = ba->seek(8); + ensure("Seek worked", 8 == len); + len = ba->write(str2, 4); + ensure("Overwrite length correct", 4 == len); + + // Leave position and read verifying content + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(buffer, 4); + ensure("Read length correct", 4 == len); + ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]); + ensure("Read content correct.2", 'e' == buffer[2] && 'f' == buffer[3]); + ensure("Read didn't overwrite", 'X' == buffer[4]); + + // reposition and check + len = ba->seek(6); + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(buffer, 8); + ensure("Read length correct.2", 8 == len); + ensure("Read content correct.3", 'g' == buffer[0] && 'h' == buffer[1]); + ensure("Read content correct.4", 'A' == buffer[2] && 'B' == buffer[3]); + ensure("Read content correct.5", 'C' == buffer[4] && 'D' == buffer[5]); + ensure("Read content correct.6", 'c' == buffer[6] && 'd' == buffer[7]); + ensure("Read didn't overwrite.7", 'X' == buffer[8]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<5>() +{ + set_test_name("BufferArray multiple writes - sequential reads"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char buffer[256]; + + size_t len = ba->write(str1, str1_len); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", str1_len == ba->size()); + + // again... + len = ba->write(str1, strlen(str1)); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", (2 * str1_len) == ba->size()); + + // read some data back + len = ba->seek(8); + ensure("Seek worked", 8 == len); + + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(buffer, 4); + ensure("Read length correct", 4 == len); + ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]); + ensure("Read content correct.2", 'a' == buffer[2] && 'b' == buffer[3]); + ensure("Read didn't overwrite", 'X' == buffer[4]); + + // Read some more without repositioning + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(buffer, sizeof(buffer)); + ensure("Read length correct", (str1_len - 2) == len); + ensure("Read content correct.3", 0 == strncmp(buffer, str1+2, str1_len-2)); + ensure("Read didn't overwrite.2", 'X' == buffer[str1_len-1]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<6>() +{ + set_test_name("BufferArray overwrite spanning blocks and appending"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char str2[] = "ABCDEFGHIJKLMNOPQRST"; + size_t str2_len(strlen(str2)); + char buffer[256]; + + size_t len = ba->write(str1, str1_len); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", str1_len == ba->size()); + + // again... + len = ba->write(str1, strlen(str1)); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", (2 * str1_len) == ba->size()); + + // reposition and overwrite + len = ba->seek(8); + ensure("Seek worked", 8 == len); + len = ba->write(str2, str2_len); + ensure("Overwrite length correct", str2_len == len); + + // Leave position and read verifying content + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(buffer, 0); + ensure("Read length correct", 0 == len); + ensure("Read didn't overwrite", 'X' == buffer[0]); + + // reposition and check + len = ba->seek(0); + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(buffer, sizeof(buffer)); + ensure("Read length correct.2", (str1_len + str2_len - 2) == len); + ensure("Read content correct", 0 == strncmp(buffer, str1, str1_len-2)); + ensure("Read content correct.2", 0 == strncmp(buffer+str1_len-2, str2, str2_len)); + ensure("Read didn't overwrite.2", 'X' == buffer[str1_len + str2_len - 2]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure("All memory released", mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<7>() +{ + set_test_name("BufferArray overwrite spanning blocks and sequential writes"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char str2[] = "ABCDEFGHIJKLMNOPQRST"; + size_t str2_len(strlen(str2)); + char buffer[256]; + + // 2x str1 + size_t len = ba->write(str1, str1_len); + len = ba->write(str1, strlen(str1)); + + // reposition and overwrite + len = ba->seek(6); + len = ba->write(str2, 2); + ensure("Overwrite length correct", 2 == len); + + len = ba->write(str2, 2); + ensure("Overwrite length correct.2", 2 == len); + + len = ba->write(str2, 2); + ensure("Overwrite length correct.3", 2 == len); + + // append some data + len = ba->append(str2, str2_len); + ensure("Append length correct", str2_len == len); + + // append some more + char * out_buf(ba->appendBufferAlloc(str1_len)); + memcpy(out_buf, str1, str1_len); + + // And some final writes + len = ba->write(str2, 2); + ensure("Write length correct.2", 2 == len); + + // Check contents + memset(buffer, 'X', sizeof(buffer)); + ba->seek(0); + len = ba->read(buffer, sizeof(buffer)); + ensure("Final buffer length correct", (3 * str1_len + str2_len + 2) == len); + ensure("Read content correct", 0 == strncmp(buffer, str1, 6)); + ensure("Read content correct.2", 0 == strncmp(buffer + 6, str2, 2)); + ensure("Read content correct.3", 0 == strncmp(buffer + 8, str2, 2)); + ensure("Read content correct.4", 0 == strncmp(buffer + 10, str2, 2)); + ensure("Read content correct.5", 0 == strncmp(buffer + str1_len + 2, str1 + 2, str1_len - 2)); + ensure("Read content correct.6", 0 == strncmp(buffer + str1_len + str1_len, str2, str2_len)); + ensure("Read content correct.7", 0 == strncmp(buffer + str1_len + str1_len + str2_len, str1, str1_len)); + ensure("Read content correct.8", 0 == strncmp(buffer + str1_len + str1_len + str2_len + str1_len, str2, 2)); + ensure("Read didn't overwrite", 'X' == buffer[str1_len + str1_len + str2_len + str1_len + 2]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure("All memory released", mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<8>() +{ + set_test_name("BufferArray zero-length appendBufferAlloc"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char str2[] = "ABCDEFGHIJKLMNOPQRST"; + size_t str2_len(strlen(str2)); + char buffer[256]; + + // 2x str1 + size_t len = ba->write(str1, str1_len); + len = ba->write(str1, strlen(str1)); + + // zero-length allocate (we allow this with a valid pointer returned) + char * out_buf(ba->appendBufferAlloc(0)); + ensure("Buffer from zero-length appendBufferAlloc non-NULL", NULL != out_buf); + + // Do it again + char * out_buf2(ba->appendBufferAlloc(0)); + ensure("Buffer from zero-length appendBufferAlloc non-NULL.2", NULL != out_buf2); + ensure("Two zero-length appendBufferAlloc buffers distinct", out_buf != out_buf2); + + // And some final writes + len = ba->write(str2, str2_len); + + // Check contents + memset(buffer, 'X', sizeof(buffer)); + ba->seek(0); + len = ba->read(buffer, sizeof(buffer)); + ensure("Final buffer length correct", (2 * str1_len + str2_len) == len); + ensure("Read content correct.1", 0 == strncmp(buffer, str1, str1_len)); + ensure("Read content correct.2", 0 == strncmp(buffer + str1_len, str1, str1_len)); + ensure("Read content correct.3", 0 == strncmp(buffer + str1_len + str1_len, str2, str2_len)); + ensure("Read didn't overwrite", 'X' == buffer[str1_len + str1_len + str2_len]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure("All memory released", mMemTotal == GetMemTotal()); +} + +} // end namespace tut + + +#endif // TEST_LLCORE_BUFFER_ARRAY_H_ diff --git a/indra/llcorehttp/tests/test_httpheaders.hpp b/indra/llcorehttp/tests/test_httpheaders.hpp new file mode 100644 index 0000000000..d22b516691 --- /dev/null +++ b/indra/llcorehttp/tests/test_httpheaders.hpp @@ -0,0 +1,108 @@ +/** + * @file test_httpheaders.hpp + * @brief unit tests for the LLCore::HttpHeaders class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_HTTP_HEADERS_H_ +#define TEST_LLCORE_HTTP_HEADERS_H_ + +#include + +#include + +#include "test_allocator.h" + + +using namespace LLCoreInt; + + + +namespace tut +{ + +struct HttpHeadersTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; +}; + +typedef test_group HttpHeadersTestGroupType; +typedef HttpHeadersTestGroupType::object HttpHeadersTestObjectType; +HttpHeadersTestGroupType HttpHeadersTestGroup("HttpHeaders Tests"); + +template <> template <> +void HttpHeadersTestObjectType::test<1>() +{ + set_test_name("HttpHeaders construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpHeaders * headers = new HttpHeaders(); + ensure("One ref on construction of HttpHeaders", headers->getRefCount() == 1); + ensure("Memory being used", mMemTotal < GetMemTotal()); + ensure("Nothing in headers", 0 == headers->mHeaders.size()); + + // release the implicit reference, causing the object to be released + headers->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpHeadersTestObjectType::test<2>() +{ + set_test_name("HttpHeaders construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpHeaders * headers = new HttpHeaders(); + + { + // Append a few strings + std::string str1("Pragma:"); + headers->mHeaders.push_back(str1); + std::string str2("Accept: application/json"); + headers->mHeaders.push_back(str2); + + ensure("Headers retained", 2 == headers->mHeaders.size()); + ensure("First is first", headers->mHeaders[0] == str1); + ensure("Second is second", headers->mHeaders[1] == str2); + } + + // release the implicit reference, causing the object to be released + headers->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +} // end namespace tut + + +#endif // TEST_LLCORE_HTTP_HEADERS_H_ diff --git a/indra/llcorehttp/tests/test_httpoperation.hpp b/indra/llcorehttp/tests/test_httpoperation.hpp new file mode 100644 index 0000000000..c9feaddb2a --- /dev/null +++ b/indra/llcorehttp/tests/test_httpoperation.hpp @@ -0,0 +1,128 @@ +/** + * @file test_httpoperation.hpp + * @brief unit tests for the LLCore::HttpOperation-derived classes + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_HTTP_OPERATION_H_ +#define TEST_LLCORE_HTTP_OPERATION_H_ + +#include "_httpoperation.h" +#include + +#include + +#include "test_allocator.h" + + +using namespace LLCoreInt; + + +namespace +{ + +class TestHandler : public LLCore::HttpHandler +{ +public: + virtual void onCompleted(HttpHandle, HttpResponse *) + { + std::cout << "TestHandler::onCompleted() invoked" << std::endl; + } + +}; + + +} // end namespace anonymous + + +namespace tut +{ + struct HttpOperationTestData + { + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; + }; + + typedef test_group HttpOperationTestGroupType; + typedef HttpOperationTestGroupType::object HttpOperationTestObjectType; + HttpOperationTestGroupType HttpOperationTestGroup("HttpOperation Tests"); + + template <> template <> + void HttpOperationTestObjectType::test<1>() + { + set_test_name("HttpOpNull construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpOpNull * op = new HttpOpNull(); + ensure(op->getRefCount() == 1); + ensure(mMemTotal < GetMemTotal()); + + // release the implicit reference, causing the object to be released + op->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void HttpOperationTestObjectType::test<2>() + { + set_test_name("HttpOpNull construction with handlers"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // Get some handlers + TestHandler * h1 = new TestHandler(); + TestHandler * h2 = new TestHandler(); + + // create a new ref counted object with an implicit reference + HttpOpNull * op = new HttpOpNull(); + + // Add the handlers + op->setHandlers(NULL, h1, h2); + + // Check ref count + ensure(op->getRefCount() == 1); + + // release the reference, releasing the operation but + // not the handlers. + op->release(); + op = NULL; + ensure(mMemTotal != GetMemTotal()); + + // release the handlers + delete h1; + h1 = NULL; + delete h2; + h2 = NULL; + + ensure(mMemTotal == GetMemTotal()); + } + +} + +#endif // TEST_LLCORE_HTTP_OPERATION_H_ diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp new file mode 100644 index 0000000000..df5640859f --- /dev/null +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -0,0 +1,437 @@ +/** + * @file test_httprequest.hpp + * @brief unit tests for the LLCore::HttpRequest class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_HTTP_REQUEST_H_ +#define TEST_LLCORE_HTTP_REQUEST_H_ + +#include +#include +#include +#include +#include + +#include + +#include "test_allocator.h" + + +using namespace LLCoreInt; + + +namespace +{ + +#if defined(WIN32) + +void usleep(unsigned long usec); + +#endif + +} + +namespace tut +{ + +struct HttpRequestTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; + int mHandlerCalls; + HttpStatus mStatus; +}; + +class TestHandler2 : public LLCore::HttpHandler +{ +public: + TestHandler2(HttpRequestTestData * state, + const std::string & name) + : mState(state), + mName(name), + mExpectHandle(LLCORE_HTTP_HANDLE_INVALID) + {} + + virtual void onCompleted(HttpHandle handle, HttpResponse * response) + { + if (LLCORE_HTTP_HANDLE_INVALID != mExpectHandle) + { + ensure("Expected handle received in handler", mExpectHandle == handle); + } + ensure("Handler got a response", NULL != response); + if (response && mState) + { + const HttpStatus actual_status(response->getStatus()); + + ensure("Expected HttpStatus received in response", actual_status == mState->mStatus); + } + if (mState) + { + mState->mHandlerCalls++; + } + // std::cout << "TestHandler2::onCompleted() invoked" << std::endl; + } + + HttpRequestTestData * mState; + std::string mName; + HttpHandle mExpectHandle; +}; + +typedef test_group HttpRequestTestGroupType; +typedef HttpRequestTestGroupType::object HttpRequestTestObjectType; +HttpRequestTestGroupType HttpRequestTestGroup("HttpRequest Tests"); + +template <> template <> +void HttpRequestTestObjectType::test<1>() +{ + set_test_name("HttpRequest construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // Get singletons created + HttpRequest::createService(); + + // create a new ref counted object with an implicit reference + HttpRequest * req = new HttpRequest(); + ensure(mMemTotal < GetMemTotal()); + + // release the request object + delete req; + req = NULL; + + HttpRequest::destroyService(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestTestObjectType::test<2>() +{ + set_test_name("HttpRequest and Null Op queued"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // Get singletons created + HttpRequest::createService(); + + // create a new ref counted object with an implicit reference + HttpRequest * req = new HttpRequest(); + ensure(mMemTotal < GetMemTotal()); + + // Issue a NoOp + HttpHandle handle = req->requestNoOp(NULL); + ensure(handle != LLCORE_HTTP_HANDLE_INVALID); + + // release the request object + delete req; + req = NULL; + + // We're still holding onto the operation which is + // sitting, unserviced, on the request queue so... + ensure(mMemTotal < GetMemTotal()); + + // Request queue should have two references: global singleton & service object + ensure("Two references to request queue", 2 == HttpRequestQueue::instanceOf()->getRefCount()); + + // Okay, tear it down + HttpRequest::destroyService(); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure(mMemTotal == GetMemTotal()); +} + + +template <> template <> +void HttpRequestTestObjectType::test<3>() +{ + set_test_name("HttpRequest NoOp + Stop execution"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + HttpRequest * req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a NoOp + HttpHandle handle = req->requestNoOp(&handler); + ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(20); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + // We have left over state so can't really say something + // definitive about memory usage at the end of this. + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestTestObjectType::test<4>() +{ + set_test_name("2 HttpRequest instances, one thread"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + TestHandler2 handler1(this, "handler1"); + TestHandler2 handler2(this, "handler2"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + HttpRequest * req1 = new HttpRequest(); + HttpRequest * req2 = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue some NoOps + HttpHandle handle = req1->requestNoOp(&handler1); + ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + handler1.mExpectHandle = handle; + + handle = req2->requestNoOp(&handler2); + ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + handler2.mExpectHandle = handle; + + // Run the notification pump. + int count(0); + int limit(20); + while (count++ < limit && mHandlerCalls < 2) + { + req1->update(1000); + req2->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 2); + + // Okay, request a shutdown of the servicing thread + handle = req2->requestStopThread(&handler2); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + handler2.mExpectHandle = handle; + + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 3) + { + req1->update(1000); + req2->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 3); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req1; + req1 = NULL; + delete req2; + req2 = NULL; + + // Shut down service + HttpRequest::destroyService(); + + // We have left over state so can't really say something + // definitive about memory usage at the end of this. + ensure("Two handler calls on the way out", 3 == mHandlerCalls); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestTestObjectType::test<5>() +{ + set_test_name("HttpRequest GET + Stop execution"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + HttpRequest * req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that can't connect + mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0.0f, + "http://localhost:2/nothing/here", + 0, + 0, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(20); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + // We have left over state so can't really say something + // definitive about memory usage at the end of this. + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +} + +} // end namespace tut + +namespace +{ + +#if defined(WIN32) + +void usleep(unsigned long usec) +{ + Sleep((DWORD) (usec / 1000UL)); +} + +#endif + +} + +#endif // TEST_LLCORE_HTTP_REQUEST_H_ diff --git a/indra/llcorehttp/tests/test_httprequestqueue.hpp b/indra/llcorehttp/tests/test_httprequestqueue.hpp new file mode 100644 index 0000000000..1de2d8f9ab --- /dev/null +++ b/indra/llcorehttp/tests/test_httprequestqueue.hpp @@ -0,0 +1,186 @@ +/** + * @file test_httprequestqueue.hpp + * @brief unit tests for the LLCore::HttpRequestQueue class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_HTTP_REQUESTQUEUE_H_ +#define TEST_LLCORE_HTTP_REQUESTQUEUE_H_ + +#include "_httprequestqueue.h" + +#include + +#include "test_allocator.h" +#include "_httpoperation.h" + + +using namespace LLCoreInt; + + + +namespace tut +{ + +struct HttpRequestqueueTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; +}; + +typedef test_group HttpRequestqueueTestGroupType; +typedef HttpRequestqueueTestGroupType::object HttpRequestqueueTestObjectType; +HttpRequestqueueTestGroupType HttpRequestqueueTestGroup("HttpRequestqueue Tests"); + +template <> template <> +void HttpRequestqueueTestObjectType::test<1>() +{ + set_test_name("HttpRequestQueue construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpRequestQueue::init(); + + ensure("One ref on construction of HttpRequestQueue", HttpRequestQueue::instanceOf()->getRefCount() == 1); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // release the implicit reference, causing the object to be released + HttpRequestQueue::term(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestqueueTestObjectType::test<2>() +{ + set_test_name("HttpRequestQueue refcount works"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpRequestQueue::init(); + + HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + rq->addRef(); + + // release the singleton, hold on to the object + HttpRequestQueue::term(); + + ensure("One ref after term() called", rq->getRefCount() == 1); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // Drop ref + rq->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestqueueTestObjectType::test<3>() +{ + set_test_name("HttpRequestQueue addOp/fetchOp work"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpRequestQueue::init(); + + HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + + HttpOperation * op = new HttpOpNull(); + + rq->addOp(op); // transfer my refcount + + op = rq->fetchOp(true); // Potentially hangs the test on failure + ensure("One goes in, one comes out", NULL != op); + op->release(); + + op = rq->fetchOp(false); + ensure("Better not be two of them", NULL == op); + + // release the singleton, hold on to the object + HttpRequestQueue::term(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestqueueTestObjectType::test<4>() +{ + set_test_name("HttpRequestQueue addOp/fetchAll work"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpRequestQueue::init(); + + HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + + HttpOperation * op = new HttpOpNull(); + rq->addOp(op); // transfer my refcount + + op = new HttpOpNull(); + rq->addOp(op); // transfer my refcount + + op = new HttpOpNull(); + rq->addOp(op); // transfer my refcount + + { + HttpRequestQueue::OpContainer ops; + rq->fetchAll(true, ops); // Potentially hangs the test on failure + ensure("Three go in, three come out", 3 == ops.size()); + + op = rq->fetchOp(false); + ensure("Better not be any more of them", NULL == op); + + // release the singleton, hold on to the object + HttpRequestQueue::term(); + + // We're still holding onto the ops. + ensure(mMemTotal < GetMemTotal()); + + // Release them + while (! ops.empty()) + { + HttpOperation * op = ops.front(); + ops.erase(ops.begin()); + op->release(); + } + } + + // Should be clean + ensure("All memory returned", mMemTotal == GetMemTotal()); +} + +} // end namespace tut + + +#endif // TEST_LLCORE_HTTP_REQUESTQUEUE_H_ diff --git a/indra/llcorehttp/tests/test_httpstatus.hpp b/indra/llcorehttp/tests/test_httpstatus.hpp new file mode 100644 index 0000000000..fa5803a17c --- /dev/null +++ b/indra/llcorehttp/tests/test_httpstatus.hpp @@ -0,0 +1,163 @@ +/** + * @file test_llrefcounted + * @brief unit tests for HttpStatus struct + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef TEST_HTTP_STATUS_H_ +#define TEST_HTTP_STATUS_H_ + +#include + +#include +#include + +using namespace LLCore; + +namespace tut +{ + +struct HttpStatusTestData +{ + HttpStatusTestData() + {} +}; + +typedef test_group HttpStatusTestGroupType; +typedef HttpStatusTestGroupType::object HttpStatusTestObjectType; + +HttpStatusTestGroupType HttpStatusTestGroup("HttpStatus Tests"); + +template <> template <> +void HttpStatusTestObjectType::test<1>() +{ + set_test_name("HttpStatus construction"); + + // auto allocation fine for this + HttpStatus status; + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = 0; + + ensure(bool(status)); + ensure(false == !(status)); + + status.mType = HttpStatus::EXT_CURL_MULTI; + status.mStatus = 0; + + ensure(bool(status)); + ensure(false == !(status)); + + status.mType = HttpStatus::LLCORE; + status.mStatus = HE_SUCCESS; + + ensure(bool(status)); + ensure(false == !(status)); + + status.mType = HttpStatus::EXT_CURL_MULTI; + status.mStatus = -1; + + ensure(false == bool(status)); + ensure(!(status)); + + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = CURLE_BAD_DOWNLOAD_RESUME; + + ensure(false == bool(status)); + ensure(!(status)); +} + + +template <> template <> +void HttpStatusTestObjectType::test<2>() +{ + set_test_name("HttpStatus memory structure"); + + // Require that an HttpStatus object can be trivially + // returned as a function return value in registers. + // One should fit in an int on all platforms. + + ensure(sizeof(HttpStatus) <= sizeof(int)); +} + + +template <> template <> +void HttpStatusTestObjectType::test<3>() +{ + set_test_name("HttpStatus valid error string conversion"); + + HttpStatus status; + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = 0; + std::string msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(msg.empty()); + + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = CURLE_BAD_FUNCTION_ARGUMENT; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); + + status.mType = HttpStatus::EXT_CURL_MULTI; + status.mStatus = CURLM_OUT_OF_MEMORY; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); + + status.mType = HttpStatus::LLCORE; + status.mStatus = HE_SHUTTING_DOWN; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); +} + + +template <> template <> +void HttpStatusTestObjectType::test<4>() +{ + set_test_name("HttpStatus invalid error string conversion"); + + HttpStatus status; + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = 32726; + std::string msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); + + status.mType = HttpStatus::EXT_CURL_MULTI; + status.mStatus = -470; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); + + status.mType = HttpStatus::LLCORE; + status.mStatus = 923; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); +} + +} // end namespace tut + +#endif // TEST_HTTP_STATUS_H + diff --git a/indra/llcorehttp/tests/test_refcounted.hpp b/indra/llcorehttp/tests/test_refcounted.hpp new file mode 100644 index 0000000000..6a17947288 --- /dev/null +++ b/indra/llcorehttp/tests/test_refcounted.hpp @@ -0,0 +1,156 @@ +/** + * @file test_refcounted.hpp + * @brief unit tests for the LLCoreInt::RefCounted class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCOREINT_REF_COUNTED_H_ +#define TEST_LLCOREINT_REF_COUNTED_H_ + +#include <_refcounted.h> + +#include "test_allocator.h" + +using namespace LLCoreInt; + +namespace tut +{ + struct RefCountedTestData + { + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; + }; + + typedef test_group RefCountedTestGroupType; + typedef RefCountedTestGroupType::object RefCountedTestObjectType; + RefCountedTestGroupType RefCountedTestGroup("RefCounted Tests"); + + template <> template <> + void RefCountedTestObjectType::test<1>() + { + set_test_name("RefCounted construction with implicit count"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + RefCounted * rc = new RefCounted(true); + ensure(rc->getRefCount() == 1); + + // release the implicit reference, causing the object to be released + rc->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void RefCountedTestObjectType::test<2>() + { + set_test_name("RefCounted construction without implicit count"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + RefCounted * rc = new RefCounted(false); + ensure(rc->getRefCount() == 0); + + // add a reference + rc->addRef(); + ensure(rc->getRefCount() == 1); + + // release the implicit reference, causing the object to be released + rc->release(); + + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void RefCountedTestObjectType::test<3>() + { + set_test_name("RefCounted addRef and release"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + RefCounted * rc = new RefCounted(false); + + for (int i = 0; i < 1024; ++i) + { + rc->addRef(); + } + + ensure(rc->getRefCount() == 1024); + + for (int i = 0; i < 1024; ++i) + { + rc->release(); + } + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void RefCountedTestObjectType::test<4>() + { + set_test_name("RefCounted isLastRef check"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + RefCounted * rc = new RefCounted(true); + + // with only one reference, isLastRef should be true + ensure(rc->isLastRef()); + + // release it to clean up memory + rc->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void RefCountedTestObjectType::test<5>() + { + set_test_name("RefCounted noRef check"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + RefCounted * rc = new RefCounted(false); + + // set the noRef + rc->noRef(); + + // with only one reference, isLastRef should be true + ensure(rc->getRefCount() == RefCounted::NOT_REF_COUNTED); + + // allow this memory leak, but check that we're leaking a known amount + ensure(mMemTotal == (GetMemTotal() - sizeof(RefCounted))); + } +} + +#endif // TEST_LLCOREINT_REF_COUNTED_H_ -- cgit v1.2.3 From c09669da921f24e8e2693d8652fc56465d5fa19c Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 23 Apr 2012 16:20:45 -0400 Subject: And forgot the .cmake library file. --- indra/cmake/LLCoreHttp.cmake | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 indra/cmake/LLCoreHttp.cmake diff --git a/indra/cmake/LLCoreHttp.cmake b/indra/cmake/LLCoreHttp.cmake new file mode 100644 index 0000000000..280189da3d --- /dev/null +++ b/indra/cmake/LLCoreHttp.cmake @@ -0,0 +1,14 @@ +# -*- cmake -*- + +include(CARes) +include(CURL) +include(OpenSSL) + +set(LLCOREHTTP_INCLUDE_DIRS + ${LIBS_OPEN_DIR}/llcorehttp + ${CARES_INCLUDE_DIRS} + ${CURL_INCLUDE_DIRS} + ${OPENSSL_INCLUDE_DIRS} + ) + +set(LLCOREHTTP_LIBRARIES llcorehttp) -- cgit v1.2.3 From 88bc54192fa274bb1617801e4aeddbbf4e9e7944 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 25 Apr 2012 13:18:27 -0400 Subject: Get Mac building. Unused variable in boost and missing return value which wasn't caught in other environments. --- indra/llcorehttp/CMakeLists.txt | 5 +++++ indra/llcorehttp/_thread.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 8d21007ca0..b3f2d8234c 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -58,6 +58,11 @@ set(llcorehttp_HEADER_FILES set_source_files_properties(${llcorehttp_HEADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) +if (APPLE) + # Boost headers define unused members so... + set_source_files_properties(${llcorehttp_SOURCE_FILES} + PROPERTIES COMPILE_FLAGS -Wno-unused-variable) +endif (APPLE) list(APPEND llcorehttp_SOURCE_FILES ${llcorehttp_HEADER_FILES}) diff --git a/indra/llcorehttp/_thread.h b/indra/llcorehttp/_thread.h index 5960f0dcdb..0937d698c7 100644 --- a/indra/llcorehttp/_thread.h +++ b/indra/llcorehttp/_thread.h @@ -91,7 +91,7 @@ public: inline bool joinable() const { - mThread->joinable(); + return mThread->joinable(); } private: -- cgit v1.2.3 From a33bec1e5ef5e399f41267f06dc6d355dd34efe6 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 25 Apr 2012 14:43:31 -0400 Subject: Build tweak for linux which has the same boost/unused variable problem as Mac. --- indra/llcorehttp/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index b3f2d8234c..e0d1817781 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -58,11 +58,11 @@ set(llcorehttp_HEADER_FILES set_source_files_properties(${llcorehttp_HEADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) -if (APPLE) - # Boost headers define unused members so... +if (DARWIN OR LINUX) + # Boost headers define unused members in condition_variable so... set_source_files_properties(${llcorehttp_SOURCE_FILES} PROPERTIES COMPILE_FLAGS -Wno-unused-variable) -endif (APPLE) +endif (DARWIN OR LINUX) list(APPEND llcorehttp_SOURCE_FILES ${llcorehttp_HEADER_FILES}) -- cgit v1.2.3 From a54e9c379550f7901404990ea7fddb99c0188048 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 25 Apr 2012 15:47:30 -0400 Subject: Another fix for Mac warnings. Uninitialized auto check. Not an actual problem but this will quiet the compiler. --- indra/llcorehttp/_httpoprequest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index d71ace5d57..316df8bd49 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -410,7 +410,7 @@ int parse_content_range_header(char * buffer, unsigned int * last, unsigned int * length) { - char * tok_state(NULL), * tok; + char * tok_state(NULL), * tok(NULL); bool match(true); if (! strtok_r(buffer, ": \t", &tok_state)) -- cgit v1.2.3 From 438a6431e418eac5a3a4e00f7adfe379994869d7 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 26 Apr 2012 11:35:07 -0400 Subject: Bring llcorehttp into the compile and link phases. Windows looks okay though it's a dead library so far. --- indra/llcorehttp/httpcommon.h | 14 +++++++------- indra/newview/CMakeLists.txt | 3 +++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index 617286fb38..cd7c09f097 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -44,13 +44,13 @@ /// - Minimal data sharing across threads for correctness and low latency. /// /// The public interface is declared in a few key header files: -/// - -/// - -/// - -/// - -/// - -/// - -/// - +/// - "llcorehttp/bufferarray.h" +/// - "llcorehttp/httpcommon.h" +/// - "llcorehttp/httphandler.h" +/// - "llcorehttp/httpheaders.h" +/// - "llcorehttp/httpoptions.h" +/// - "llcorehttp/httprequest.h" +/// - "llcorehttp/httpresponse.h" /// /// The library is still under early development and particular users /// may need access to internal implementation details that are found diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index f85b943c70..fc9d89aea0 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -18,6 +18,7 @@ include(LLAudio) include(LLCharacter) include(LLCommon) include(LLConvexDecomposition) +include(LLCoreHttp) include(LLImage) include(LLImageJ2COJ) include(LLInventory) @@ -52,6 +53,7 @@ include_directories( ${LLCHARACTER_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} ${LLCONVEXDECOMP_INCLUDE_DIRS} + ${LLCOREHTTP_INCLUDE_DIRS} ${FMOD_INCLUDE_DIR} ${LLIMAGE_INCLUDE_DIRS} ${LLKDU_INCLUDE_DIRS} @@ -1747,6 +1749,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${LLXUIXML_LIBRARIES} ${LSCRIPT_LIBRARIES} ${LLMATH_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} ${NDOF_LIBRARY} ${viewer_LIBRARIES} -- cgit v1.2.3 From 74d59e7128bb02a4b49af99e44f437a736a3f62b Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 7 May 2012 15:16:31 -0400 Subject: Build llcorehttp as part of a viewer dependency with unit tests. This required boost::thread and the easiest path to that was to go with the 1.48 Boost release in the 3P tree (eliminating a fork for a modified 1.45 packaging). One unit test, the most important one, is failing in test_httprequest but that can be attended to later. This test issues a GET to http://localhost:2/ and that is hitting the wire but the libcurl plumbing isn't delivering the failure, only the eventual timeout. An unexpected change in behavior. --- autobuild.xml | 12 ++--- indra/cmake/Boost.cmake | 23 ++++++---- indra/cmake/LLCoreHttp.cmake | 2 + indra/cmake/LLPrimitive.cmake | 8 ++-- indra/llcommon/linden_common.h | 4 ++ indra/llcorehttp/CMakeLists.txt | 30 ++++++------ indra/llcorehttp/_httpoprequest.cpp | 2 + indra/llcorehttp/_thread.h | 4 +- indra/llcorehttp/httpcommon.h | 2 + indra/llcorehttp/tests/all_test.cpp | 64 -------------------------- indra/llcorehttp/tests/llcorehttp_test.cpp | 66 +++++++++++++++++++++++++++ indra/llcorehttp/tests/test_bufferarray.hpp | 3 +- indra/llcorehttp/tests/test_httpheaders.hpp | 2 +- indra/llcorehttp/tests/test_httpoperation.hpp | 2 +- indra/llcorehttp/tests/test_httprequest.hpp | 12 ++--- indra/llcorehttp/tests/test_httpstatus.hpp | 2 +- indra/llcorehttp/tests/test_refcounted.hpp | 2 +- 17 files changed, 131 insertions(+), 109 deletions(-) delete mode 100644 indra/llcorehttp/tests/all_test.cpp create mode 100644 indra/llcorehttp/tests/llcorehttp_test.cpp diff --git a/autobuild.xml b/autobuild.xml index 9914be6867..5c9af29b99 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -186,9 +186,9 @@ archive hash - d98078791ce345bf6168ce9ba53ca2d7 + 0c4678ac85395f5f5294b63da1d79007 url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/222752/arch/Darwin/installer/boost-1.45.0-darwin-20110304.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/249117/arch/Darwin/installer/boost-1.48.0-darwin-20120208.tar.bz2 name darwin @@ -198,9 +198,9 @@ archive hash - a34e7fffdb94a6a4d8a2966b1f216da3 + 848766eac189e0fa785f4a025532acd9 url - http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.45.0-linux-20110310.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/249117/arch/Linux/installer/boost-1.48.0-linux-20120208.tar.bz2 name linux @@ -210,9 +210,9 @@ archive hash - 98be22c8833aa2bca184b9fa09fbb82b + cd1e60a00d40f4475ae5e0aca86f74c1 url - http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.45.0-windows-20110124.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/249117/arch/CYGWIN/installer/boost-1.48.0-windows-20120208.tar.bz2 name windows diff --git a/indra/cmake/Boost.cmake b/indra/cmake/Boost.cmake index 2135f0584c..24de1fc0c2 100644 --- a/indra/cmake/Boost.cmake +++ b/indra/cmake/Boost.cmake @@ -12,12 +12,13 @@ if (STANDALONE) set(BOOST_SIGNALS_LIBRARY boost_signals-mt) set(BOOST_SYSTEM_LIBRARY boost_system-mt) set(BOOST_FILESYSTEM_LIBRARY boost_filesystem-mt) + set(BOOST_THREAD_LIBRARY boost_thread-mt) else (STANDALONE) use_prebuilt_binary(boost) set(Boost_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include) if (WINDOWS) - set(BOOST_VERSION 1_45) + set(BOOST_VERSION 1_48) if(MSVC80) set(BOOST_PROGRAM_OPTIONS_LIBRARY optimized libboost_program_options-vc80-mt-${BOOST_VERSION} @@ -37,22 +38,26 @@ else (STANDALONE) else(MSVC80) # MSVC 10.0 config set(BOOST_PROGRAM_OPTIONS_LIBRARY - optimized libboost_program_options-vc100-mt-${BOOST_VERSION} - debug libboost_program_options-vc100-mt-gd-${BOOST_VERSION}) + optimized libboost_program_options-mt + debug libboost_program_options-mt-gd) set(BOOST_REGEX_LIBRARY - optimized libboost_regex-vc100-mt-${BOOST_VERSION} - debug libboost_regex-vc100-mt-gd-${BOOST_VERSION}) + optimized libboost_regex-mt + debug libboost_regex-mt-gd) set(BOOST_SYSTEM_LIBRARY - optimized libboost_system-vc100-mt-${BOOST_VERSION} - debug libboost_system-vc100-mt-gd-${BOOST_VERSION}) + optimized libboost_system-mt + debug libboost_system-mt-gd) set(BOOST_FILESYSTEM_LIBRARY - optimized libboost_filesystem-vc100-mt-${BOOST_VERSION} - debug libboost_filesystem-vc100-mt-gd-${BOOST_VERSION}) + optimized libboost_filesystem-mt + debug libboost_filesystem-mt-gd) + set(BOOST_THREAD_LIBRARY + optimized libboost_thread-mt + debug libboost_thread-mt-gd) endif (MSVC80) elseif (DARWIN OR LINUX) set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options) set(BOOST_REGEX_LIBRARY boost_regex) set(BOOST_SYSTEM_LIBRARY boost_system) set(BOOST_FILESYSTEM_LIBRARY boost_filesystem) + set(BOOST_THREAD_LIBRARY boost_thread) endif (WINDOWS) endif (STANDALONE) diff --git a/indra/cmake/LLCoreHttp.cmake b/indra/cmake/LLCoreHttp.cmake index 280189da3d..61e4b23d98 100644 --- a/indra/cmake/LLCoreHttp.cmake +++ b/indra/cmake/LLCoreHttp.cmake @@ -3,12 +3,14 @@ include(CARes) include(CURL) include(OpenSSL) +include(Boost) set(LLCOREHTTP_INCLUDE_DIRS ${LIBS_OPEN_DIR}/llcorehttp ${CARES_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS} + ${BOOST_INCLUDE_DIRS} ) set(LLCOREHTTP_LIBRARIES llcorehttp) diff --git a/indra/cmake/LLPrimitive.cmake b/indra/cmake/LLPrimitive.cmake index f15a2c2649..ab39cbb6be 100644 --- a/indra/cmake/LLPrimitive.cmake +++ b/indra/cmake/LLPrimitive.cmake @@ -15,10 +15,10 @@ if (WINDOWS) optimized llprimitive debug libcollada14dom22-d optimized libcollada14dom22 - debug libboost_filesystem-vc100-mt-gd-1_45 - optimized libboost_filesystem-vc100-mt-1_45 - debug libboost_system-vc100-mt-gd-1_45 - optimized libboost_system-vc100-mt-1_45 + debug libboost_filesystem-mt-gd + optimized libboost_filesystem-mt + debug libboost_system-mt-gd + optimized libboost_system-mt ) else (WINDOWS) set(LLPRIMITIVE_LIBRARIES diff --git a/indra/llcommon/linden_common.h b/indra/llcommon/linden_common.h index bdcc98e402..50db03cb6a 100644 --- a/indra/llcommon/linden_common.h +++ b/indra/llcommon/linden_common.h @@ -82,4 +82,8 @@ #include "llfile.h" #include "llformat.h" +// Boost 1.45 had version 2 as the default for the filesystem library, +// 1.48 has version 3 as the default. Keep compatibility for now. +#define BOOST_FILESYSTEM_VERSION 2 + #endif diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index e0d1817781..a950e9180c 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -73,23 +73,24 @@ target_link_libraries( ${CARES_LIBRARIES} ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARIES} + ${BOOST_THREAD_LIBRARY} ) # tests -#if (LL_TESTS) -if (LL_TESTS AND 0) +if (LL_TESTS) +#if (LL_TESTS AND 0) SET(llcorehttp_TEST_SOURCE_FILES - test_allocator.cpp + tests/test_allocator.cpp ) set(llcorehttp_TEST_HEADER_FILS - test_httpstatus.hpp - test_refcounted.hpp - test_httpoperation.hpp - test_httprequest.hpp - test_httprequestqueue.hpp - test_httpheaders.hpp - test_bufferarray.hpp + tests/test_httpstatus.hpp + tests/test_refcounted.hpp + tests/test_httpoperation.hpp + tests/test_httprequest.hpp + tests/test_httprequestqueue.hpp + tests/test_httpheaders.hpp + tests/test_bufferarray.hpp ) set_source_files_properties(${llcorehttp_TEST_HEADER_FILES} @@ -105,13 +106,16 @@ if (LL_TESTS AND 0) ${WINDOWS_LIBRARIES} ${LLCOMMON_LIBRARIES} ${GOOGLEMOCK_LIBRARIES} + ${CURL_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} ) - LL_ADD_INTEGRATION_TEST(all + LL_ADD_INTEGRATION_TEST(llcorehttp "${llcorehttp_TEST_SOURCE_FILES}" "${test_libs}" ) -#endif (LL_TESTS) -endif (LL_TESTS AND 0) +endif (LL_TESTS) +#endif (LL_TESTS AND 0) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 316df8bd49..3c9eb71b9a 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -259,6 +259,8 @@ HttpStatus HttpOpRequest::prepareForGet(HttpService * service) HttpStatus status; mCurlHandle = curl_easy_init(); + curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, 30); + curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, 30); curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1); curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str()); diff --git a/indra/llcorehttp/_thread.h b/indra/llcorehttp/_thread.h index 0937d698c7..46a333a749 100644 --- a/indra/llcorehttp/_thread.h +++ b/indra/llcorehttp/_thread.h @@ -27,9 +27,11 @@ #ifndef LLCOREINT_THREAD_H_ #define LLCOREINT_THREAD_H_ +#include +#include -#include "_refcounted.h" +#include "_refcounted.h" namespace LLCoreInt { diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index cd7c09f097..5fc497e720 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -93,6 +93,8 @@ /// /// +#include "linden_common.h" + #include diff --git a/indra/llcorehttp/tests/all_test.cpp b/indra/llcorehttp/tests/all_test.cpp deleted file mode 100644 index 636f6f8c05..0000000000 --- a/indra/llcorehttp/tests/all_test.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/** - * @file test_all - * @brief Main test runner - * - * $LicenseInfo:firstyear=2012&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2012, 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 - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - - -#include - -#include -#include - -#include - -// Pull in each of the test sets -#include "test_httpstatus.hpp" -#include "test_refcounted.hpp" -#include "test_httpoperation.hpp" -#include "test_httprequest.hpp" -#include "test_httpheaders.hpp" -#include "test_bufferarray.hpp" -#include "test_httprequestqueue.hpp" - - -namespace tut -{ - test_runner_singleton runner; -} - -int main() -{ - curl_global_init(CURL_GLOBAL_ALL); - - // *FIXME: Need threaded/SSL curl setup here. - - tut::reporter reporter; - - tut::runner.get().set_callback(&reporter); - tut::runner.get().run_tests(); - return !reporter.all_ok(); - - curl_global_cleanup(); -} - diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp new file mode 100644 index 0000000000..ad596d61cc --- /dev/null +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -0,0 +1,66 @@ +/** + * @file llcorehttp_test + * @brief Main test runner + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#include + +#include +#include + +#include + +// Pull in each of the test sets +#include "test_httpstatus.hpp" +#include "test_refcounted.hpp" +#include "test_httpoperation.hpp" +#include "test_httprequest.hpp" +#include "test_httpheaders.hpp" +#include "test_bufferarray.hpp" +#include "test_httprequestqueue.hpp" + +#if 0 + +namespace tut +{ + test_runner_singleton runner; +} + +int main() +{ + curl_global_init(CURL_GLOBAL_ALL); + + // *FIXME: Need threaded/SSL curl setup here. + + tut::reporter reporter; + + tut::runner.get().set_callback(&reporter); + tut::runner.get().run_tests(); + return !reporter.all_ok(); + + curl_global_cleanup(); +} + +#endif diff --git a/indra/llcorehttp/tests/test_bufferarray.hpp b/indra/llcorehttp/tests/test_bufferarray.hpp index 4f5d0284a1..ecbb5ef250 100644 --- a/indra/llcorehttp/tests/test_bufferarray.hpp +++ b/indra/llcorehttp/tests/test_bufferarray.hpp @@ -26,7 +26,7 @@ #ifndef TEST_LLCORE_BUFFER_ARRAY_H_ #define TEST_LLCORE_BUFFER_ARRAY_H_ -#include +#include "bufferarray.h" #include @@ -183,7 +183,6 @@ void BufferArrayTestObjectType::test<4>() char str1[] = "abcdefghij"; size_t str1_len(strlen(str1)); char str2[] = "ABCDEFGHIJ"; - size_t str2_len(strlen(str2)); char buffer[256]; size_t len = ba->write(str1, str1_len); diff --git a/indra/llcorehttp/tests/test_httpheaders.hpp b/indra/llcorehttp/tests/test_httpheaders.hpp index d22b516691..ce0d19b058 100644 --- a/indra/llcorehttp/tests/test_httpheaders.hpp +++ b/indra/llcorehttp/tests/test_httpheaders.hpp @@ -26,7 +26,7 @@ #ifndef TEST_LLCORE_HTTP_HEADERS_H_ #define TEST_LLCORE_HTTP_HEADERS_H_ -#include +#include "httpheaders.h" #include diff --git a/indra/llcorehttp/tests/test_httpoperation.hpp b/indra/llcorehttp/tests/test_httpoperation.hpp index c9feaddb2a..6c3df1e9e3 100644 --- a/indra/llcorehttp/tests/test_httpoperation.hpp +++ b/indra/llcorehttp/tests/test_httpoperation.hpp @@ -27,7 +27,7 @@ #define TEST_LLCORE_HTTP_OPERATION_H_ #include "_httpoperation.h" -#include +#include "httphandler.h" #include diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index df5640859f..ab25a2eb1a 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -26,11 +26,11 @@ #ifndef TEST_LLCORE_HTTP_REQUEST_H_ #define TEST_LLCORE_HTTP_REQUEST_H_ -#include -#include -#include -#include -#include +#include "httprequest.h" +#include "httphandler.h" +#include "httpresponse.h" +#include "_httpservice.h" +#include "_httprequestqueue.h" #include @@ -360,7 +360,7 @@ void HttpRequestTestObjectType::test<5>() mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, 0.0f, - "http://localhost:2/nothing/here", + "http://127.0.0.1:2/nothing/here", 0, 0, NULL, diff --git a/indra/llcorehttp/tests/test_httpstatus.hpp b/indra/llcorehttp/tests/test_httpstatus.hpp index fa5803a17c..38bf494dec 100644 --- a/indra/llcorehttp/tests/test_httpstatus.hpp +++ b/indra/llcorehttp/tests/test_httpstatus.hpp @@ -27,7 +27,7 @@ #ifndef TEST_HTTP_STATUS_H_ #define TEST_HTTP_STATUS_H_ -#include +#include "httpcommon.h" #include #include diff --git a/indra/llcorehttp/tests/test_refcounted.hpp b/indra/llcorehttp/tests/test_refcounted.hpp index 6a17947288..cb4b50287a 100644 --- a/indra/llcorehttp/tests/test_refcounted.hpp +++ b/indra/llcorehttp/tests/test_refcounted.hpp @@ -26,7 +26,7 @@ #ifndef TEST_LLCOREINT_REF_COUNTED_H_ #define TEST_LLCOREINT_REF_COUNTED_H_ -#include <_refcounted.h> +#include "_refcounted.h" #include "test_allocator.h" -- cgit v1.2.3 From 7caef4bc6c348a4aad4a777df0d1ea34ab13ff00 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 8 May 2012 10:38:08 -0400 Subject: Okay, got Mac building with Boost 1.48. Unit tests needed NULL pointer defenses in the delete functions of the allocation support. General boost library renaming again. Linux builds in TC though it shouldn't based on what Boost.cmake lookes like... --- indra/cmake/Boost.cmake | 31 ++++++++++++++++++++++++------ indra/llcorehttp/CMakeLists.txt | 2 -- indra/llcorehttp/tests/llcorehttp_test.cpp | 6 ++++-- indra/llcorehttp/tests/test_allocator.cpp | 10 ++++++++-- 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/indra/cmake/Boost.cmake b/indra/cmake/Boost.cmake index 24de1fc0c2..4a797d968a 100644 --- a/indra/cmake/Boost.cmake +++ b/indra/cmake/Boost.cmake @@ -53,11 +53,30 @@ else (STANDALONE) optimized libboost_thread-mt debug libboost_thread-mt-gd) endif (MSVC80) - elseif (DARWIN OR LINUX) - set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options) - set(BOOST_REGEX_LIBRARY boost_regex) - set(BOOST_SYSTEM_LIBRARY boost_system) - set(BOOST_FILESYSTEM_LIBRARY boost_filesystem) - set(BOOST_THREAD_LIBRARY boost_thread) + elseif (LINUX) + set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options-mt) + set(BOOST_REGEX_LIBRARY boost_regex-mt) + set(BOOST_SYSTEM_LIBRARY boost_system-mt) + set(BOOST_FILESYSTEM_LIBRARY boost_filesystem-mt) + set(BOOST_THREAD_LIBRARY boost_thread-mt) + elseif (DARWIN) + set(BOOST_PROGRAM_OPTIONS_LIBRARY + optimized boost_program_options-mt + debug boost_program_options-mt-d) + set(BOOST_PROGRAM_OPTIONS_LIBRARY + optimized boost_program_options-mt + debug boost_program_options-mt-d) + set(BOOST_REGEX_LIBRARY + optimized boost_regex-mt + debug boost_regex-mt-d) + set(BOOST_SYSTEM_LIBRARY + optimized boost_system-mt + debug boost_system-mt-d) + set(BOOST_FILESYSTEM_LIBRARY + optimized boost_filesystem-mt + debug boost_filesystem-mt-d) + set(BOOST_THREAD_LIBRARY + optimized boost_thread-mt + debug boost_thread-mt-d) endif (WINDOWS) endif (STANDALONE) diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index a950e9180c..99b60a5995 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -78,7 +78,6 @@ target_link_libraries( # tests if (LL_TESTS) -#if (LL_TESTS AND 0) SET(llcorehttp_TEST_SOURCE_FILES tests/test_allocator.cpp ) @@ -117,5 +116,4 @@ if (LL_TESTS) ) endif (LL_TESTS) -#endif (LL_TESTS AND 0) diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index ad596d61cc..92f16be8fd 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -27,8 +27,10 @@ #include -#include -#include +// These are not the right way in viewer for some reason: +// #include +// #include +#include "../test/lltut.h" #include diff --git a/indra/llcorehttp/tests/test_allocator.cpp b/indra/llcorehttp/tests/test_allocator.cpp index 926de0a208..ea12dc58eb 100644 --- a/indra/llcorehttp/tests/test_allocator.cpp +++ b/indra/llcorehttp/tests/test_allocator.cpp @@ -166,13 +166,19 @@ void * operator new[](std::size_t size) THROW_BAD_ALLOC() void operator delete(void * p) THROW_NOTHING() { - FreeMem( p ); + if (p) + { + FreeMem( p ); + } } void operator delete[](void * p) THROW_NOTHING() { - FreeMem( p ); + if (p) + { + FreeMem( p ); + } } -- cgit v1.2.3 From 239e072bfcf97b8a12c18ff9974fd0a2929c9ee4 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 8 May 2012 12:27:24 -0400 Subject: Unit test still giving me issues on the local windows system. Seems to be a hard stall while allocating the first easy handle in a descent of the global initiailization code but that doesn't seem to be a problem on TC machines. Perhaps the static linking is creating multiple data copies. More work needed. --- indra/cmake/Boost.cmake | 20 +++++++++++++++----- indra/llcorehttp/CMakeLists.txt | 8 +++++++- indra/llcorehttp/_httplibcurl.h | 1 + indra/llcorehttp/_httpoprequest.h | 2 ++ indra/llcorehttp/httpcommon.h | 2 +- 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/indra/cmake/Boost.cmake b/indra/cmake/Boost.cmake index 4a797d968a..2af0bc1b30 100644 --- a/indra/cmake/Boost.cmake +++ b/indra/cmake/Boost.cmake @@ -54,11 +54,21 @@ else (STANDALONE) debug libboost_thread-mt-gd) endif (MSVC80) elseif (LINUX) - set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options-mt) - set(BOOST_REGEX_LIBRARY boost_regex-mt) - set(BOOST_SYSTEM_LIBRARY boost_system-mt) - set(BOOST_FILESYSTEM_LIBRARY boost_filesystem-mt) - set(BOOST_THREAD_LIBRARY boost_thread-mt) + set(BOOST_PROGRAM_OPTIONS_LIBRARY + optimized boost_program_options-mt + debug boost_program_options-mt-d) + set(BOOST_REGEX_LIBRARY + optimized boost_regex-mt + debug boost_regex-mt-d) + set(BOOST_SYSTEM_LIBRARY + optimized boost_system-mt + debug boost_system-mt-d) + set(BOOST_FILESYSTEM_LIBRARY + optimized boost_filesystem-mt + debug boost_filesystem-mt-d) + set(BOOST_THREAD_LIBRARY + optimized boost_thread-mt + debug boost_thread-mt-d) elseif (DARWIN) set(BOOST_PROGRAM_OPTIONS_LIBRARY optimized boost_program_options-mt diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 99b60a5995..81c502b642 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -3,8 +3,12 @@ project(llcorehttp) include(00-Common) -include(LLCoreHttp) include(GoogleMock) +include(CURL) +include(CARes) +include(OpenSSL) +include(ZLIB) +include(LLCoreHttp) include(LLAddBuildTest) include(LLCommon) include(Tut) @@ -106,8 +110,10 @@ if (LL_TESTS) ${LLCOMMON_LIBRARIES} ${GOOGLEMOCK_LIBRARIES} ${CURL_LIBRARIES} + ${CARES_LIBRARIES} ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARIES} + ${BOOST_THREAD_LIBRARY} ) LL_ADD_INTEGRATION_TEST(llcorehttp diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 5ba244cee4..01c68320af 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -27,6 +27,7 @@ #ifndef _LLCORE_HTTP_LIBCURL_H_ #define _LLCORE_HTTP_LIBCURL_H_ +#include "linden_common.h" // Modifies curl/curl.h interfaces #include #include diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index e912851aed..232ee841d6 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -28,6 +28,8 @@ #define _LLCORE_HTTP_OPREQUEST_H_ +#include "linden_common.h" // Modifies curl/curl.h interfaces + #include "httpcommon.h" #include diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index 5fc497e720..9de5769d57 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -93,7 +93,7 @@ /// /// -#include "linden_common.h" +#include "linden_common.h" // Modifies curl/curl.h interfaces #include -- cgit v1.2.3 From cc878fed1b65779cd4535678117e99a68a69c62c Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 8 May 2012 11:32:46 -0700 Subject: Now trying to get linux working. libllcommon is picking up .so boost libraries when we tend to want statics. Try to work around that for the moment. --- indra/newview/viewer_manifest.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 0931c4ec9b..b3ca24a783 100644 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1031,6 +1031,8 @@ class Linux_i686Manifest(LinuxManifest): self.path("libaprutil-1.so") self.path("libaprutil-1.so.0") self.path("libaprutil-1.so.0.3.10") + self.path("libboost_program_options-mt.so.1.48.0") + self.path("libboost_regex.so.1.48.0") self.path("libbreakpad_client.so.0.0.0") self.path("libbreakpad_client.so.0") self.path("libbreakpad_client.so") -- cgit v1.2.3 From b7d3ae7dce78b6541f871eaca7152cc86cde5840 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 8 May 2012 12:17:37 -0700 Subject: More linux work. Think I found the stager. --- indra/cmake/Copy3rdPartyLibs.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index 394db362b1..ee6a3f68da 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -251,20 +251,22 @@ elseif(LINUX) libapr-1.so.0 libaprutil-1.so.0 libatk-1.0.so + libboost_program_options-mt.so + libboost_regex-mt.so libbreakpad_client.so.0 libcollada14dom.so libcrypto.so.1.0.0 libdb-5.1.so libexpat.so libexpat.so.1 - libglod.so + libglod.so libgmock_main.so libgmock.so.0 libgmodule-2.0.so libgobject-2.0.so libgtest_main.so libgtest.so.0 - libminizip.so + libminizip.so libopenal.so libopenjpeg.so libssl.so -- cgit v1.2.3 From c5be6d0b92642b3ba12a12441b6b436cde18dcdb Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 8 May 2012 13:15:42 -0700 Subject: Okay, needed full version numbers. --- indra/cmake/Copy3rdPartyLibs.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index ee6a3f68da..0866b35e9d 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -251,8 +251,8 @@ elseif(LINUX) libapr-1.so.0 libaprutil-1.so.0 libatk-1.0.so - libboost_program_options-mt.so - libboost_regex-mt.so + libboost_program_options-mt.so.1.48.0 + libboost_regex-mt.so.1.48.0 libbreakpad_client.so.0 libcollada14dom.so libcrypto.so.1.0.0 -- cgit v1.2.3 From e1a978bbcfe4640e3163fd0a3ed41460f1d6942a Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 8 May 2012 13:39:06 -0700 Subject: And more boost library work. --- indra/cmake/Copy3rdPartyLibs.cmake | 1 + indra/newview/viewer_manifest.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index 0866b35e9d..fe42cc5251 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -253,6 +253,7 @@ elseif(LINUX) libatk-1.0.so libboost_program_options-mt.so.1.48.0 libboost_regex-mt.so.1.48.0 + libboost_thread-mt.so.1.48.0 libbreakpad_client.so.0 libcollada14dom.so libcrypto.so.1.0.0 diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index b3ca24a783..2be89b77de 100644 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1032,7 +1032,8 @@ class Linux_i686Manifest(LinuxManifest): self.path("libaprutil-1.so.0") self.path("libaprutil-1.so.0.3.10") self.path("libboost_program_options-mt.so.1.48.0") - self.path("libboost_regex.so.1.48.0") + self.path("libboost_regex-mt.so.1.48.0") + self.path("libboost_thread-mt.so.1.48.0") self.path("libbreakpad_client.so.0.0.0") self.path("libbreakpad_client.so.0") self.path("libbreakpad_client.so") -- cgit v1.2.3 From 2f496ecaeeb0ab90c29ca6f0414cad1fe16cd4b0 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 9 May 2012 06:49:18 -0700 Subject: Add more boost libs to the lib copy and manifest. --- indra/cmake/Copy3rdPartyLibs.cmake | 3 +++ indra/newview/viewer_manifest.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index fe42cc5251..17ea48f31f 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -254,6 +254,9 @@ elseif(LINUX) libboost_program_options-mt.so.1.48.0 libboost_regex-mt.so.1.48.0 libboost_thread-mt.so.1.48.0 + libboost_filesystem-mt.so.1.48.0 + libboost_signals-mt.so.1.48.0 + libboost_system-mt.so.1.48.0 libbreakpad_client.so.0 libcollada14dom.so libcrypto.so.1.0.0 diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 2be89b77de..fffb61e89c 100644 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1034,6 +1034,9 @@ class Linux_i686Manifest(LinuxManifest): self.path("libboost_program_options-mt.so.1.48.0") self.path("libboost_regex-mt.so.1.48.0") self.path("libboost_thread-mt.so.1.48.0") + self.path("libboost_filesystem-mt.so.1.48.0") + self.path("libboost_signals-mt.so.1.48.0") + self.path("libboost_system-mt.so.1.48.0") self.path("libbreakpad_client.so.0.0.0") self.path("libbreakpad_client.so.0") self.path("libbreakpad_client.so") -- cgit v1.2.3 From 7a9acdc68a454886efc38cd4558b64856f4a9a04 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 9 May 2012 07:06:15 -0700 Subject: Try to get some more correct curl init into the unit testing. --- indra/llcorehttp/tests/llcorehttp_test.cpp | 78 ++++++++++++++++++++++++++++- indra/llcorehttp/tests/llcorehttp_test.h | 34 +++++++++++++ indra/llcorehttp/tests/test_httprequest.hpp | 5 ++ 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 indra/llcorehttp/tests/llcorehttp_test.h diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index 92f16be8fd..da811adb19 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -24,15 +24,18 @@ * $/LicenseInfo$ */ +#include "llcorehttp_test.h" #include // These are not the right way in viewer for some reason: // #include // #include +// This works: #include "../test/lltut.h" #include +#include // Pull in each of the test sets #include "test_httpstatus.hpp" @@ -43,7 +46,10 @@ #include "test_bufferarray.hpp" #include "test_httprequestqueue.hpp" -#if 0 +unsigned long ssl_thread_id_callback(void); +void ssl_locking_callback(int mode, int type, const char * file, int line); + +#if 0 // lltut provides main namespace tut { @@ -65,4 +71,74 @@ int main() curl_global_cleanup(); } +#endif // 0 + +int ssl_mutex_count(0); +LLCoreInt::HttpMutex ** ssl_mutex_list = NULL; + +void init_curl() +{ + curl_global_init(CURL_GLOBAL_ALL); + + ssl_mutex_count = CRYPTO_num_locks(); + if (ssl_mutex_count > 0) + { + ssl_mutex_list = new LLCoreInt::HttpMutex * [ssl_mutex_count]; + + for (int i(0); i < ssl_mutex_count; ++i) + { + ssl_mutex_list[i] = new LLCoreInt::HttpMutex; + } + + CRYPTO_set_locking_callback(ssl_locking_callback); + CRYPTO_set_id_callback(ssl_thread_id_callback); + } +} + + +void term_curl() +{ + CRYPTO_set_locking_callback(NULL); + for (int i(0); i < ssl_mutex_count; ++i) + { + delete ssl_mutex_list[i]; + } + delete [] ssl_mutex_list; +} + + +unsigned long ssl_thread_id_callback(void) +{ +#if defined(WIN32) + return (unsigned long) GetCurrentThread(); +#else + return (unsigned long) pthread_self(); +#endif +} + + +void ssl_locking_callback(int mode, int type, const char * /* file */, int /* line */) +{ + if (type >= 0 && type < ssl_mutex_count) + { + if (mode & CRYPTO_LOCK) + { + ssl_mutex_list[type]->lock(); + } + else + { + ssl_mutex_list[type]->unlock(); + } + } +} + + +#if defined(WIN32) + +int getopt(int argc, char * const argv[], const char *optstring) +{ + return -1; +} + #endif + diff --git a/indra/llcorehttp/tests/llcorehttp_test.h b/indra/llcorehttp/tests/llcorehttp_test.h new file mode 100644 index 0000000000..941cb457d3 --- /dev/null +++ b/indra/llcorehttp/tests/llcorehttp_test.h @@ -0,0 +1,34 @@ +/** + * @file llcorehttp_test.h + * @brief Main test runner + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#ifndef _LLCOREHTTP_TEST_H_ +#define _LLCOREHTTP_TEST_H_ + +extern void init_curl(); +extern void term_curl(); + +#endif // _LLCOREHTTP_TEST_H_ diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index ab25a2eb1a..a77a36c96e 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -35,6 +35,7 @@ #include #include "test_allocator.h" +#include "llcorehttp_test.h" using namespace LLCoreInt; @@ -334,6 +335,8 @@ void HttpRequestTestObjectType::test<4>() template <> template <> void HttpRequestTestObjectType::test<5>() { + init_curl(); + set_test_name("HttpRequest GET + Stop execution"); // Handler can be stack-allocated *if* there are no dangling @@ -416,6 +419,8 @@ void HttpRequestTestObjectType::test<5>() ensure("Two handler calls on the way out", 2 == mHandlerCalls); // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + + term_curl(); } } // end namespace tut -- cgit v1.2.3 From 30d72b041f3221b903ac11c0054dc221b0c0329b Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 9 May 2012 10:23:02 -0400 Subject: Added correct libcurl initialization to the unit tests which makes Windows builds reliable. It's the right thing to do and introduced a scoped version for convenience in tests. --- indra/llcorehttp/tests/llcorehttp_test.cpp | 5 +---- indra/llcorehttp/tests/llcorehttp_test.h | 25 +++++++++++++++++++++++++ indra/llcorehttp/tests/test_httprequest.hpp | 14 ++++++++++---- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index da811adb19..0ee767fdca 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -34,9 +34,6 @@ // This works: #include "../test/lltut.h" -#include -#include - // Pull in each of the test sets #include "test_httpstatus.hpp" #include "test_refcounted.hpp" @@ -49,7 +46,7 @@ unsigned long ssl_thread_id_callback(void); void ssl_locking_callback(int mode, int type, const char * file, int line); -#if 0 // lltut provides main +#if 0 // lltut provides main and runner namespace tut { diff --git a/indra/llcorehttp/tests/llcorehttp_test.h b/indra/llcorehttp/tests/llcorehttp_test.h index 941cb457d3..1550881a00 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.h +++ b/indra/llcorehttp/tests/llcorehttp_test.h @@ -28,7 +28,32 @@ #ifndef _LLCOREHTTP_TEST_H_ #define _LLCOREHTTP_TEST_H_ +#include "linden_common.h" // Modifies curl interfaces + +#include +#include + +// Initialization and cleanup for libcurl. Mainly provides +// a mutex callback for SSL and a thread ID hash for libcurl. +// If you don't use these (or equivalent) and do use libcurl, +// you'll see stalls and other anomalies when performing curl +// operations. extern void init_curl(); extern void term_curl(); +class ScopedCurlInit +{ +public: + ScopedCurlInit() + { + init_curl(); + } + + ~ScopedCurlInit() + { + term_curl(); + } +}; + + #endif // _LLCOREHTTP_TEST_H_ diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index a77a36c96e..a73d90957e 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -106,6 +106,8 @@ HttpRequestTestGroupType HttpRequestTestGroup("HttpRequest Tests"); template <> template <> void HttpRequestTestObjectType::test<1>() { + ScopedCurlInit ready; + set_test_name("HttpRequest construction"); // record the total amount of dynamically allocated memory @@ -131,6 +133,8 @@ void HttpRequestTestObjectType::test<1>() template <> template <> void HttpRequestTestObjectType::test<2>() { + ScopedCurlInit ready; + set_test_name("HttpRequest and Null Op queued"); // record the total amount of dynamically allocated memory @@ -168,6 +172,8 @@ void HttpRequestTestObjectType::test<2>() template <> template <> void HttpRequestTestObjectType::test<3>() { + ScopedCurlInit ready; + set_test_name("HttpRequest NoOp + Stop execution"); // Handler can be stack-allocated *if* there are no dangling @@ -246,6 +252,8 @@ void HttpRequestTestObjectType::test<3>() template <> template <> void HttpRequestTestObjectType::test<4>() { + ScopedCurlInit ready; + set_test_name("2 HttpRequest instances, one thread"); // Handler can be stack-allocated *if* there are no dangling @@ -335,8 +343,8 @@ void HttpRequestTestObjectType::test<4>() template <> template <> void HttpRequestTestObjectType::test<5>() { - init_curl(); - + ScopedCurlInit ready; + set_test_name("HttpRequest GET + Stop execution"); // Handler can be stack-allocated *if* there are no dangling @@ -419,8 +427,6 @@ void HttpRequestTestObjectType::test<5>() ensure("Two handler calls on the way out", 2 == mHandlerCalls); // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); - - term_curl(); } } // end namespace tut -- cgit v1.2.3 From 8fc350125c671baeae6b7f8b1814251009f4f50a Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 23 May 2012 19:12:09 -0400 Subject: Integrate llcorehttp library into lltexturefetch design. This is the first functional viewer pass with the HTTP work of the texture fetch code performed by the llcorehttp library. Not exactly a 'drop-in' replacement but a work-alike with some changes (e.g. handler notification in consumer thread versus responder notification in worker thread). This also includes some temporary changes in the priority scheme to prevent the kind of priority inversion found in VWR-28996. Scheme used here does provide liveness if not optimal responsiveness or order-of-operation. The llcorehttp library at this point is far from optimally performing. Its worker thread is making relatively poor use of cycles it gets and it doesn't idle or sleep intelligently yet. This early integration step helps shake out the interfaces, implementation niceties will be covered soon. --- indra/llcorehttp/CMakeLists.txt | 2 + indra/llcorehttp/_httplibcurl.cpp | 11 +- indra/llcorehttp/_httplibcurl.h | 3 +- indra/llcorehttp/_httpopcancel.cpp | 82 ++++ indra/llcorehttp/_httpopcancel.h | 75 ++++ indra/llcorehttp/_httpoperation.h | 25 -- indra/llcorehttp/_httpoprequest.cpp | 105 +++-- indra/llcorehttp/_httpoprequest.h | 9 +- indra/llcorehttp/_httppolicy.h | 3 +- indra/llcorehttp/_httpreplyqueue.h | 2 +- indra/llcorehttp/_httprequestqueue.h | 2 +- indra/llcorehttp/_httpservice.h | 4 +- indra/llcorehttp/httpcommon.cpp | 90 ++++- indra/llcorehttp/httpcommon.h | 59 ++- indra/llcorehttp/httprequest.cpp | 17 + indra/llcorehttp/httpresponse.cpp | 3 +- indra/llcorehttp/httpresponse.h | 32 +- indra/llcorehttp/tests/test_httpstatus.hpp | 102 +++++ indra/newview/llappviewer.cpp | 159 ++++++++ indra/newview/lltexturefetch.cpp | 610 +++++++++++++++++------------ indra/newview/lltexturefetch.h | 19 +- 21 files changed, 1057 insertions(+), 357 deletions(-) create mode 100644 indra/llcorehttp/_httpopcancel.cpp create mode 100644 indra/llcorehttp/_httpopcancel.h diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 81c502b642..ae92fb96fd 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -30,6 +30,7 @@ set(llcorehttp_SOURCE_FILES _httprequestqueue.cpp _httpoperation.cpp _httpoprequest.cpp + _httpopcancel.cpp _httpreplyqueue.cpp _httppolicy.cpp _httplibcurl.cpp @@ -49,6 +50,7 @@ set(llcorehttp_HEADER_FILES httpresponse.h _httpoperation.h _httpoprequest.h + _httpopcancel.h _httprequestqueue.h _httpreplyqueue.h _httpservice.h diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 15be977adf..1b951818e4 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -27,7 +27,6 @@ #include "_httplibcurl.h" #include "httpheaders.h" - #include "_httpoprequest.h" #include "_httpservice.h" @@ -173,8 +172,11 @@ void HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode { int http_status(200); - curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status); - op->mReplyStatus = http_status; + curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_status); + op->mStatus = LLCore::HttpStatus(http_status, + (http_status >= 200 && http_status <= 299 + ? HE_SUCCESS + : HE_REPLY_ERROR)); } // Detach from multi and recycle handle @@ -202,7 +204,8 @@ int HttpLibcurl::activeCount() const struct curl_slist * append_headers_to_slist(const HttpHeaders * headers, struct curl_slist * slist) { for (HttpHeaders::container_t::const_iterator it(headers->mHeaders.begin()); - headers->mHeaders.end() != it; + + headers->mHeaders.end() != it; ++it) { slist = curl_slist_append(slist, (*it).c_str()); diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 01c68320af..807196628d 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -45,6 +45,7 @@ class HttpOpRequest; class HttpHeaders; +/// Implements libcurl-based transport for an HttpService instance. class HttpLibcurl { public: @@ -71,7 +72,7 @@ protected: typedef std::set active_set_t; protected: - HttpService * mService; + HttpService * mService; // Simple reference, not owner active_set_t mActiveOps; CURLM * mMultiHandles[1]; }; // end class HttpLibcurl diff --git a/indra/llcorehttp/_httpopcancel.cpp b/indra/llcorehttp/_httpopcancel.cpp new file mode 100644 index 0000000000..69dbff4bb4 --- /dev/null +++ b/indra/llcorehttp/_httpopcancel.cpp @@ -0,0 +1,82 @@ +/** + * @file _httpopcancel.cpp + * @brief Definitions for internal class HttpOpCancel + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpopcancel.h" + +#include +#include + +#include "httpcommon.h" +#include "httphandler.h" +#include "httpresponse.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" +#include "_httppolicy.h" +#include "_httplibcurl.h" + + +namespace LLCore +{ + + +// ================================== +// HttpOpCancel +// ================================== + + +HttpOpCancel::HttpOpCancel(HttpHandle handle) + : HttpOperation(), + mHandle(handle) +{} + + +HttpOpCancel::~HttpOpCancel() +{} + + +void HttpOpCancel::stageFromRequest(HttpService * service) +{ + // *FIXME: Need cancel functionality into services + addAsReply(); +} + + +void HttpOpCancel::visitNotifier(HttpRequest * request) +{ + if (mLibraryHandler) + { + HttpResponse * response = new HttpResponse(); + mLibraryHandler->onCompleted(static_cast(this), response); + response->release(); + } +} + + +} // end namespace LLCore + + diff --git a/indra/llcorehttp/_httpopcancel.h b/indra/llcorehttp/_httpopcancel.h new file mode 100644 index 0000000000..38ccc585ed --- /dev/null +++ b/indra/llcorehttp/_httpopcancel.h @@ -0,0 +1,75 @@ +/** + * @file _httpopcancel.h + * @brief Internal declarations for the HttpOpCancel subclass + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_OPCANCEL_H_ +#define _LLCORE_HTTP_OPCANCEL_H_ + + +#include "linden_common.h" // Modifies curl/curl.h interfaces + +#include "httpcommon.h" + +#include + +#include "_httpoperation.h" +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// HttpOpCancel requests that a previously issued request +/// be canceled, if possible. Requests that have been made +/// active and are available for sending on the wire cannot +/// be canceled. + +class HttpOpCancel : public HttpOperation +{ +public: + HttpOpCancel(HttpHandle handle); + virtual ~HttpOpCancel(); + +private: + HttpOpCancel(const HttpOpCancel &); // Not defined + void operator=(const HttpOpCancel &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + + virtual void visitNotifier(HttpRequest * request); + +public: + // Request data + HttpHandle mHandle; + +}; // end class HttpOpCancel + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_OPCANCEL_H_ + diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index d04961c47b..5d06a28586 100644 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -87,31 +87,6 @@ public: }; // end class HttpOperation -/// HttpOpCancel requests that a previously issued request -/// be canceled, if possible. Requests that have been made -/// active and are available for sending on the wire cannot -/// be canceled. - -class HttpOpCancel : public HttpOperation -{ -public: - HttpOpCancel(); - virtual ~HttpOpCancel(); - -private: - HttpOpCancel(const HttpOpCancel &); // Not defined - void operator=(const HttpOpCancel &); // Not defined - -public: - virtual void stageFromRequest(HttpService *); - virtual void stageFromReady(HttpService *); - virtual void stageFromActive(HttpService *); - -public: - HttpHandle mHandle; -}; // end class HttpOpCancel - - /// HttpOpStop requests the servicing thread to shutdown /// operations, cease pulling requests from the request /// queue and release shared resources (particularly diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 3c9eb71b9a..521bd5b879 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -86,53 +86,53 @@ HttpOpRequest::HttpOpRequest() mReqMethod(HOR_GET), mReqBody(NULL), mReqOffset(0), - mReqLen(0), + mReqLength(0), mReqHeaders(NULL), mReqOptions(NULL), mCurlActive(false), mCurlHandle(NULL), - mCurlHeaders(NULL), mCurlService(NULL), - mReplyStatus(200), + mCurlHeaders(NULL), mReplyBody(NULL), mReplyOffset(0), - mReplyLen(0), + mReplyLength(0), mReplyHeaders(NULL) {} HttpOpRequest::~HttpOpRequest() { - if (mCurlHandle) - { - curl_easy_cleanup(mCurlHandle); - mCurlHandle = NULL; - } - - if (mCurlHeaders) - { - curl_slist_free_all(mCurlHeaders); - mCurlHeaders = NULL; - } - - mCurlService = NULL; - if (mReqBody) { mReqBody->release(); mReqBody = NULL; } + if (mReqOptions) + { + mReqOptions->release(); + mReqOptions = NULL; + } + if (mReqHeaders) { - curl_slist_free_all(mReqHeaders); + mReqHeaders->release(); mReqHeaders = NULL; } - if (mReqOptions) + if (mCurlHandle) { - mReqOptions->release(); - mReqOptions = NULL; + curl_easy_cleanup(mCurlHandle); + mCurlHandle = NULL; + } + + mCurlService = NULL; + + + if (mCurlHeaders) + { + curl_slist_free_all(mCurlHeaders); + mCurlHeaders = NULL; } if (mReplyBody) @@ -165,28 +165,29 @@ void HttpOpRequest::stageFromReady(HttpService * service) void HttpOpRequest::stageFromActive(HttpService * service) { - if (mReplyLen) + if (mReplyLength) { // If non-zero, we received and processed a Content-Range // header with the response. Verify that what it says // is consistent with the received data. - if (mReplyLen != mReplyBody->size()) + if (mReplyLength != mReplyBody->size()) { // Not as expected, fail the request mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR); } } - if (mReqHeaders) + if (mCurlHeaders) { // We take these headers out of the request now as they were // allocated originally in this thread and the notifier doesn't // need them. This eliminates one source of heap moving across // threads. - curl_slist_free_all(mReqHeaders); - mReqHeaders = NULL; + curl_slist_free_all(mCurlHeaders); + mCurlHeaders = NULL; } + addAsReply(); } @@ -196,12 +197,24 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) if (mLibraryHandler) { HttpResponse * response = new HttpResponse(); - - // *FIXME: add http status, offset, length response->setStatus(mStatus); - response->setReplyStatus(mReplyStatus); response->setBody(mReplyBody); response->setHeaders(mReplyHeaders); + unsigned int offset(0), length(0); + if (mReplyOffset || mReplyLength) + { + // Got an explicit offset/length in response + offset = mReplyOffset; + length = mReplyLength; + } + else if (mReplyBody) + { + // Provide implicit offset/length from request/response + offset = mReqOffset; + length = mReplyBody->size(); + } + response->setRange(offset, length); + mLibraryHandler->onCompleted(static_cast(this), response); response->release(); @@ -235,14 +248,15 @@ HttpStatus HttpOpRequest::setupGetByteRange(unsigned int policy_id, mReqMethod = HOR_GET; mReqURL = url; mReqOffset = offset; - mReqLen = len; + mReqLength = len; if (offset || len) { mProcFlags |= PF_SCAN_RANGE_HEADER; } if (headers && ! mReqHeaders) { - mReqHeaders = append_headers_to_slist(headers, mReqHeaders); + headers->addRef(); + mReqHeaders = headers; } if (options && ! mReqOptions) { @@ -257,7 +271,7 @@ HttpStatus HttpOpRequest::prepareForGet(HttpService * service) { // *FIXME: better error handling later HttpStatus status; - + mCurlHandle = curl_easy_init(); curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, 30); curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, 30); @@ -268,14 +282,18 @@ HttpStatus HttpOpRequest::prepareForGet(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); // curl_easy_setopt(handle, CURLOPT_PROXY, ""); - mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); - curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders); curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback); curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, mCurlHandle); - if (mReqOffset || mReqLen) + if (mReqHeaders) + { + mCurlHeaders = append_headers_to_slist(mReqHeaders, mCurlHeaders); + } + mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); + + if (mReqOffset || mReqLength) { static const char * fmt1("Range: bytes=%d-%d"); static const char * fmt2("Range: bytes=%d-"); @@ -284,16 +302,17 @@ HttpStatus HttpOpRequest::prepareForGet(HttpService * service) #if defined(WIN32) _snprintf_s(range_line, sizeof(range_line), sizeof(range_line) - 1, - (mReqLen ? fmt1 : fmt2), - mReqOffset, mReqOffset + mReqLen - 1); + (mReqLength ? fmt1 : fmt2), + mReqOffset, mReqOffset + mReqLength - 1); #else snprintf(range_line, sizeof(range_line), - (mReqLen ? fmt1 : fmt2), - mReqOffset, mReqOffset + mReqLen - 1); + (mReqLength ? fmt1 : fmt2), + mReqOffset, mReqOffset + mReqLength - 1); #endif // defined(WIN32) range_line[sizeof(range_line) - 1] = '\0'; mCurlHeaders = curl_slist_append(mCurlHeaders, range_line); } + curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders); if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS)) { @@ -347,8 +366,9 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi if (hdr_size >= status_line_len && ! strncmp(status_line, hdr_data, status_line_len)) { // One of possibly several status lines. Reset what we know and start over + // taking results from the last header stanza we receive. op->mReplyOffset = 0; - op->mReplyLen = 0; + op->mReplyLength = 0; op->mStatus = HttpStatus(); } else if (op->mProcFlags & PF_SCAN_RANGE_HEADER) @@ -371,7 +391,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi { // Success, record the fragment position op->mReplyOffset = first; - op->mReplyLen = last - first + 1; + op->mReplyLength = last - first + 1; } else if (-1 == status) { @@ -390,6 +410,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi if (op->mProcFlags & PF_SAVE_HEADERS) { // Save headers in response + // *FIXME: Implement this... ; } diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 232ee841d6..601937a943 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -103,22 +103,21 @@ public: std::string mReqURL; BufferArray * mReqBody; off_t mReqOffset; - size_t mReqLen; - curl_slist * mReqHeaders; + size_t mReqLength; + HttpHeaders * mReqHeaders; HttpOptions * mReqOptions; // Transport data bool mCurlActive; CURL * mCurlHandle; - curl_slist * mCurlHeaders; HttpService * mCurlService; + curl_slist * mCurlHeaders; // Result data HttpStatus mStatus; - int mReplyStatus; BufferArray * mReplyBody; off_t mReplyOffset; - size_t mReplyLen; + size_t mReplyLength; HttpHeaders * mReplyHeaders; }; // end class HttpOpRequest diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 28aea27f38..192bc73b31 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -39,6 +39,7 @@ class HttpService; class HttpOpRequest; +/// Implements class-based queuing policies for an HttpService instance. class HttpPolicy { public: @@ -58,7 +59,7 @@ protected: typedef std::vector ready_queue_t; protected: - HttpService * mService; // Naked pointer, not refcounted + HttpService * mService; // Naked pointer, not refcounted, not owner ready_queue_t mReadyQueue; }; // end class HttpPolicy diff --git a/indra/llcorehttp/_httpreplyqueue.h b/indra/llcorehttp/_httpreplyqueue.h index 56dadec87c..28cb1d68b7 100644 --- a/indra/llcorehttp/_httpreplyqueue.h +++ b/indra/llcorehttp/_httpreplyqueue.h @@ -58,7 +58,7 @@ class HttpOperation; /// will be coded anyway so it shouldn't be too much of a /// burden. -class HttpReplyQueue: public LLCoreInt::RefCounted +class HttpReplyQueue : public LLCoreInt::RefCounted { public: /// Caller acquires a Refcount on construction diff --git a/indra/llcorehttp/_httprequestqueue.h b/indra/llcorehttp/_httprequestqueue.h index 3a9ce0c3c6..f96bd7520c 100644 --- a/indra/llcorehttp/_httprequestqueue.h +++ b/indra/llcorehttp/_httprequestqueue.h @@ -46,7 +46,7 @@ class HttpOperation; /// requests from all HttpRequest instances into the /// singleton HttpService instance. -class HttpRequestQueue: public LLCoreInt::RefCounted +class HttpRequestQueue : public LLCoreInt::RefCounted { protected: /// Caller acquires a Refcount on construction diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index c052e35452..ba76e1eeca 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -152,8 +152,8 @@ protected: LLCoreInt::HttpThread * mThread; // === working-thread-only data === - HttpPolicy * mPolicy; - HttpLibcurl * mTransport; + HttpPolicy * mPolicy; // Simple pointer, has ownership + HttpLibcurl * mTransport; // Simple pointer, has ownership }; // end class HttpService diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp index c37d081150..b5872606b8 100644 --- a/indra/llcorehttp/httpcommon.cpp +++ b/indra/llcorehttp/httpcommon.cpp @@ -37,22 +37,82 @@ HttpStatus::type_enum_t EXT_CURL_EASY; HttpStatus::type_enum_t EXT_CURL_MULTI; HttpStatus::type_enum_t LLCORE; +HttpStatus::operator unsigned long() const +{ + static const int shift(sizeof(unsigned long) * 4); + + unsigned long result(((unsigned long) mType) << shift | (unsigned long) (int) mStatus); + return result; +} + + std::string HttpStatus::toString() const { static const char * llcore_errors[] = { "", + "HTTP error reply status", "Services shutting down", "Operation canceled", "Invalid Content-Range header encountered" }; static const int llcore_errors_count(sizeof(llcore_errors) / sizeof(llcore_errors[0])); + + static const struct + { + type_enum_t mCode; + char * mText; + } + http_errors[] = + { + // Keep sorted by mCode, we binary search this list. + { 100, "Continue" }, + { 101, "Switching Protocols" }, + { 200, "OK" }, + { 201, "Created" }, + { 202, "Accepted" }, + { 203, "Non-Authoritative Information" }, + { 204, "No Content" }, + { 205, "Reset Content" }, + { 206, "Partial Content" }, + { 300, "Multiple Choices" }, + { 301, "Moved Permanently" }, + { 302, "Found" }, + { 303, "See Other" }, + { 304, "Not Modified" }, + { 305, "Use Proxy" }, + { 307, "Temporary Redirect" }, + { 400, "Bad Request" }, + { 401, "Unauthorized" }, + { 402, "Payment Required" }, + { 403, "Forbidden" }, + { 404, "Not Found" }, + { 405, "Method Not Allowed" }, + { 406, "Not Acceptable" }, + { 407, "Proxy Authentication Required" }, + { 408, "Request Time-out" }, + { 409, "Conflict" }, + { 410, "Gone" }, + { 411, "Length Required" }, + { 412, "Precondition Failed" }, + { 413, "Request Entity Too Large" }, + { 414, "Request-URI Too Large" }, + { 415, "Unsupported Media Type" }, + { 416, "Requested range not satisfiable" }, + { 417, "Expectation Failed" }, + { 500, "Internal Server Error" }, + { 501, "Not Implemented" }, + { 502, "Bad Gateway" }, + { 503, "Service Unavailable" }, + { 504, "Gateway Time-out" }, + { 505, "HTTP Version not supported" } + }; + static const int http_errors_count(sizeof(http_errors) / sizeof(http_errors[0])); if (*this) { return std::string(""); } - switch (mType) { case EXT_CURL_EASY: @@ -67,7 +127,33 @@ std::string HttpStatus::toString() const return std::string(llcore_errors[mStatus]); } break; - + + default: + if (isHttpStatus()) + { + int bottom(0), top(http_errors_count); + while (true) + { + int at((bottom + top) / 2); + if (mType == http_errors[at].mCode) + { + return std::string(http_errors[at].mText); + } + if (at == bottom) + { + break; + } + else if (mType < http_errors[at].mCode) + { + top = at; + } + else + { + bottom = at; + } + } + } + break; } return std::string("Unknown error"); } diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index 9de5769d57..f81be7103e 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -122,23 +122,38 @@ enum HttpError // Successful value compatible with the libcurl codes. HE_SUCCESS = 0, + // Intended for HTTP reply codes 100-999, indicates that + // the reply should be considered an error by the application. + HE_REPLY_ERROR = 1, + // Service is shutting down and requested operation will // not be queued or performed. - HE_SHUTTING_DOWN = 1, + HE_SHUTTING_DOWN = 2, // Operation was canceled by request. - HE_OP_CANCELED = 2, + HE_OP_CANCELED = 3, // Invalid content range header received. - HE_INV_CONTENT_RANGE_HDR = 3 + HE_INV_CONTENT_RANGE_HDR = 4 }; // end enum HttpError -/// HttpStatus encapsulates errors from libcurl (easy, multi) as well as -/// internal errors. The encapsulation isn't expected to completely -/// isolate the caller from libcurl but basic operational tests (success -/// or failure) are provided. +/// HttpStatus encapsulates errors from libcurl (easy, multi), HTTP +/// reply status codes and internal errors as well. The encapsulation +/// isn't expected to completely isolate the caller from libcurl but +/// basic operational tests (success or failure) are provided. +/// +/// Non-HTTP status are encoded as (type, status) with type being +/// one of: EXT_CURL_EASY, EXT_CURL_MULTI or LLCORE and status +/// being the success/error code from that domain. HTTP status +/// is encoded as (status, error_flag). Status should be in the +/// range [100, 999] and error_flag is either HE_SUCCESS or +/// HE_REPLY_ERROR to indicate whether this should be treated as +/// a successful status or an error. The application is responsible +/// for making that determination and a range like [200, 299] isn't +/// automatically assumed to be definitive. + struct HttpStatus { typedef unsigned short type_enum_t; @@ -192,12 +207,42 @@ struct HttpStatus return 0 != mStatus; } + /// Equality and inequality tests to bypass bool conversion + /// which will do the wrong thing in conditional expressions. + bool operator==(const HttpStatus & rhs) const + { + return mType == rhs.mType && mStatus == rhs.mStatus; + } + + bool operator!=(const HttpStatus & rhs) const + { + return ! operator==(rhs); + } + + /// Convert to single numeric representation. Mainly + /// for logging or other informal purposes. Also + /// creates an ambiguous second path to integer conversion + /// which tends to find programming errors such as formatting + /// the status to a stream (operator<<). + operator unsigned long() const; + unsigned long toULong() const + { + return operator unsigned long(); + } + /// Convert status to a string representation. For /// success, returns an empty string. For failure /// statuses, a string as appropriate for the source of /// the error code (libcurl easy, libcurl multi, or /// LLCore itself). std::string toString() const; + + /// Returns true if the status value represents an + /// HTTP response status (100 - 999). + bool isHttpStatus() const + { + return mType >= type_enum_t(100) && mType <= type_enum_t(999); + } }; // end struct HttpStatus diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 2a87f5231a..6c62f931ff 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -31,6 +31,7 @@ #include "_httpservice.h" #include "_httpoperation.h" #include "_httpoprequest.h" +#include "_httpopcancel.h" namespace @@ -189,6 +190,22 @@ HttpHandle HttpRequest::requestGetByteRange(unsigned int policy_id, } +HttpHandle HttpRequest::requestCancel(HttpHandle handle, HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpCancel * op = new HttpOpCancel(handle); + op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + mRequestQueue->addOp(op); // transfer refcount as well + + mLastReqStatus = status; + ret_handle = static_cast(op); + + return ret_handle; +} + + HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) { HttpStatus status; diff --git a/indra/llcorehttp/httpresponse.cpp b/indra/llcorehttp/httpresponse.cpp index 9ac8276f05..3dcdadb337 100644 --- a/indra/llcorehttp/httpresponse.cpp +++ b/indra/llcorehttp/httpresponse.cpp @@ -35,7 +35,8 @@ namespace LLCore HttpResponse::HttpResponse() : LLCoreInt::RefCounted(true), - mReplyStatus(0U), + mReplyOffset(0U), + mReplyLength(0U), mBufferArray(NULL), mHeaders(NULL) {} diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h index a25e22aef6..5cf3a919f4 100644 --- a/indra/llcorehttp/httpresponse.h +++ b/indra/llcorehttp/httpresponse.h @@ -67,8 +67,6 @@ protected: public: /// Returns the final status of the requested operation. /// - // *FIXME: Haven't incorporated HTTP status into this yet. - // Will do soon. HttpStatus getStatus() const { return mStatus; @@ -79,19 +77,6 @@ public: mStatus = status; } - /// Fetch the HTTP reply status. This is only guaranteed to be - /// valid if the HttpStatus tests successful and was the result - /// of a completed HTTP request. - unsigned int getReplyStatus() const - { - return mReplyStatus; - } - - void setReplyStatus(unsigned int status) - { - mReplyStatus = status; - } - /// Simple getter for the response body returned as a scatter/gather /// buffer. If the operation doesn't produce data (such as the Null /// or StopThread operations), this may be NULL. @@ -122,11 +107,26 @@ public: /// Behaves like @see setResponse() but for header data. void setHeaders(HttpHeaders * headers); + + /// If a 'Range:' header was used, these methods are involved + /// in setting and returning data about the actual response. + void getRange(unsigned int * offset, unsigned int * length) const + { + *offset = mReplyOffset; + *length = mReplyLength; + } + + void setRange(unsigned int offset, unsigned int length) + { + mReplyOffset = offset; + mReplyLength = length; + } protected: // Response data here HttpStatus mStatus; - unsigned int mReplyStatus; + unsigned int mReplyOffset; + unsigned int mReplyLength; BufferArray * mBufferArray; HttpHeaders * mHeaders; }; diff --git a/indra/llcorehttp/tests/test_httpstatus.hpp b/indra/llcorehttp/tests/test_httpstatus.hpp index 38bf494dec..f7b542d3b5 100644 --- a/indra/llcorehttp/tests/test_httpstatus.hpp +++ b/indra/llcorehttp/tests/test_httpstatus.hpp @@ -157,6 +157,108 @@ void HttpStatusTestObjectType::test<4>() ensure(! msg.empty()); } +template <> template <> +void HttpStatusTestObjectType::test<5>() +{ + set_test_name("HttpStatus equality/inequality testing"); + + // Make certain equality/inequality tests do not pass + // through the bool conversion. Distinct successful + // and error statuses should compare unequal. + + HttpStatus status1(HttpStatus::LLCORE, HE_SUCCESS); + HttpStatus status2(HttpStatus::EXT_CURL_EASY, HE_SUCCESS); + ensure(status1 != status2); + + status1.mType = HttpStatus::LLCORE; + status1.mStatus = HE_REPLY_ERROR; + status2.mType = HttpStatus::LLCORE; + status2.mStatus= HE_SHUTTING_DOWN; + ensure(status1 != status2); +} + +template <> template <> +void HttpStatusTestObjectType::test<6>() +{ + set_test_name("HttpStatus basic HTTP status encoding"); + + HttpStatus status; + status.mType = 200; + status.mStatus = HE_SUCCESS; + std::string msg = status.toString(); + ensure(msg.empty()); + ensure(bool(status)); + + // Normally a success but application says error + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(! msg.empty()); + ensure(! bool(status)); + ensure(status.toULong() > 1UL); // Biggish number, not a bool-to-ulong + + // Same statuses with distinct success/fail are distinct + status.mType = 200; + status.mStatus = HE_SUCCESS; + HttpStatus status2(200, HE_REPLY_ERROR); + ensure(status != status2); + + // Normally an error but application says okay + status.mType = 406; + status.mStatus = HE_SUCCESS; + msg = status.toString(); + ensure(msg.empty()); + ensure(bool(status)); + + // Different statuses but both successful are distinct + status.mType = 200; + status.mStatus = HE_SUCCESS; + status2.mType = 201; + status2.mStatus = HE_SUCCESS; + ensure(status != status2); + + // Different statuses but both failed are distinct + status.mType = 200; + status.mStatus = HE_REPLY_ERROR; + status2.mType = 201; + status2.mStatus = HE_REPLY_ERROR; + ensure(status != status2); +} + +template <> template <> +void HttpStatusTestObjectType::test<7>() +{ + set_test_name("HttpStatus HTTP error text strings"); + + HttpStatus status(100, HE_REPLY_ERROR); + std::string msg(status.toString()); + ensure(! msg.empty()); // Should be something + ensure(msg == "Continue"); + + status.mStatus = HE_SUCCESS; + msg = status.toString(); + ensure(msg.empty()); // Success is empty + + status.mType = 199; + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(msg == "Unknown error"); + + status.mType = 505; // Last defined string + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(msg == "HTTP Version not supported"); + + status.mType = 506; // One beyond + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(msg == "Unknown error"); + + status.mType = 999; // Last HTTP status + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(msg == "Unknown error"); +} + } // end namespace tut #endif // TEST_HTTP_STATUS_H diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 1174d108d2..8e6deb9cce 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -212,6 +212,11 @@ #include "llmachineid.h" #include "llmainlooprepeater.h" +// LLCore::HTTP +#include "httpcommon.h" +#include "httprequest.h" +#include "httphandler.h" + // *FIX: These extern globals should be cleaned up. // The globals either represent state/config/resource-storage of either // this app, or another 'component' of the viewer. App globals should be @@ -326,6 +331,53 @@ static std::string gLaunchFileOnQuit; // Used on Win32 for other apps to identify our window (eg, win_setup) const char* const VIEWER_WINDOW_CLASSNAME = "Second Life"; +namespace +{ + +// This class manages the lifecyle of the core http library. +// Slightly different style than traditional code but reflects +// the use of handler classes and light-weight interface +// object instances of the new libraries. To be used +// as a singleton and static construction is fine. +class CoreHttp : public LLCore::HttpHandler +{ +public: + CoreHttp(); + ~CoreHttp(); + + // Initialize the LLCore::HTTP library creating service classes + // and starting the servicing thread. Caller is expected to do + // other initializations (SSL mutex, thread hash function) appropriate + // for the application. + void init(); + + // Request that the servicing thread stop servicing requests, + // release resource references and stop. + void requestStop(); + + // Terminate LLCore::HTTP library services. Caller is expected + // to have made a best-effort to shutdown the servicing thread + // by issuing a requestThreadStop() and waiting for completion + // notification that the stop has completed. + void cleanup(); + + // Notification when the stop request is complete. + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + +private: + static const F64 MAX_THREAD_WAIT_TIME; + +private: + LLCore::HttpRequest * mRequest; + LLCore::HttpHandle mStopHandle; + F64 mStopRequested; + bool mStopped; +}; + +CoreHttp coreHttpLib; + +} // end anonymous namespace + //-- LLDeferredTaskList ------------------------------------------------------ /** @@ -720,6 +772,9 @@ bool LLAppViewer::init() LLViewerStatsRecorder::initClass(); #endif + // Initialize the non-LLCurl libcurl library + coreHttpLib.init(); + // *NOTE:Mani - LLCurl::initClass is not thread safe. // Called before threads are created. LLCurl::initClass(gSavedSettings.getF32("CurlRequestTimeOut"), @@ -1807,6 +1862,7 @@ bool LLAppViewer::cleanup() // Delete workers first // shotdown all worker threads before deleting them in case of co-dependencies + coreHttpLib.requestStop(); sTextureFetch->shutdown(); sTextureCache->shutdown(); sImageDecodeThread->shutdown(); @@ -1890,6 +1946,9 @@ bool LLAppViewer::cleanup() // *NOTE:Mani - The following call is not thread safe. LLCurl::cleanupClass(); + // Non-LLCurl libcurl library + coreHttpLib.cleanup(); + // If we're exiting to launch an URL, do that here so the screen // is at the right resolution before we launch IE. if (!gLaunchFileOnQuit.empty()) @@ -5267,3 +5326,103 @@ void LLAppViewer::metricsSend(bool enable_reporting) gViewerAssetStatsMain->reset(); } +namespace +{ + +const F64 CoreHttp::MAX_THREAD_WAIT_TIME(10.0); + +CoreHttp::CoreHttp() + : mRequest(NULL), + mStopHandle(LLCORE_HTTP_HANDLE_INVALID), + mStopRequested(0.0), + mStopped(false) +{} + + +CoreHttp::~CoreHttp() +{ + delete mRequest; + mRequest = NULL; +} + + +void CoreHttp::init() +{ + LLCore::HttpStatus status = LLCore::HttpRequest::createService(); + if (! status) + { + LL_ERRS("Init") << "Failed to initialize HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } + + status = LLCore::HttpRequest::startThread(); + if (! status) + { + LL_ERRS("Init") << "Failed to start HTTP servicing thread. Reason: " + << status.toString() + << LL_ENDL; + } + + mRequest = new LLCore::HttpRequest; +} + + +void CoreHttp::requestStop() +{ + llassert_always(mRequest); + + mStopHandle = mRequest->requestStopThread(this); + if (LLCORE_HTTP_HANDLE_INVALID != mStopHandle) + { + mStopRequested = LLTimer::getTotalSeconds(); + } +} + + +void CoreHttp::cleanup() +{ + if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle) + { + // Should have been started already... + requestStop(); + } + + if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle) + { + LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services without thread shutdown" + << LL_ENDL; + } + else + { + while (! mStopped && LLTimer::getTotalSeconds() < (mStopRequested + MAX_THREAD_WAIT_TIME)) + { + mRequest->update(200); + ms_sleep(50); + } + if (! mStopped) + { + LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services with thread shutdown incomplete" + << LL_ENDL; + } + } + + delete mRequest; + mRequest = NULL; + + LLCore::HttpStatus status = LLCore::HttpRequest::destroyService(); + if (! status) + { + LL_WARNS("Cleanup") << "Failed to shutdown HTTP services, continuing. Reason: " + << status.toString() + << LL_ENDL; + } +} + + +void CoreHttp::onCompleted(LLCore::HttpHandle, LLCore::HttpResponse *) +{ + mStopped = true; +} + +} // end anonymous namespace diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index f18aa8b4e6..17c68f7c22 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2012, 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 @@ -33,7 +33,6 @@ #include "lltexturefetch.h" -#include "llcurl.h" #include "lldir.h" #include "llhttpclient.h" #include "llhttpstatuscodes.h" @@ -53,11 +52,17 @@ #include "llviewerassetstats.h" #include "llworld.h" +#include "httprequest.h" +#include "httphandler.h" +#include "httpresponse.h" +#include "bufferarray.h" + + ////////////////////////////////////////////////////////////////////////////// -class LLTextureFetchWorker : public LLWorkerClass +class LLTextureFetchWorker : public LLWorkerClass, public LLCore::HttpHandler + { friend class LLTextureFetch; - friend class HTTPGetResponder; private: class CacheReadResponder : public LLTextureCache::ReadResponder @@ -147,15 +152,14 @@ public: ~LLTextureFetchWorker(); // void relese() { --mActiveCount; } - S32 callbackHttpGet(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer, - bool partial, bool success); + S32 callbackHttpGet(LLCore::HttpResponse * response, + bool partial, bool success); void callbackCacheRead(bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal); void callbackCacheWrite(bool success); void callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux); - void setGetStatus(U32 status, const std::string& reason) + void setGetStatus(LLCore::HttpStatus status, const std::string& reason) { LLMutexLock lock(&mWorkMutex); @@ -167,6 +171,9 @@ public: bool getCanUseHTTP() const { return mCanUseHTTP; } LLTextureFetch & getFetcher() { return *mFetcher; } + + // Inherited from LLCore::HttpHandler + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); protected: LLTextureFetchWorker(LLTextureFetch* fetcher, const std::string& url, const LLUUID& id, const LLHost& host, @@ -191,9 +198,15 @@ private: void lockWorkMutex() { mWorkMutex.lock(); } void unlockWorkMutex() { mWorkMutex.unlock(); } + void recordTextureStart(bool is_http); + void recordTextureDone(bool is_http); + private: enum e_state // mState { + // *NOTE: Do not change the order/value of state variables, some code + // depends upon specific ordering/adjacency. + // NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack) INVALID = 0, INIT, @@ -244,9 +257,8 @@ private: LLFrameTimer mFetchTimer; LLTextureCache::handle_t mCacheReadHandle; LLTextureCache::handle_t mCacheWriteHandle; - U8* mBuffer; - S32 mBufferSize; S32 mRequestedSize; + S32 mRequestedOffset; S32 mDesiredSize; S32 mFileSize; S32 mCachedSize; @@ -263,7 +275,7 @@ private: S32 mHTTPFailCount; S32 mRetryAttempt; S32 mActiveCount; - U32 mGetStatus; + LLCore::HttpStatus mGetStatus; std::string mGetReason; // Work Data @@ -283,106 +295,17 @@ private: U8 mImageCodec; LLViewerAssetStats::duration_t mMetricsStartTime; + + LLCore::HttpHandle mHttpHandle; + LLCore::BufferArray * mHttpBufferArray; + int mHttpPolicyClass; + bool mHttpActive; + unsigned int mHttpReplySize; + unsigned int mHttpReplyOffset; }; ////////////////////////////////////////////////////////////////////////////// -class HTTPGetResponder : public LLCurl::Responder -{ - LOG_CLASS(HTTPGetResponder); -public: - HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id, U64 startTime, S32 requestedSize, U32 offset, bool redir) - : mFetcher(fetcher), mID(id), mStartTime(startTime), mRequestedSize(requestedSize), mOffset(offset), mFollowRedir(redir) - { - } - ~HTTPGetResponder() - { - } - - virtual void completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - static LLCachedControl log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog"); - static LLCachedControl log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator"); - static LLCachedControl log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ; - - if (log_to_viewer_log || log_to_sim) - { - mFetcher->mTextureInfo.setRequestStartTime(mID, mStartTime); - U64 timeNow = LLTimer::getTotalTime(); - mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP); - mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize); - mFetcher->mTextureInfo.setRequestOffset(mID, mOffset); - mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow); - } - - lldebugs << "HTTP COMPLETE: " << mID << llendl; - LLTextureFetchWorker* worker = mFetcher->getWorker(mID); - if (worker) - { - bool success = false; - bool partial = false; - if (HTTP_OK <= status && status < HTTP_MULTIPLE_CHOICES) - { - success = true; - if (HTTP_PARTIAL_CONTENT == status) // partial information - { - partial = true; - } - } - - if (!success) - { - worker->setGetStatus(status, reason); -// llwarns << "CURL GET FAILED, status:" << status << " reason:" << reason << llendl; - } - - S32 data_size = worker->callbackHttpGet(channels, buffer, partial, success); - - if(log_texture_traffic && data_size > 0) - { - LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID) ; - if(tex) - { - gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ; - } - } - - mFetcher->removeFromHTTPQueue(mID, data_size); - - if (worker->mMetricsStartTime) - { - LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, - true, - LLImageBase::TYPE_AVATAR_BAKE == worker->mType, - LLViewerAssetStatsFF::get_timestamp() - worker->mMetricsStartTime); - worker->mMetricsStartTime = 0; - } - LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE, - true, - LLImageBase::TYPE_AVATAR_BAKE == worker->mType); - } - else - { - mFetcher->removeFromHTTPQueue(mID); - llwarns << "Worker not found: " << mID << llendl; - } - } - - virtual bool followRedir() - { - return mFollowRedir; - } - -private: - LLTextureFetch* mFetcher; - LLUUID mID; - U64 mStartTime; - S32 mRequestedSize; - U32 mOffset; - bool mFollowRedir; -}; ////////////////////////////////////////////////////////////////////////////// @@ -639,6 +562,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, S32 discard, // Desired discard S32 size) // Desired size : LLWorkerClass(fetcher, "TextureFetch"), + LLCore::HttpHandler(), mState(INIT), mWriteToCacheState(NOT_WRITE), mFetcher(fetcher), @@ -655,9 +579,8 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mDecodedDiscard(-1), mCacheReadHandle(LLTextureCache::nullHandle()), mCacheWriteHandle(LLTextureCache::nullHandle()), - mBuffer(NULL), - mBufferSize(0), mRequestedSize(0), + mRequestedOffset(0), mDesiredSize(TEXTURE_CACHE_ENTRY_SIZE), mFileSize(0), mCachedSize(0), @@ -673,13 +596,18 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mHTTPFailCount(0), mRetryAttempt(0), mActiveCount(0), - mGetStatus(0), mWorkMutex(NULL), mFirstPacket(0), mLastPacket(-1), mTotalPackets(0), mImageCodec(IMG_CODEC_INVALID), - mMetricsStartTime(0) + mMetricsStartTime(0), + mHttpHandle(LLCORE_HTTP_HANDLE_INVALID), + mHttpBufferArray(NULL), + mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mHttpActive(false), + mHttpReplySize(0U), + mHttpReplyOffset(0U) { mCanUseNET = mUrl.empty() ; @@ -701,6 +629,11 @@ LLTextureFetchWorker::~LLTextureFetchWorker() // << " Requested=" << mRequestedDiscard // << " Desired=" << mDesiredDiscard << llendl; llassert_always(!haveWork()); + if (mHttpActive) + { + LL_WARNS("Texture") << "Deleting worker object while HTTP request is active." + << LL_ENDL; + } lockWorkMutex(); if (mCacheReadHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache) { @@ -714,6 +647,11 @@ LLTextureFetchWorker::~LLTextureFetchWorker() clearPackets(); unlockWorkMutex(); mFetcher->removeFromHTTPQueue(mID); + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } } void LLTextureFetchWorker::clearPackets() @@ -797,7 +735,7 @@ void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size) if ((prioritize && mState == INIT) || mState == DONE) { mState = INIT; - U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH; + U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_LOW; setPriority(work_priority); } } @@ -810,16 +748,18 @@ void LLTextureFetchWorker::setImagePriority(F32 priority) { mImagePriority = priority; calcWorkPriority(); - U32 work_priority = mWorkPriority | (getPriority() & LLWorkerThread::PRIORITY_HIGHBITS); + U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_LOW; setPriority(work_priority); } } void LLTextureFetchWorker::resetFormattedData() { - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); - mBuffer = NULL; - mBufferSize = 0; + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } if (mFormattedImage.notNull()) { mFormattedImage->deleteData(); @@ -875,6 +815,14 @@ bool LLTextureFetchWorker::doWork(S32 param) mFetchTimer.reset(); } + static LLUUID last_id; + if (mID != last_id) + { + // LL_WARNS("Texture") << "DOWORK SWITCH: " << last_id << " to: " << mID + // << LL_ENDL; + last_id = mID; + } + if (mState == INIT) { mRawImage = NULL ; @@ -882,15 +830,18 @@ bool LLTextureFetchWorker::doWork(S32 param) mLoadedDiscard = -1; mDecodedDiscard = -1; mRequestedSize = 0; + mRequestedOffset = 0; mFileSize = 0; mCachedSize = 0; mLoaded = FALSE; mSentRequest = UNSENT; mDecoded = FALSE; mWritten = FALSE; - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); - mBuffer = NULL; - mBufferSize = 0; + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } mHaveAllData = FALSE; clearPackets(); // TODO: Shouldn't be necessary mCacheReadHandle = LLTextureCache::nullHandle(); @@ -904,6 +855,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == LOAD_FROM_TEXTURE_CACHE) { + setPriority(0); // Set priority first since Responder may change it if (mCacheReadHandle == LLTextureCache::nullHandle()) { U32 cache_priority = mWorkPriority; @@ -919,8 +871,6 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mUrl.compare(0, 7, "file://") == 0) { - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it - // read file from local disk std::string filename = mUrl.substr(7, std::string::npos); CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); @@ -929,8 +879,6 @@ bool LLTextureFetchWorker::doWork(S32 param) } else if (mUrl.empty()) { - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it - CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority, offset, size, responder); @@ -942,12 +890,12 @@ bool LLTextureFetchWorker::doWork(S32 param) // *TODO:?remove this warning llwarns << "Unknown URL Type: " << mUrl << llendl; } - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = SEND_HTTP_REQ; } else { - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = LOAD_FROM_NETWORK; } } @@ -959,6 +907,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { mCacheReadHandle = LLTextureCache::nullHandle(); mState = CACHE_POST; + setPriority(LLWorkerThread::PRIORITY_HIGH); // fall through } else @@ -982,6 +931,7 @@ bool LLTextureFetchWorker::doWork(S32 param) llassert_always(mFormattedImage->getDataSize() > 0); mLoadedDiscard = mDesiredDiscard; mState = DECODE_IMAGE; + setPriority(LLWorkerThread::PRIORITY_HIGH); mWriteToCacheState = NOT_WRITE ; LL_DEBUGS("Texture") << mID << ": Cached. Bytes: " << mFormattedImage->getDataSize() << " Size: " << llformat("%dx%d",mFormattedImage->getWidth(),mFormattedImage->getHeight()) @@ -999,6 +949,7 @@ bool LLTextureFetchWorker::doWork(S32 param) else { LL_DEBUGS("Texture") << mID << ": Not in Cache" << LL_ENDL; + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = LOAD_FROM_NETWORK; } // fall through @@ -1009,6 +960,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { static LLCachedControl use_http(gSavedSettings,"ImagePipelineUseHTTP"); + setPriority(0); // if (mHost != LLHost::invalid) get_url = false; if ( use_http && mCanUseHTTP && mUrl.empty())//get http url. { @@ -1040,8 +992,8 @@ bool LLTextureFetchWorker::doWork(S32 param) } if (mCanUseHTTP && !mUrl.empty()) { - mState = LLTextureFetchWorker::SEND_HTTP_REQ; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + mState = SEND_HTTP_REQ; + setPriority(LLWorkerThread::PRIORITY_HIGH); if(mWriteToCacheState != NOT_WRITE) { mWriteToCacheState = CAN_WRITE ; @@ -1057,14 +1009,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mRequestedDiscard = mDesiredDiscard; mSentRequest = QUEUED; mFetcher->addToNetworkQueue(this); - if (! mMetricsStartTime) - { - mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - } - LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType); - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + recordTextureStart(false); return false; } @@ -1074,12 +1019,7 @@ bool LLTextureFetchWorker::doWork(S32 param) //llassert_always(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end()); // Make certain this is in the network queue //mFetcher->addToNetworkQueue(this); - //if (! mMetricsStartTime) - //{ - // mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - //} - //LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, false, - // LLImageBase::TYPE_AVATAR_BAKE == mType); + //recordTextureStart(false); //setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } @@ -1087,6 +1027,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == LOAD_FROM_SIMULATOR) { + setPriority(0); if (mFormattedImage.isNull()) { mFormattedImage = new LLImageJ2C; @@ -1101,39 +1042,22 @@ bool LLTextureFetchWorker::doWork(S32 param) // llwarns << "processSimulatorPackets() failed to load buffer" << llendl; return true; // failed } - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DECODE_IMAGE; mWriteToCacheState = SHOULD_WRITE; - - if (mMetricsStartTime) - { - LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType, - LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime); - mMetricsStartTime = 0; - } - LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType); + recordTextureDone(false); } else { mFetcher->addToNetworkQueue(this); // failsafe - if (! mMetricsStartTime) - { - mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - } - LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType); - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + recordTextureStart(false); } return false; } if (mState == SEND_HTTP_REQ) { + setPriority(0); if(mCanUseHTTP) { //NOTE: @@ -1159,6 +1083,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { // We already have all the data, just decode it mLoadedDiscard = mFormattedImage->getDiscardLevel(); + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DECODE_IMAGE; return false; } @@ -1171,44 +1096,48 @@ bool LLTextureFetchWorker::doWork(S32 param) mRequestedSize = mDesiredSize; mRequestedDiscard = mDesiredDiscard; mRequestedSize -= cur_size; - S32 offset = cur_size; - mBufferSize = cur_size; // This will get modified by callbackHttpGet() + mRequestedOffset = cur_size; - bool res = false; + mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; if (!mUrl.empty()) { mLoaded = FALSE; - mGetStatus = 0; + mGetStatus = LLCore::HttpStatus(); mGetReason.clear(); - LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << offset + LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset << " Bytes: " << mRequestedSize << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth << LL_ENDL; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - mState = WAIT_HTTP_REQ; - - mFetcher->addToHTTPQueue(mID); - if (! mMetricsStartTime) - { - mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - } - LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, - true, - LLImageBase::TYPE_AVATAR_BAKE == mType); +// LL_WARNS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset +// << " Bytes: " << mRequestedSize +// << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth +// << LL_ENDL; // Will call callbackHttpGet when curl request completes - std::vector headers; - headers.push_back("Accept: image/x-j2c"); - res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize, - new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true)); + // *FIXME: enable redirection follow + mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass, + mRequestedPriority, + mUrl, + mRequestedOffset, + mRequestedSize, + mFetcher->mHttpOptions, + mFetcher->mHttpHeaders, + this); } - if (!res) + if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle) { llwarns << "HTTP GET request failed for " << mID << llendl; resetFormattedData(); ++mHTTPFailCount; return true; // failed } + + mHttpActive = true; + mFetcher->addToHTTPQueue(mID); + recordTextureStart(true); + setPriority(LLWorkerThread::PRIORITY_HIGH); + mState = WAIT_HTTP_REQ; + // fall through } else //can not use http fetch. @@ -1219,27 +1148,28 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == WAIT_HTTP_REQ) { + setPriority(0); if (mLoaded) { S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; if (mRequestedSize < 0) { S32 max_attempts; - if (mGetStatus == HTTP_NOT_FOUND) + if (mGetStatus == LLCore::HttpStatus(HTTP_NOT_FOUND, LLCore::HE_REPLY_ERROR)) { mHTTPFailCount = max_attempts = 1; // Don't retry llwarns << "Texture missing from server (404): " << mUrl << llendl; //roll back to try UDP - if(mCanUseNET) + if (mCanUseNET) { - mState = INIT ; - mCanUseHTTP = false ; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - return false ; + mState = INIT; + mCanUseHTTP = false; + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + return false; } } - else if (mGetStatus == HTTP_SERVICE_UNAVAILABLE) + else if (mGetStatus == LLCore::HttpStatus(HTTP_SERVICE_UNAVAILABLE, LLCore::HE_REPLY_ERROR)) { // *TODO: Should probably introduce a timer here to delay future HTTP requsts // for a short time (~1s) to ease server load? Ideally the server would queue @@ -1254,7 +1184,7 @@ bool LLTextureFetchWorker::doWork(S32 param) max_attempts = HTTP_MAX_RETRY_COUNT + 1; ++mHTTPFailCount; llinfos << "HTTP GET failed for: " << mUrl - << " Status: " << mGetStatus << " Reason: '" << mGetReason << "'" + << " Status: " << mGetStatus.toULong() << " Reason: '" << mGetReason << "'" << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl; } @@ -1264,12 +1194,14 @@ bool LLTextureFetchWorker::doWork(S32 param) { // Use available data mLoadedDiscard = mFormattedImage->getDiscardLevel(); + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DECODE_IMAGE; return false; } else { resetFormattedData(); + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DONE; return true; // failed } @@ -1281,17 +1213,25 @@ bool LLTextureFetchWorker::doWork(S32 param) } } - llassert_always(mBufferSize == cur_size + mRequestedSize); - if(!mBufferSize)//no data received. + if (! mHttpBufferArray || ! mHttpBufferArray->size()) { - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); - mBuffer = NULL; + // no data received. + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } - //abort. + // abort. + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DONE; return true; } + const S32 append_size(mHttpBufferArray->size()); + const S32 total_size(cur_size + append_size); + llassert_always(append_size == mRequestedSize); + if (mFormattedImage.isNull()) { // For now, create formatted image based on extension @@ -1305,49 +1245,52 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mHaveAllData && mRequestedDiscard == 0) //the image file is fully loaded. { - mFileSize = mBufferSize; + mFileSize = total_size; } else //the file size is unknown. { - mFileSize = mBufferSize + 1 ; //flag the file is not fully loaded. + mFileSize = total_size + 1 ; //flag the file is not fully loaded. } - U8* buffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), mBufferSize); + U8 * buffer = (U8 *) ALLOCATE_MEM(LLImageBase::getPrivatePool(), total_size); if (cur_size > 0) { memcpy(buffer, mFormattedImage->getData(), cur_size); } - memcpy(buffer + cur_size, mBuffer, mRequestedSize); // append + mHttpBufferArray->seek(0); + mHttpBufferArray->read((char *) buffer + cur_size, append_size); + // NOTE: setData releases current data and owns new data (buffer) - mFormattedImage->setData(buffer, mBufferSize); - // delete temp data - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); // Note: not 'buffer' (assigned in setData()) - mBuffer = NULL; - mBufferSize = 0; + mFormattedImage->setData(buffer, total_size); + + // Done with buffer array + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + mLoadedDiscard = mRequestedDiscard; + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DECODE_IMAGE; - if(mWriteToCacheState != NOT_WRITE) + if (mWriteToCacheState != NOT_WRITE) { mWriteToCacheState = SHOULD_WRITE ; } - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); return false; } else { - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } } if (mState == DECODE_IMAGE) { + setPriority(0); static LLCachedControl textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled"); - if(textures_decode_disabled) + if (textures_decode_disabled) { // for debug use, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); return true; } @@ -1355,7 +1298,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { // We aborted, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); return true; } @@ -1365,7 +1308,7 @@ bool LLTextureFetchWorker::doWork(S32 param) //abort, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); return true; } if (mLoadedDiscard < 0) @@ -1374,10 +1317,10 @@ bool LLTextureFetchWorker::doWork(S32 param) //abort, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); return true; } - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + mRawImage = NULL; mAuxImage = NULL; llassert_always(mFormattedImage.notNull()); @@ -1394,6 +1337,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == DECODE_IMAGE_UPDATE) { + setPriority(0); if (mDecoded) { if (mDecodedDiscard < 0) @@ -1406,13 +1350,14 @@ bool LLTextureFetchWorker::doWork(S32 param) llassert_always(mDecodeHandle == 0); mFormattedImage = NULL; ++mRetryAttempt; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); mState = INIT; return false; } else { // llwarns << "UNABLE TO LOAD TEXTURE: " << mID << " RETRIES: " << mRetryAttempt << llendl; + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DONE; // failed } } @@ -1421,7 +1366,7 @@ bool LLTextureFetchWorker::doWork(S32 param) llassert_always(mRawImage.notNull()); LL_DEBUGS("Texture") << mID << ": Decoded. Discard: " << mDecodedDiscard << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = WRITE_TO_CACHE; } // fall through @@ -1434,10 +1379,12 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == WRITE_TO_CACHE) { + setPriority(0); if (mWriteToCacheState != SHOULD_WRITE || mFormattedImage.isNull()) { // If we're in a local cache or we didn't actually receive any new data, // or we failed to load anything, skip + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DONE; return false; } @@ -1457,6 +1404,7 @@ bool LLTextureFetchWorker::doWork(S32 param) setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it U32 cache_priority = mWorkPriority; mWritten = FALSE; + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = WAIT_ON_WRITE; CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID); mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority, @@ -1467,8 +1415,10 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == WAIT_ON_WRITE) { + setPriority(0); if (writeToCacheComplete()) { + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DONE; // fall through } @@ -1487,16 +1437,15 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == DONE) { + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); if (mDecodedDiscard >= 0 && mDesiredDiscard < mDecodedDiscard) { // More data was requested, return to INIT mState = INIT; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); return false; } else { - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return true; } } @@ -1504,6 +1453,71 @@ bool LLTextureFetchWorker::doWork(S32 param) return false; } +// virtual +void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ + static LLCachedControl log_to_viewer_log(gSavedSettings, "LogTextureDownloadsToViewerLog"); + static LLCachedControl log_to_sim(gSavedSettings, "LogTextureDownloadsToSimulator"); + static LLCachedControl log_texture_traffic(gSavedSettings, "LogTextureNetworkTraffic") ; + + mHttpActive = false; + + if (log_to_viewer_log || log_to_sim) + { + U64 timeNow = LLTimer::getTotalTime(); + mFetcher->mTextureInfo.setRequestStartTime(mID, mMetricsStartTime); + mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP); + mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize); + mFetcher->mTextureInfo.setRequestOffset(mID, mRequestedOffset); + mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow); + } + + bool success = true; + bool partial = false; + LLCore::HttpStatus status(response->getStatus()); + + lldebugs << "HTTP COMPLETE: " << mID + << " status: " << status.toULong() << " '" << status.toString() << "'" + << llendl; + unsigned int offset(0), length(0); + response->getRange(&offset, &length); +// llwarns << "HTTP COMPLETE: " << mID << " handle: " << handle +// << " status: " << status.toULong() << " '" << status.toString() << "'" +// << " req offset: " << mRequestedOffset << " req length: " << mRequestedSize +// << " offset: " << offset << " length: " << length +// << llendl; + + if (! status) + { + success = false; + std::string reason(status.toString()); + setGetStatus(status, reason); + llwarns << "CURL GET FAILED, status:" << status.toULong() << " reason:" << reason << llendl; + } + else + { + static const LLCore::HttpStatus par_status(LLCore::HttpStatus(HTTP_PARTIAL_CONTENT, LLCore::HE_SUCCESS)); + + partial = (par_status == status); + } + + S32 data_size = callbackHttpGet(response, partial, success); + + if (log_texture_traffic && data_size > 0) + { + LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID); + if (tex) + { + gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ; + } + } + + mFetcher->removeFromHTTPQueue(mID); + + recordTextureDone(true); +} + + // Called from MAIN thread void LLTextureFetchWorker::endWork(S32 param, bool aborted) { @@ -1537,6 +1551,14 @@ void LLTextureFetchWorker::finishWork(S32 param, bool completed) bool LLTextureFetchWorker::deleteOK() { bool delete_ok = true; + + if (mHttpActive) + { + // HTTP library has a pointer to this worker + // and will dereference it to do notification. + delete_ok = false; + } + // Allow any pending reads or writes to complete if (mCacheReadHandle != LLTextureCache::nullHandle()) { @@ -1642,9 +1664,8 @@ bool LLTextureFetchWorker::processSimulatorPackets() ////////////////////////////////////////////////////////////////////////////// -S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer, - bool partial, bool success) +S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, + bool partial, bool success) { S32 data_size = 0 ; @@ -1664,15 +1685,22 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, if (success) { // get length of stream: - data_size = buffer->countAfter(channels.in(), NULL); - + LLCore::BufferArray * body(response->getBody()); + data_size = body ? body->size() : 0; + LL_DEBUGS("Texture") << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL; if (data_size > 0) { // *TODO: set the formatted image data here directly to avoid the copy - mBuffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), data_size); - buffer->readAfter(channels.in(), NULL, mBuffer, data_size); - mBufferSize += data_size; + // *FIXME: deal with actual offset and actual datasize, don't assume + // server gave exactly what was asked for. + + llassert_always(NULL == mHttpBufferArray); + + // Hold on to body for later copy + body->addRef(); + mHttpBufferArray = body; + if (data_size < mRequestedSize && mRequestedDiscard == 0) { mHaveAllData = TRUE; @@ -1684,7 +1712,6 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, mHaveAllData = TRUE; llassert_always(mDecodeHandle == 0); mFormattedImage = NULL; // discard any previous data we had - mBufferSize = data_size; } } else @@ -1700,7 +1727,7 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, mRequestedSize = -1; // error } mLoaded = TRUE; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); return data_size ; } @@ -1729,7 +1756,7 @@ void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* ima } } mLoaded = TRUE; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); } void LLTextureFetchWorker::callbackCacheWrite(bool success) @@ -1741,7 +1768,7 @@ void LLTextureFetchWorker::callbackCacheWrite(bool success) return; } mWritten = TRUE; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); } ////////////////////////////////////////////////////////////////////////////// @@ -1779,7 +1806,7 @@ void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImag } mDecoded = TRUE; // llinfos << mID << " : DECODE COMPLETE " << llendl; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); } ////////////////////////////////////////////////////////////////////////////// @@ -1806,6 +1833,34 @@ bool LLTextureFetchWorker::writeToCacheComplete() } +void LLTextureFetchWorker::recordTextureStart(bool is_http) +{ + if (! mMetricsStartTime) + { + mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); + } + LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, + is_http, + LLImageBase::TYPE_AVATAR_BAKE == mType); +} + + +void LLTextureFetchWorker::recordTextureDone(bool is_http) +{ + if (mMetricsStartTime) + { + LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, + is_http, + LLImageBase::TYPE_AVATAR_BAKE == mType, + LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime); + mMetricsStartTime = 0; + } + LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE, + is_http, + LLImageBase::TYPE_AVATAR_BAKE == mType); +} + + ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // public @@ -1823,17 +1878,26 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mTextureBandwidth(0), mHTTPTextureBits(0), mTotalHTTPRequests(0), - mCurlGetRequest(NULL), - mQAMode(qa_mode) + mQAMode(qa_mode), + mHttpRequest(NULL), + mHttpOptions(NULL), + mHttpHeaders(NULL) { mCurlPOSTRequestCount = 0; mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold")); + + mHttpRequest = new LLCore::HttpRequest; + mHttpOptions = new LLCore::HttpOptions; + mHttpHeaders = new LLCore::HttpHeaders; + mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c"); } LLTextureFetch::~LLTextureFetch() { - clearDeleteList() ; + cancelHttpRequests(); + + clearDeleteList(); while (! mCommands.empty()) { @@ -1841,7 +1905,22 @@ LLTextureFetch::~LLTextureFetch() mCommands.erase(mCommands.begin()); delete req; } - + + if (mHttpOptions) + { + mHttpOptions->release(); + mHttpOptions = NULL; + } + + if (mHttpHeaders) + { + mHttpHeaders->release(); + mHttpHeaders = NULL; + } + + delete mHttpRequest; + mHttpRequest = NULL; + // ~LLQueuedThread() called here } @@ -1984,6 +2063,28 @@ void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id, S32 received_size) mHTTPTextureBits += received_size * 8; // Approximate - does not include header bits } +void LLTextureFetch::cancelHttpRequests() +{ + for (queue_t::iterator iter(mHTTPTextureQueue.begin()); + mHTTPTextureQueue.end() != iter; + ++iter) + { + LLTextureFetchWorker* worker = getWorker(*iter); + if (worker && worker->mHttpActive) + { + mHttpRequest->requestCancel(worker->mHttpHandle, NULL); + } + } + + // *FIXME: Do this better with less time wasting. + int tries(10); + while (! mHTTPTextureQueue.empty() && --tries) + { + mHttpRequest->update(100); + ms_sleep(100); + } +} + void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) { lockQueue() ; @@ -2194,11 +2295,21 @@ void LLTextureFetch::commonUpdate() cmdDoWork(); // Update Curl on same thread as mCurlGetRequest was constructed - S32 processed = mCurlGetRequest->process(); + LLCore::HttpStatus status = mHttpRequest->update(200); + if (! status) + { + LL_INFOS_ONCE("Texture") << "Problem during HTTP servicing. Reason: " + << status.toString() + << LL_ENDL; + } + +#if 0 + // *FIXME: maybe implement this another way... if (processed > 0) { lldebugs << "processed: " << processed << " messages." << llendl; } +#endif } @@ -2256,22 +2367,22 @@ void LLTextureFetch::shutDownImageDecodeThread() // WORKER THREAD void LLTextureFetch::startThread() { - // Construct mCurlGetRequest from Worker Thread - mCurlGetRequest = new LLCurlRequest(); } // WORKER THREAD +// +// This detaches the texture fetch thread from the LLCore +// HTTP library but doesn't stop the thread running in that +// library... void LLTextureFetch::endThread() { - // Destroy mCurlGetRequest from Worker Thread - delete mCurlGetRequest; - mCurlGetRequest = NULL; + cancelHttpRequests(); } // WORKER THREAD void LLTextureFetch::threadedUpdate() { - llassert_always(mCurlGetRequest); + llassert_always(mHttpRequest); // Limit update frequency const F32 PROCESS_TIME = 0.05f; @@ -2579,7 +2690,7 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 llassert_always(totalbytes > 0); llassert_always(data_size == FIRST_PACKET_SIZE || data_size == worker->mFileSize); res = worker->insertPacket(0, data, data_size); - worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); + worker->setPriority(LLWorkerThread::PRIORITY_HIGH); worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; worker->unlockWorkMutex(); return res; @@ -2623,7 +2734,7 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 if ((worker->mState == LLTextureFetchWorker::LOAD_FROM_SIMULATOR) || (worker->mState == LLTextureFetchWorker::LOAD_FROM_NETWORK)) { - worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); + worker->setPriority(LLWorkerThread::PRIORITY_HIGH); worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; } else @@ -2730,6 +2841,14 @@ void LLTextureFetch::dump() << " STATE: " << worker->sStateDescs[worker->mState] << llendl; } + + llinfos << "LLTextureFetch ACTIVE_HTTP:" << llendl; + for (queue_t::const_iterator iter(mHTTPTextureQueue.begin()); + mHTTPTextureQueue.end() != iter; + ++iter) + { + llinfos << " ID: " << (*iter) << llendl; + } } ////////////////////////////////////////////////////////////////////////////// @@ -2942,6 +3061,8 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) if (! mCapsURL.empty()) { LLCurlRequest::headers_t headers; +#if 0 + // *FIXME: Going to need a post op after all... fetcher->getCurlRequest().post(mCapsURL, headers, merged_llsd, @@ -2950,6 +3071,7 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) report_sequence, LLTextureFetch::svMetricsDataBreak, reporting_started)); +#endif } else { diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 35df7d816f..402b198246 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2012, 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 @@ -31,9 +31,11 @@ #include "llimage.h" #include "lluuid.h" #include "llworkerthread.h" -#include "llcurl.h" #include "lltextureinfo.h" #include "llapr.h" +#include "httprequest.h" +#include "httpoptions.h" +#include "httpheaders.h" class LLViewerTexture; class LLTextureFetchWorker; @@ -98,7 +100,7 @@ public: LLViewerAssetStats * main_stats); void commandDataBreak(); - LLCurlRequest & getCurlRequest() { return *mCurlGetRequest; } + LLCore::HttpRequest & getHttpRequest() { return *mHttpRequest; } bool isQAMode() const { return mQAMode; } @@ -112,7 +114,8 @@ protected: void addToHTTPQueue(const LLUUID& id); void removeFromHTTPQueue(const LLUUID& id, S32 received_size = 0); void removeRequest(LLTextureFetchWorker* worker, bool cancel); - + void cancelHttpRequests(); + // Overrides from the LLThread tree bool runCondition(); @@ -166,7 +169,6 @@ private: LLTextureCache* mTextureCache; LLImageDecodeThread* mImageDecodeThread; - LLCurlRequest* mCurlGetRequest; // Map of all requests by UUID typedef std::map map_t; @@ -203,6 +205,13 @@ private: // use the LLCurl module's request counter as it isn't thread compatible. // *NOTE: Don't mix Atomic and static, apr_initialize must be called first. LLAtomic32 mCurlPOSTRequestCount; + + // Interfaces and objects into the core http library used + // to make our HTTP requests. These replace the various + // LLCurl interfaces used in the past. + LLCore::HttpRequest * mHttpRequest; + LLCore::HttpOptions * mHttpOptions; + LLCore::HttpHeaders * mHttpHeaders; public: // A probabilistically-correct indicator that the current -- cgit v1.2.3 From bc34217979c0a692b17de7ffcb524ed2418da967 Mon Sep 17 00:00:00 2001 From: Nicky Date: Wed, 26 Sep 2012 20:02:32 +0200 Subject: Add virtual destructor to LLGLFence. --- indra/llrender/llgl.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h index 964495a3ab..d70e764769 100644 --- a/indra/llrender/llgl.h +++ b/indra/llrender/llgl.h @@ -424,6 +424,10 @@ const U32 FENCE_WAIT_TIME_NANOSECONDS = 1000; //1 ms class LLGLFence { public: + virtual ~LLGLFence() + { + } + virtual void placeFence() = 0; virtual bool isCompleted() = 0; virtual void wait() = 0; -- cgit v1.2.3 From 1282cd91d18fe74016b58474b7d3afbc99c29aac Mon Sep 17 00:00:00 2001 From: Ansariel Date: Thu, 20 Sep 2012 20:23:54 +0200 Subject: Crash fix for non finite target in editing motion --- indra/llcharacter/lleditingmotion.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/indra/llcharacter/lleditingmotion.cpp b/indra/llcharacter/lleditingmotion.cpp index 66b3c2bd25..0d0b85ba60 100644 --- a/indra/llcharacter/lleditingmotion.cpp +++ b/indra/llcharacter/lleditingmotion.cpp @@ -214,8 +214,10 @@ BOOL LLEditingMotion::onUpdate(F32 time, U8* joint_mask) target = target * target_dist; if (!target.isFinite()) { - llerrs << "Non finite target in editing motion with target distance of " << target_dist << + // Don't error out here, set a fail-safe target vector + llwarns << "Non finite target in editing motion with target distance of " << target_dist << " and focus point " << focus_pt << llendl; + target.setVec(1.f, 1.f, 1.f); } mTarget.setPosition( target + mParentJoint.getPosition()); -- cgit v1.2.3 From 510e1b9e0ec14e98793041b9560694454a9aec2b Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 12 Sep 2012 19:44:53 +0200 Subject: Possible crash fix in LLAgent::doTeleportViaLocation --- indra/newview/llagent.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 447836910d..11fa50b51a 100755 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -4045,6 +4045,12 @@ void LLAgent::teleportViaLocation(const LLVector3d& pos_global) void LLAgent::doTeleportViaLocation(const LLVector3d& pos_global) { LLViewerRegion* regionp = getRegion(); + + if (!regionp) + { + return; + } + U64 handle = to_region_handle(pos_global); LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromHandle(handle); if(regionp && info) -- cgit v1.2.3 From 86e84ae75ef86417be32cff9a22a48fd7853758c Mon Sep 17 00:00:00 2001 From: Nicky Date: Sat, 1 Sep 2012 15:24:54 +0200 Subject: Crashfix; handle errors in release builds more gracefully. --- indra/llrender/llrendertarget.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index cc5c232380..846311a8d0 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -163,10 +163,19 @@ bool LLRenderTarget::addColorAttachment(U32 color_fmt) } U32 offset = mTex.size(); - if (offset >= 4 || - (offset > 0 && (mFBO == 0 || !gGLManager.mHasDrawBuffers))) + + if( offset >= 4 ) { - llerrs << "Too many color attachments!" << llendl; + llwarns << "Too many color attachments" << llendl; + llassert( offset < 4 ); + return false; + } + if( offset > 0 && (mFBO == 0 || !gGLManager.mHasDrawBuffers) ) + { + llwarns << "FBO not used or no drawbuffers available; mFBO=" << (U32)mFBO << " gGLManager.mHasDrawBuffers=" << (U32)gGLManager.mHasDrawBuffers << llendl; + llassert( mFBO != 0 ); + llassert( gGLManager.mHasDrawBuffers ); + return false; } U32 tex; -- cgit v1.2.3 From 48631db986f4f16ba6810c4f91844066407d430d Mon Sep 17 00:00:00 2001 From: Nicky Date: Tue, 28 Aug 2012 23:53:16 +0200 Subject: Crashfix; During TP, or shortly after, gAgent.getRegion can be invalid. Handle that instead of crashing. --- indra/newview/llwlhandlers.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/indra/newview/llwlhandlers.cpp b/indra/newview/llwlhandlers.cpp index 2425b96678..be3e3ff30e 100644 --- a/indra/newview/llwlhandlers.cpp +++ b/indra/newview/llwlhandlers.cpp @@ -105,10 +105,16 @@ LLEnvironmentRequestResponder::LLEnvironmentRequestResponder() return; } - if (unvalidated_content[0]["regionID"].asUUID() != gAgent.getRegion()->getRegionID()) + LLUUID regionId; + if( gAgent.getRegion() ) + { + regionId = gAgent.getRegion()->getRegionID(); + } + + if (unvalidated_content[0]["regionID"].asUUID() != regionId ) { LL_WARNS("WindlightCaps") << "Not in the region from where this data was received (wanting " - << gAgent.getRegion()->getRegionID() << " but got " << unvalidated_content[0]["regionID"].asUUID() + << regionId << " but got " << unvalidated_content[0]["regionID"].asUUID() << ") - ignoring..." << LL_ENDL; return; } -- cgit v1.2.3 From 33263bb5e7dad106ef82c62f789ae430bad63d60 Mon Sep 17 00:00:00 2001 From: Nicky Date: Tue, 28 Aug 2012 23:52:30 +0200 Subject: Don't crash if star_brightness misses in a WL-Param set. Instead warn to log and exit the fuction gracefully. --- indra/newview/lldrawpoolwlsky.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/indra/newview/lldrawpoolwlsky.cpp b/indra/newview/lldrawpoolwlsky.cpp index caf15fe1cb..b5faff7968 100644 --- a/indra/newview/lldrawpoolwlsky.cpp +++ b/indra/newview/lldrawpoolwlsky.cpp @@ -192,14 +192,18 @@ void LLDrawPoolWLSky::renderStars(void) const bool error; LLColor4 star_alpha(LLColor4::black); star_alpha.mV[3] = LLWLParamManager::getInstance()->mCurParams.getFloat("star_brightness", error) / 2.f; - llassert_always(!error); + + // If start_brightness is not set, exit + if( error ) + { + llwarns << "star_brightness missing in mCurParams" << llendl; + return; + } gGL.getTexUnit(0)->bind(gSky.mVOSkyp->getBloomTex()); gGL.pushMatrix(); gGL.rotatef(gFrameTimeSeconds*0.01f, 0.f, 0.f, 1.f); - // gl_FragColor.rgb = gl_Color.rgb; - // gl_FragColor.a = gl_Color.a * star_alpha.a; if (LLGLSLShader::sNoFixedFunction) { gCustomAlphaProgram.bind(); -- cgit v1.2.3 From dec4f9b4be6936ad4b342eb6030c740359713f06 Mon Sep 17 00:00:00 2001 From: Nicky Date: Sun, 26 Aug 2012 12:56:13 +0200 Subject: Gracefully handle 'Started' status. Ignore it and continue login. --- indra/viewer_components/login/lllogin.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp index d480b63094..bdcb068200 100644 --- a/indra/viewer_components/login/lllogin.cpp +++ b/indra/viewer_components/login/lllogin.cpp @@ -271,6 +271,16 @@ void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD login_para } return; // Done! } + + /* Sometimes we end with "Started" here. Slightly slow server? + * Seems to be ok to just skip it. Otherwise we'd error out and crash in the if below. + */ + if( status == "Started") + { + LL_DEBUGS("LLLogin") << mAuthResponse << LL_ENDL; + continue; + } + // If we don't recognize status at all, trouble if (! (status == "CURLError" || status == "XMLRPCError" -- cgit v1.2.3 From 57d36df3bc3c601d87dcf35a4e6692e1ae4d65ff Mon Sep 17 00:00:00 2001 From: Nicky Date: Tue, 21 Aug 2012 19:47:16 +0200 Subject: Make sure mWearableItem/mNameEditor are valid before dereferencing them. --- indra/newview/llpaneleditwearable.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index d58d6d536c..d42056ef9d 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -835,11 +835,11 @@ BOOL LLPanelEditWearable::isDirty() const BOOL isDirty = FALSE; if (mWearablePtr) { - if (mWearablePtr->isDirty() || - mWearableItem->getName().compare(mNameEditor->getText()) != 0) - { - isDirty = TRUE; - } + if (mWearablePtr->isDirty() || + ( mWearableItem && mNameEditor && mWearableItem->getName().compare(mNameEditor->getText()) != 0 )) + { + isDirty = TRUE; + } } return isDirty; } -- cgit v1.2.3 From f9c9a894442c7ff51255451663110a553c4c35c9 Mon Sep 17 00:00:00 2001 From: Nicky Date: Tue, 21 Aug 2012 19:45:45 +0200 Subject: Crashfix in animation preview floater. --- indra/newview/llfloaterbvhpreview.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp index fa0ad20fdb..62848586cd 100644 --- a/indra/newview/llfloaterbvhpreview.cpp +++ b/indra/newview/llfloaterbvhpreview.cpp @@ -422,13 +422,14 @@ void LLFloaterBvhPreview::resetMotion() LLVOAvatar* avatarp = mAnimPreview->getDummyAvatar(); BOOL paused = avatarp->areAnimationsPaused(); - // *TODO: Fix awful casting hack - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); - - // Set emotion - std::string emote = getChild("emote_combo")->getValue().asString(); - motionp->setEmote(mIDList[emote]); - + LLKeyframeMotion* motionp = dynamic_cast(avatarp->findMotion(mMotionID)); + if( motionp ) + { + // Set emotion + std::string emote = getChild("emote_combo")->getValue().asString(); + motionp->setEmote(mIDList[emote]); + } + LLUUID base_id = mIDList[getChild("preview_base_anim")->getValue().asString()]; avatarp->deactivateAllMotions(); avatarp->startMotion(mMotionID, 0.0f); @@ -438,8 +439,12 @@ void LLFloaterBvhPreview::resetMotion() // Set pose std::string handpose = getChild("hand_pose_combo")->getValue().asString(); avatarp->startMotion( ANIM_AGENT_HAND_MOTION, 0.0f ); - motionp->setHandPose(LLHandMotion::getHandPose(handpose)); + if( motionp ) + { + motionp->setHandPose(LLHandMotion::getHandPose(handpose)); + } + if (paused) { mPauseRequest = avatarp->requestPause(); -- cgit v1.2.3 From 944469b84f226cd96a62a9a1bb6302781000c497 Mon Sep 17 00:00:00 2001 From: Nicky Date: Tue, 14 Aug 2012 19:57:43 +0200 Subject: Make sure only one thread access mPendingLOD at a time. --- indra/newview/llmeshrepository.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index bc7f522848..52077c620f 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1097,11 +1097,13 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat mMeshHeader[mesh_id] = header; } + + LLMutexLock lock(mMutex); // make sure only one thread access mPendingLOD at the same time. + //check for pending requests pending_lod_map::iterator iter = mPendingLOD.find(mesh_params); if (iter != mPendingLOD.end()) { - LLMutexLock lock(mMutex); for (U32 i = 0; i < iter->second.size(); ++i) { LODRequest req(mesh_params, iter->second[i]); -- cgit v1.2.3 From c8ba971e84b6dba31636dbfba20b2d9ca219f67f Mon Sep 17 00:00:00 2001 From: Nicky Date: Tue, 24 Jul 2012 17:22:18 +0200 Subject: Crashfix; don't crash in LLRightClickInventoryFetchDescendentsObserver::execute when there's a 0 pointer for item_array or cat_array. --- indra/newview/llinventorybridge.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index fce0b7c9c9..14a228df1c 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -2577,14 +2577,23 @@ void LLRightClickInventoryFetchDescendentsObserver::execute(bool clear_observer) LLInventoryModel::item_array_t* item_array; gInventory.getDirectDescendentsOf(*current_folder, cat_array, item_array); - S32 item_count = item_array->count(); - S32 cat_count = cat_array->count(); - + S32 item_count(0); + if( item_array ) + { + item_count = item_array->count(); + } + + S32 cat_count(0); + if( cat_array ) + { + cat_count = cat_array->count(); + } + // Move to next if current folder empty if ((item_count == 0) && (cat_count == 0)) - { + { continue; - } + } uuid_vec_t ids; LLRightClickInventoryFetchObserver* outfit = NULL; -- cgit v1.2.3 From fdb0218bfc156842a70661e339ba4592e02dc9c4 Mon Sep 17 00:00:00 2001 From: Nicky Date: Mon, 22 Oct 2012 15:40:18 -0400 Subject: Crashfix; Guard against 0 pointer textures. --- indra/newview/llface.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 188f943f13..605cb81c10 100755 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -1062,7 +1062,11 @@ bool LLFace::canRenderAsMask() } const LLTextureEntry* te = getTextureEntry(); - + if( !te || !getViewerObject() || !getTexture() ) + { + return false; + } + if ((te->getColor().mV[3] == 1.0f) && // can't treat as mask if we have face alpha (te->getGlow() == 0.f) && // glowing masks are hard to implement - don't mask getTexture()->getIsAlphaMask()) // texture actually qualifies for masking (lazily recalculated but expensive) -- cgit v1.2.3 From e0b334c6154e87729addfb01a52bd5b4c085bf44 Mon Sep 17 00:00:00 2001 From: Nicky Date: Sat, 21 Jul 2012 14:18:10 +0200 Subject: Crashfix; Change llassert_always to llassert + llwarns. This way users don't get disruptive crashes. --- indra/llrender/llvertexbuffer.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index eadef93c89..2fe0aa0b72 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -555,8 +555,21 @@ void LLVertexBuffer::drawArrays(U32 mode, const std::vector& pos, con gGL.syncMatrices(); U32 count = pos.size(); - llassert_always(norm.size() >= pos.size()); - llassert_always(count > 0); + + llassert(norm.size() >= pos.size()); + llassert(count > 0); + + if( count == 0 ) + { + llwarns << "Called drawArrays with 0 vertices" << llendl; + return; + } + + if( norm.size() < pos.size() ) + { + llwarns << "Called drawArrays with #" << norm.size() << " normals and #" << pos.size() << " vertices" << llendl; + return; + } unbind(); -- cgit v1.2.3 From bef60207e309db96d1aace39c24906903ed3bdc1 Mon Sep 17 00:00:00 2001 From: Nicky Date: Sat, 21 Jul 2012 13:35:14 +0200 Subject: Crashfix; Guard against 0 pointer in LLRiggedVolume::update. --- indra/newview/llvovolume.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index e99898a83c..afe62bbb2e 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -4880,6 +4880,7 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std:: std::vector texture_list; + if( pos && weight && dst_face.mExtents ) { LLFastTimer t(FTM_GEN_DRAW_INFO_FACE_SIZE); if (batch_textures) @@ -4976,6 +4977,7 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std:: //create vertex buffer LLVertexBuffer* buffer = NULL; + if( dst_face.mExtents ) { LLFastTimer t(FTM_GEN_DRAW_INFO_ALLOCATE); buffer = createVertexBuffer(mask, buffer_usage); -- cgit v1.2.3 From 6fab95060fe2793e1206a5329f81877e3980f0cd Mon Sep 17 00:00:00 2001 From: Nicky Date: Thu, 19 Jul 2012 22:54:17 +0200 Subject: Crashfix; Make sure getImage returns a valid result. --- indra/newview/llvoavatar.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 366b6004be..2c1291c954 100755 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -4421,7 +4421,9 @@ U32 LLVOAvatar::renderTransparent(BOOL first_pass) } // Can't test for baked hair being defined, since that won't always be the case (not all viewers send baked hair) // TODO: 1.25 will be able to switch this logic back to calling isTextureVisible(); - if (getImage(TEX_HAIR_BAKED, 0)->getID() != IMG_INVISIBLE || LLDrawPoolAlpha::sShowDebugAlpha) + + if ( getImage(TEX_HAIR_BAKED, 0) + && getImage(TEX_HAIR_BAKED, 0)->getID() != IMG_INVISIBLE || LLDrawPoolAlpha::sShowDebugAlpha) { num_indices += mMeshLOD[MESH_ID_HAIR]->render(mAdjustedPixelArea, first_pass, mIsDummy); first_pass = FALSE; -- cgit v1.2.3 From 9b856a124ac11f976c7913d1ce2af8d57ab0464c Mon Sep 17 00:00:00 2001 From: Nicky Date: Sat, 14 Jul 2012 03:10:24 +0200 Subject: Crashfix: showToastsTop/Bottom, don't iterate over mToastList. The member can change during recursive calls, invalidating iterators. --- indra/newview/llscreenchannel.cpp | 40 +++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp index d340b304ca..d2280ea089 100644 --- a/indra/newview/llscreenchannel.cpp +++ b/indra/newview/llscreenchannel.cpp @@ -571,9 +571,13 @@ void LLScreenChannel::showToastsBottom() LLDockableFloater* floater = dynamic_cast(LLDockableFloater::getInstanceHandle().get()); - for(it = mToastList.rbegin(); it != mToastList.rend(); ++it) + // Use a local variable instead of mToastList. + // mToastList can be modified during recursive calls and then all iteratos will be invalidated. + std::vector vToastList( mToastList ); + + for(it = vToastList.rbegin(); it != vToastList.rend(); ++it) { - if(it != mToastList.rbegin()) + if(it != vToastList.rbegin()) { LLToast* toast = (it-1)->getToast(); if (!toast) @@ -601,7 +605,7 @@ void LLScreenChannel::showToastsBottom() if(floater && floater->overlapsScreenChannel()) { - if(it == mToastList.rbegin()) + if(it == vToastList.rbegin()) { // move first toast above docked floater S32 shift = floater->getRect().getHeight(); @@ -624,7 +628,7 @@ void LLScreenChannel::showToastsBottom() if(!stop_showing_toasts) { - if( it != mToastList.rend()-1) + if( it != vToastList.rend()-1) { S32 toast_top = toast->getRect().mTop + gSavedSettings.getS32("ToastGap"); stop_showing_toasts = toast_top > getRect().mTop; @@ -632,7 +636,8 @@ void LLScreenChannel::showToastsBottom() } // at least one toast should be visible - if(it == mToastList.rbegin()) + + if(it == vToastList.rbegin()) { stop_showing_toasts = false; } @@ -655,10 +660,11 @@ void LLScreenChannel::showToastsBottom() } // Dismiss toasts we don't have space for (STORM-391). - if(it != mToastList.rend()) + if(it != vToastList.rend()) { mHiddenToastsNum = 0; - for(; it != mToastList.rend(); it++) + + for(; it != vToastList.rend(); it++) { LLToast* toast = it->getToast(); if (toast) @@ -714,9 +720,13 @@ void LLScreenChannel::showToastsTop() LLDockableFloater* floater = dynamic_cast(LLDockableFloater::getInstanceHandle().get()); - for(it = mToastList.rbegin(); it != mToastList.rend(); ++it) + // Use a local variable instead of mToastList. + // mToastList can be modified during recursive calls and then all iteratos will be invalidated. + std::vector vToastList( mToastList ); + + for(it = vToastList.rbegin(); it != vToastList.rend(); ++it) { - if(it != mToastList.rbegin()) + if(it != vToastList.rbegin()) { LLToast* toast = (it-1)->getToast(); if (!toast) @@ -744,7 +754,7 @@ void LLScreenChannel::showToastsTop() if(floater && floater->overlapsScreenChannel()) { - if(it == mToastList.rbegin()) + if(it == vToastList.rbegin()) { // move first toast above docked floater S32 shift = -floater->getRect().getHeight(); @@ -767,7 +777,7 @@ void LLScreenChannel::showToastsTop() if(!stop_showing_toasts) { - if( it != mToastList.rend()-1) + if( it != vToastList.rend()-1) { S32 toast_bottom = toast->getRect().mBottom - gSavedSettings.getS32("ToastGap"); stop_showing_toasts = toast_bottom < channel_rect.mBottom; @@ -775,7 +785,7 @@ void LLScreenChannel::showToastsTop() } // at least one toast should be visible - if(it == mToastList.rbegin()) + if(it == vToastList.rbegin()) { stop_showing_toasts = false; } @@ -799,10 +809,12 @@ void LLScreenChannel::showToastsTop() // Dismiss toasts we don't have space for (STORM-391). std::vector toasts_to_hide; - if(it != mToastList.rend()) + + if(it != vToastList.rend()) { mHiddenToastsNum = 0; - for(; it != mToastList.rend(); it++) + + for(; it != vToastList.rend(); it++) { LLToast* toast = it->getToast(); if (toast) -- cgit v1.2.3 From c0e8656477e41fffa26be9a23bef1c5bb1357330 Mon Sep 17 00:00:00 2001 From: Nicky Date: Fri, 13 Jul 2012 14:04:45 +0200 Subject: Crashfix: Stop notifications when we're about to exit. --- indra/newview/llnotificationmanager.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/indra/newview/llnotificationmanager.cpp b/indra/newview/llnotificationmanager.cpp index f792f53ac5..3d8150eed3 100644 --- a/indra/newview/llnotificationmanager.cpp +++ b/indra/newview/llnotificationmanager.cpp @@ -97,6 +97,13 @@ bool LLNotificationManager::onNotification(const LLSD& notify) { LLSysHandler* handle = NULL; + // Don't bother if we're going down. + // Otherwise we might crash when trying to use handlers that are already dead. + if( LLApp::isExiting() ) + { + return false; + } + if (LLNotifications::destroyed()) return false; -- cgit v1.2.3 From 85025ffc7be40c0953ca8905f149701b4b8f568f Mon Sep 17 00:00:00 2001 From: Nicky Date: Fri, 13 Jul 2012 13:46:38 +0200 Subject: Crashfix: LLVOVolume::calcLOD make sure avatar and avatar->mDrawable are valid. --- indra/newview/llvovolume.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index afe62bbb2e..49513eb206 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -1227,6 +1227,13 @@ BOOL LLVOVolume::calcLOD() if (mDrawable->isState(LLDrawable::RIGGED)) { LLVOAvatar* avatar = getAvatar(); + + // Not sure how this can really happen, but alas it does. Better exit here than crashing. + if( !avatar || !avatar->mDrawable ) + { + return FALSE; + } + distance = avatar->mDrawable->mDistanceWRTCamera; radius = avatar->getBinRadius(); } -- cgit v1.2.3 From 60393abdad98c61d9cb01d004e3d69bd8d34bfda Mon Sep 17 00:00:00 2001 From: Nicky Date: Fri, 13 Jul 2012 13:37:18 +0200 Subject: Crashfix: In LLAttachmentsMgr::onIdle make sure we've a valid region before dereferencing it. --- indra/newview/llattachmentsmgr.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/indra/newview/llattachmentsmgr.cpp b/indra/newview/llattachmentsmgr.cpp index c9543988a6..ea0b8f00a4 100644 --- a/indra/newview/llattachmentsmgr.cpp +++ b/indra/newview/llattachmentsmgr.cpp @@ -62,6 +62,12 @@ void LLAttachmentsMgr::onIdle(void *) void LLAttachmentsMgr::onIdle() { + // Make sure we got a region before trying anything else + if( !gAgent.getRegion() ) + { + return; + } + S32 obj_count = mPendingAttachments.size(); if (obj_count == 0) { -- cgit v1.2.3 From 2005b4fed4dcfc17ec46d21ce093dbc6a368083b Mon Sep 17 00:00:00 2001 From: Nicky Date: Thu, 12 Jul 2012 18:04:53 +0200 Subject: Crashfix: in ll_safe_string not only guard against 0 pointer, but against illegal length of buffer too. --- indra/llcommon/llstring.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index fa0eb9f72c..0c32679744 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -47,7 +47,8 @@ std::string ll_safe_string(const char* in) std::string ll_safe_string(const char* in, S32 maxlen) { - if(in) return std::string(in, maxlen); + if(in && maxlen > 0 ) return std::string(in, maxlen); + return std::string(); } -- cgit v1.2.3 From 8902f4c6c0fed60ae6d8b7596e61c7b8387079c1 Mon Sep 17 00:00:00 2001 From: Nicky Date: Tue, 10 Jul 2012 20:12:31 +0200 Subject: Crashfix: Don't blindly trust getImage() returns a valid pointer. --- indra/newview/llvoavatar.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 2c1291c954..39222c25fd 100755 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -8707,6 +8707,12 @@ BOOL LLVOAvatar::isTextureDefined(LLVOAvatarDefines::ETextureIndex te, U32 index return FALSE; } + if( !getImage( te, index ) ) + { + llwarns << "getImage( " << te << ", " << index << " ) returned 0" << llendl; + return FALSE; + } + return (getImage(te, index)->getID() != IMG_DEFAULT_AVATAR && getImage(te, index)->getID() != IMG_DEFAULT); } -- cgit v1.2.3 From 82c51c8d29d7c9a59373f45fa794bbc0729c97d5 Mon Sep 17 00:00:00 2001 From: Nicky Date: Mon, 9 Jul 2012 13:37:21 +0200 Subject: Crashfix: LLVOAvatar::updateTextures in some odd cases getTE can return 0. Safeguard against that. --- indra/newview/llvoavatar.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 39222c25fd..627238b0f5 100755 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -4567,7 +4567,20 @@ void LLVOAvatar::updateTextures() LLWearableType::EType wearable_type = LLVOAvatarDictionary::getTEWearableType((ETextureIndex)texture_index); U32 num_wearables = gAgentWearables.getWearableCount(wearable_type); const LLTextureEntry *te = getTE(texture_index); - const F32 texel_area_ratio = fabs(te->mScaleS * te->mScaleT); + + // getTE can return 0. + // Not sure yet why it does, but of course it crashes when te->mScale? gets used. + // Put safeguard in place so this corner case get better handling and does not result in a crash. + F32 texel_area_ratio = 1.0f; + if( te ) + { + texel_area_ratio = fabs(te->mScaleS * te->mScaleT); + } + else + { + llwarns << "getTE( " << texture_index << " ) returned 0" < Date: Sun, 8 Jul 2012 12:48:43 +0200 Subject: Crashfix: Make sure drawable exists before calling any method on it. --- indra/newview/llviewertexture.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 8eb8717de2..32cf8cc1b3 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -1904,7 +1904,7 @@ void LLViewerFetchedTexture::updateVirtualSize() for(U32 i = 0 ; i < mNumFaces ; i++) { LLFace* facep = mFaceList[i] ; - if(facep->getDrawable()->isRecentlyVisible()) + if( facep && facep->getDrawable() && facep->getDrawable()->isRecentlyVisible()) { addTextureStats(facep->getVirtualSize()) ; setAdditionalDecodePriority(facep->getImportanceToCamera()) ; -- cgit v1.2.3 From d1e924a71ef8ce41ba43bba1fbc1e1825401625c Mon Sep 17 00:00:00 2001 From: Nicky Date: Sat, 7 Jul 2012 22:10:43 +0200 Subject: Crashfix: During cleanup gAgentAvatarp can already be 0. --- indra/newview/llappearancemgr.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 6d67e098a6..06a9892c7e 100755 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -52,7 +52,8 @@ std::string self_av_string() { - return gAgentAvatarp->avString(); + // On logout gAgentAvatarp can already be invalid + return isAgentAvatarValid() ? gAgentAvatarp->avString() : ""; } // RAII thingy to guarantee that a variable gets reset when the Setter -- cgit v1.2.3 From d40a620483729d87bb7bca5de42d338f32ab09dc Mon Sep 17 00:00:00 2001 From: Nicky Date: Sun, 17 Jun 2012 18:20:30 +0200 Subject: Don't reference any LLFace via LLDrawable::getFace whose index is out of bounds. --- indra/newview/llvovolume.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 49513eb206..8465a9dadd 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -1342,7 +1342,8 @@ BOOL LLVOVolume::setDrawableParent(LLDrawable* parentp) void LLVOVolume::updateFaceFlags() { - for (S32 i = 0; i < getVolume()->getNumFaces(); i++) + // There's no guarantee that getVolume()->getNumFaces() == mDrawable->getNumFaces() + for (S32 i = 0; i < getVolume()->getNumFaces() && i < mDrawable->getNumFaces(); i++) { LLFace *face = mDrawable->getFace(i); if (face) @@ -1443,7 +1444,10 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global) volume = getVolume(); } - for (S32 i = 0; i < getVolume()->getNumVolumeFaces(); i++) + // There's no guarantee that getVolume()->getNumFaces() == mDrawable->getNumFaces() + for (S32 i = 0; + i < getVolume()->getNumVolumeFaces() && i < mDrawable->getNumFaces() && i < getNumTEs(); + i++) { LLFace *face = mDrawable->getFace(i); if (!face) @@ -1744,6 +1748,11 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable) void LLVOVolume::updateFaceSize(S32 idx) { + if( mDrawable->getNumFaces() <= idx ) + { + return; + } + LLFace* facep = mDrawable->getFace(idx); if (facep) { @@ -2434,7 +2443,12 @@ void LLVOVolume::addMediaImpl(LLViewerMediaImpl* media_impl, S32 texture_index) //add the face to show the media if it is in playing if(mDrawable) { - LLFace* facep = mDrawable->getFace(texture_index) ; + LLFace* facep(NULL); + if( texture_index < mDrawable->getNumFaces() ) + { + facep = mDrawable->getFace(texture_index) ; + } + if(facep) { LLViewerMediaTexture* media_tex = LLViewerTextureManager::findMediaTexture(mMediaImplList[texture_index]->getMediaTextureID()) ; -- cgit v1.2.3 From e963cefea596e41922e91f794356ff2533594587 Mon Sep 17 00:00:00 2001 From: Nicky Date: Fri, 1 Jun 2012 19:37:10 +0200 Subject: check mesh repo thread before actively using it. --- indra/newview/llmeshrepository.cpp | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 52077c620f..ba0a590910 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1798,7 +1798,12 @@ void LLMeshLODResponder::completedRaw(U32 status, const std::string& reason, const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer) { - + // thread could have already be destroyed during logout + if( !gMeshRepo.mThread ) + { + return; + } + S32 data_size = buffer->countAfter(channels.in(), NULL); if (status < 200 || status > 400) @@ -1853,6 +1858,12 @@ void LLMeshSkinInfoResponder::completedRaw(U32 status, const std::string& reason const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer) { + // thread could have already be destroyed during logout + if( !gMeshRepo.mThread ) + { + return; + } + S32 data_size = buffer->countAfter(channels.in(), NULL); if (status < 200 || status > 400) @@ -1907,6 +1918,11 @@ void LLMeshDecompositionResponder::completedRaw(U32 status, const std::string& r const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer) { + if( !gMeshRepo.mThread ) + { + return; + } + S32 data_size = buffer->countAfter(channels.in(), NULL); if (status < 200 || status > 400) @@ -1961,6 +1977,12 @@ void LLMeshPhysicsShapeResponder::completedRaw(U32 status, const std::string& re const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer) { + // thread could have already be destroyed during logout + if( !gMeshRepo.mThread ) + { + return; + } + S32 data_size = buffer->countAfter(channels.in(), NULL); if (status < 200 || status > 400) @@ -2015,6 +2037,12 @@ void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason, const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer) { + // thread could have already be destroyed during logout + if( !gMeshRepo.mThread ) + { + return; + } + if (status < 200 || status > 400) { //llwarns -- cgit v1.2.3 From e8bebc5b3c17cbb3c3d1021104be2de99dcc3efe Mon Sep 17 00:00:00 2001 From: Nicky Date: Wed, 30 May 2012 23:46:41 +0200 Subject: Make sure to not use floaters of different type than LLPreview as LLPreview instance. --- indra/newview/llpreview.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/indra/newview/llpreview.cpp b/indra/newview/llpreview.cpp index 18626e3273..04934b13f1 100644 --- a/indra/newview/llpreview.cpp +++ b/indra/newview/llpreview.cpp @@ -464,7 +464,9 @@ LLMultiPreview::LLMultiPreview() void LLMultiPreview::onOpen(const LLSD& key) { - LLPreview* frontmost_preview = (LLPreview*)mTabContainer->getCurrentPanel(); + // Floater could be something else than LLPreview, eg LLFloaterProfile. + LLPreview* frontmost_preview = dynamic_cast(mTabContainer->getCurrentPanel()); + if (frontmost_preview && frontmost_preview->getAssetStatus() == LLPreview::PREVIEW_ASSET_UNLOADED) { frontmost_preview->loadAsset(); @@ -477,8 +479,13 @@ void LLMultiPreview::handleReshape(const LLRect& new_rect, bool by_user) { if(new_rect.getWidth() != getRect().getWidth() || new_rect.getHeight() != getRect().getHeight()) { - LLPreview* frontmost_preview = (LLPreview*)mTabContainer->getCurrentPanel(); - if (frontmost_preview) frontmost_preview->userResized(); + // Floater could be something else than LLPreview, eg LLFloaterProfile. + LLPreview* frontmost_preview = dynamic_cast(mTabContainer->getCurrentPanel()); + + if (frontmost_preview) + { + frontmost_preview->userResized(); + } } LLFloater::handleReshape(new_rect, by_user); } @@ -486,7 +493,9 @@ void LLMultiPreview::handleReshape(const LLRect& new_rect, bool by_user) void LLMultiPreview::tabOpen(LLFloater* opened_floater, bool from_click) { - LLPreview* opened_preview = (LLPreview*)opened_floater; + // Floater could be something else than LLPreview, eg LLFloaterProfile. + LLPreview* opened_preview = dynamic_cast(opened_floater); + if (opened_preview && opened_preview->getAssetStatus() == LLPreview::PREVIEW_ASSET_UNLOADED) { opened_preview->loadAsset(); -- cgit v1.2.3 From b8edacd0bb4feacc3ac1d61421e600c75ab87f7c Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 1 Jun 2012 14:07:34 -0400 Subject: Major steps towards implementing the policy component. Identified and reacted to the priority inversion problem we have in texturefetch. Includes the introduction of a priority_queue for the requests that are ready. Start some parameterization in anticipation of having policy_class everywhere. Removed _assert.h which isn't really needed in indra codebase. Implemented async setPriority request (which I hope I can get rid of eventually along with all priorities in this library). Converted to using unsigned int for priority rather than float. Implemented POST and did groundwork for PUT. --- indra/llcorehttp/CMakeLists.txt | 27 ++-- indra/llcorehttp/_assert.h | 39 ----- indra/llcorehttp/_httplibcurl.cpp | 52 ++++-- indra/llcorehttp/_httplibcurl.h | 27 +++- indra/llcorehttp/_httpopcancel.h | 2 +- indra/llcorehttp/_httpoperation.cpp | 8 +- indra/llcorehttp/_httpoperation.h | 42 ++++- indra/llcorehttp/_httpoprequest.cpp | 113 ++++++++++++- indra/llcorehttp/_httpoprequest.h | 11 +- indra/llcorehttp/_httpopsetpriority.cpp | 77 +++++++++ indra/llcorehttp/_httpopsetpriority.h | 70 +++++++++ indra/llcorehttp/_httppolicy.cpp | 97 ++++++++++-- indra/llcorehttp/_httppolicy.h | 28 +++- indra/llcorehttp/_httpreadyqueue.h | 85 ++++++++++ indra/llcorehttp/_httprequestqueue.cpp | 2 +- indra/llcorehttp/_httpservice.cpp | 63 +++++--- indra/llcorehttp/_httpservice.h | 26 ++- indra/llcorehttp/_refcounted.h | 14 +- indra/llcorehttp/httpcommon.cpp | 15 +- indra/llcorehttp/httpcommon.h | 8 +- indra/llcorehttp/httprequest.cpp | 52 +++++- indra/llcorehttp/httprequest.h | 49 +++++- indra/llcorehttp/tests/test_httprequest.hpp | 2 +- indra/newview/lltexturefetch.cpp | 235 ++++++++++++---------------- indra/newview/lltexturefetch.h | 15 -- 25 files changed, 869 insertions(+), 290 deletions(-) delete mode 100644 indra/llcorehttp/_assert.h create mode 100644 indra/llcorehttp/_httpopsetpriority.cpp create mode 100644 indra/llcorehttp/_httpopsetpriority.h create mode 100644 indra/llcorehttp/_httpreadyqueue.h diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index ae92fb96fd..85df5364db 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -23,17 +23,18 @@ include_directories( set(llcorehttp_SOURCE_FILES bufferarray.cpp httpcommon.cpp + httpheaders.cpp + httpoptions.cpp httprequest.cpp httpresponse.cpp - httpoptions.cpp - httpheaders.cpp - _httprequestqueue.cpp + _httplibcurl.cpp + _httpopcancel.cpp _httpoperation.cpp _httpoprequest.cpp - _httpopcancel.cpp - _httpreplyqueue.cpp + _httpopsetpriority.cpp _httppolicy.cpp - _httplibcurl.cpp + _httpreplyqueue.cpp + _httprequestqueue.cpp _httpservice.cpp _refcounted.cpp ) @@ -48,17 +49,19 @@ set(llcorehttp_HEADER_FILES httpoptions.h httprequest.h httpresponse.h + _assert.h + _httplibcurl.h + _httpopcancel.h _httpoperation.h _httpoprequest.h - _httpopcancel.h - _httprequestqueue.h + _httpopsetpriority.h + _httppolicy.h + _httpreadyqueue.h _httpreplyqueue.h + _httprequestqueue.h _httpservice.h - _httppolicy.h - _httplibcurl.h - _assert.h - _refcounted.h _mutex.h + _refcounted.h _thread.h ) diff --git a/indra/llcorehttp/_assert.h b/indra/llcorehttp/_assert.h deleted file mode 100644 index 054f23ef32..0000000000 --- a/indra/llcorehttp/_assert.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * @file _assert - * @brief assert abstraction - * - * $LicenseInfo:firstyear=2012&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2012, 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 - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LLCOREINT__ASSERT_H_ -#define LLCOREINT__ASSERT_H_ - -#ifdef DEBUG_ASSERT -#include -#define LLINT_ASSERT(x) assert(x) -#else -#define LLINT_ASSERT(x) -#endif - -#endif // LLCOREINT__ASSERT_H_ - - diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 1b951818e4..704f9baac9 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -28,7 +28,6 @@ #include "httpheaders.h" #include "_httpoprequest.h" -#include "_httpservice.h" namespace LLCore @@ -38,6 +37,12 @@ namespace LLCore HttpLibcurl::HttpLibcurl(HttpService * service) : mService(service) { + for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) + { + mMultiHandles[policy_class] = 0; + } + + // Create multi handle for default class mMultiHandles[0] = curl_multi_init(); } @@ -51,15 +56,18 @@ HttpLibcurl::~HttpLibcurl() (*item)->cancel(); (*item)->release(); + mActiveOps.erase(item); } - if (mMultiHandles[0]) + for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) { - // *FIXME: Do some multi cleanup here first - + if (mMultiHandles[policy_class]) + { + // *FIXME: Do some multi cleanup here first - curl_multi_cleanup(mMultiHandles[0]); - mMultiHandles[0] = NULL; + curl_multi_cleanup(mMultiHandles[policy_class]); + mMultiHandles[policy_class] = 0; + } } mService = NULL; @@ -74,31 +82,34 @@ void HttpLibcurl::term() {} -void HttpLibcurl::processTransport() +HttpService::ELoopSpeed HttpLibcurl::processTransport() { - if (mMultiHandles[0]) + // Give libcurl some cycles to do I/O & callbacks + for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) { - // Give libcurl some cycles to do I/O & callbacks + if (! mMultiHandles[policy_class]) + continue; + int running(0); CURLMcode status(CURLM_CALL_MULTI_PERFORM); do { running = 0; - status = curl_multi_perform(mMultiHandles[0], &running); + status = curl_multi_perform(mMultiHandles[policy_class], &running); } while (0 != running && CURLM_CALL_MULTI_PERFORM == status); // Run completion on anything done CURLMsg * msg(NULL); int msgs_in_queue(0); - while ((msg = curl_multi_info_read(mMultiHandles[0], &msgs_in_queue))) + while ((msg = curl_multi_info_read(mMultiHandles[policy_class], &msgs_in_queue))) { if (CURLMSG_DONE == msg->msg) { CURL * handle(msg->easy_handle); CURLcode result(msg->data.result); - completeRequest(mMultiHandles[0], handle, result); + completeRequest(mMultiHandles[policy_class], handle, result); handle = NULL; // No longer valid on return } else if (CURLMSG_NONE == msg->msg) @@ -114,13 +125,18 @@ void HttpLibcurl::processTransport() msgs_in_queue = 0; } } + + return mActiveOps.empty() ? HttpService::REQUEST_SLEEP : HttpService::NORMAL; } void HttpLibcurl::addOp(HttpOpRequest * op) { + llassert_always(op->mReqPolicy < HttpRequest::POLICY_CLASS_LIMIT); + llassert_always(mMultiHandles[op->mReqPolicy] != NULL); + // Create standard handle - if (! op->prepareForGet(mService)) + if (! op->prepareRequest(mService)) { // Couldn't issue request, fail with notification // *FIXME: Need failure path @@ -128,7 +144,7 @@ void HttpLibcurl::addOp(HttpOpRequest * op) } // Make the request live - curl_multi_add_handle(mMultiHandles[0], op->mCurlHandle); + curl_multi_add_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle); op->mCurlActive = true; // On success, make operation active @@ -190,12 +206,18 @@ void HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode } -int HttpLibcurl::activeCount() const +int HttpLibcurl::getActiveCount() const { return mActiveOps.size(); } +int HttpLibcurl::getActiveCountInClass(int /* policy_class */) const +{ + return getActiveCount(); +} + + // --------------------------------------- // Free functions // --------------------------------------- diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 807196628d..ec325c1946 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -34,18 +34,25 @@ #include +#include "httprequest.h" +#include "_httpservice.h" + namespace LLCore { -class HttpService; class HttpPolicy; class HttpOpRequest; class HttpHeaders; /// Implements libcurl-based transport for an HttpService instance. +/// +/// Threading: Single-threaded. Other than for construction/destruction, +/// all methods are expected to be invoked in a single thread, typically +/// a worker thread of some sort. + class HttpLibcurl { public: @@ -60,12 +67,22 @@ public: static void init(); static void term(); - void processTransport(); - void addOp(HttpOpRequest * op); + /// Give cycles to libcurl to run active requests. Completed + /// operations (successful or failed) will be retried or handed + /// over to the reply queue as final responses. + HttpService::ELoopSpeed processTransport(); - int activeCount() const; + /// Add request to the active list. Caller is expected to have + /// provided us with a reference count to hold the request. (No + /// additional references will be added.) + void addOp(HttpOpRequest * op); + int getActiveCount() const; + int getActiveCountInClass(int policy_class) const; + protected: + /// Invoked when libcurl has indicated a request has been processed + /// to completion and we need to move the request to a new state. void completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status); protected: @@ -74,7 +91,7 @@ protected: protected: HttpService * mService; // Simple reference, not owner active_set_t mActiveOps; - CURLM * mMultiHandles[1]; + CURLM * mMultiHandles[HttpRequest::POLICY_CLASS_LIMIT]; }; // end class HttpLibcurl diff --git a/indra/llcorehttp/_httpopcancel.h b/indra/llcorehttp/_httpopcancel.h index 38ccc585ed..fab6f1f362 100644 --- a/indra/llcorehttp/_httpopcancel.h +++ b/indra/llcorehttp/_httpopcancel.h @@ -45,7 +45,7 @@ namespace LLCore /// HttpOpCancel requests that a previously issued request /// be canceled, if possible. Requests that have been made /// active and are available for sending on the wire cannot -/// be canceled. +/// be canceled. class HttpOpCancel : public HttpOperation { diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index 17c65b0379..d966efd12b 100644 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -50,7 +50,7 @@ HttpOperation::HttpOperation() mLibraryHandler(NULL), mUserHandler(NULL), mReqPolicy(HttpRequest::DEFAULT_POLICY_ID), - mReqPriority(0.0f) + mReqPriority(0U) { } @@ -95,7 +95,7 @@ void HttpOperation::stageFromRequest(HttpService *) // Default implementation should never be called. This // indicates an operation making a transition that isn't // defined. - LLINT_ASSERT(false); + llassert_always(false); } @@ -105,7 +105,7 @@ void HttpOperation::stageFromReady(HttpService *) // Default implementation should never be called. This // indicates an operation making a transition that isn't // defined. - LLINT_ASSERT(false); + llassert_always(false); } @@ -115,7 +115,7 @@ void HttpOperation::stageFromActive(HttpService *) // Default implementation should never be called. This // indicates an operation making a transition that isn't // defined. - LLINT_ASSERT(false); + llassert_always(false); } diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index 5d06a28586..6c0c3183b7 100644 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -44,6 +44,32 @@ class HttpRequest; /// HttpOperation is the base class for all request/reply /// pairs. /// +/// Operations are expected to be of two types: immediate +/// and queued. Immediate requests go to the singleton +/// request queue and when picked up by the worker thread +/// are executed immediately and there results placed on +/// the supplied reply queue. Queued requests (namely for +/// HTTP operations), go to the request queue, are picked +/// up and moved to a ready queue where they're ordered by +/// priority and managed by the policy component, are +/// then activated issuing HTTP requests and moved to an +/// active list managed by the transport (libcurl) component +/// and eventually finalized when a response is available +/// and status and data return via reply queue. +/// +/// To manage these transitions, derived classes implement +/// three methods: stageFromRequest, stageFromReady and +/// stageFromActive. Immediate requests will only override +/// stageFromRequest which will perform the operation and +/// return the result by invoking addAsReply() to put the +/// request on a reply queue. Queued requests will involve +/// all three stage methods. +/// +/// Threading: not thread-safe. Base and derived classes +/// provide no locking. Instances move across threads +/// via queue-like interfaces that are thread compatible +/// and those interfaces establish the access rules. + class HttpOperation : public LLCoreInt::RefCounted { public: @@ -82,7 +108,7 @@ protected: public: unsigned int mReqPolicy; - float mReqPriority; + unsigned int mReqPriority; }; // end class HttpOperation @@ -133,6 +159,20 @@ public: }; // end class HttpOpNull + +/// HttpOpCompare isn't an operation but a uniform comparison +/// functor for STL containers that order by priority. Mainly +/// used for the ready queue container but defined here. +class HttpOpCompare +{ +public: + bool operator()(const HttpOperation * lhs, const HttpOperation * rhs) + { + return lhs->mReqPriority > rhs->mReqPriority; + } +}; // end class HttpOpCompare + + } // end namespace LLCore #endif // _LLCORE_HTTP_OPERATION_H_ diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 521bd5b879..54b9990057 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -93,6 +93,7 @@ HttpOpRequest::HttpOpRequest() mCurlHandle(NULL), mCurlService(NULL), mCurlHeaders(NULL), + mCurlBodyPos(0), mReplyBody(NULL), mReplyOffset(0), mReplyLength(0), @@ -267,12 +268,46 @@ HttpStatus HttpOpRequest::setupGetByteRange(unsigned int policy_id, } -HttpStatus HttpOpRequest::prepareForGet(HttpService * service) +HttpStatus HttpOpRequest::setupPost(unsigned int policy_id, + float priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers) +{ + HttpStatus status; + + mProcFlags = 0; + mReqPolicy = policy_id; + mReqPriority = priority; + mReqMethod = HOR_POST; + mReqURL = url; + if (body) + { + body->addRef(); + mReqBody = body; + } + if (headers && ! mReqHeaders) + { + headers->addRef(); + mReqHeaders = headers; + } + if (options && ! mReqOptions) + { + mReqOptions = new HttpOptions(*options); + } + + return status; +} + + +HttpStatus HttpOpRequest::prepareRequest(HttpService * service) { // *FIXME: better error handling later HttpStatus status; mCurlHandle = curl_easy_init(); + // curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1); curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, 30); curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, 30); curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1); @@ -280,20 +315,68 @@ HttpStatus HttpOpRequest::prepareForGet(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str()); curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this); curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); + // *FIXME: Need to deal with proxy setup... // curl_easy_setopt(handle, CURLOPT_PROXY, ""); + // *FIXME: Revisit this old DNS timeout setting - may no longer be valid curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); + curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1); curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, 10); curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback); curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, mCurlHandle); + curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback); + curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, mCurlHandle); + switch (mReqMethod) + { + case HOR_GET: + curl_easy_setopt(mCurlHandle, CURLOPT_HTTPGET, 1); + break; + + case HOR_POST: + { + curl_easy_setopt(mCurlHandle, CURLOPT_POST, 1); + long data_size(0); + if (mReqBody) + { + mReqBody->seek(0); + data_size = mReqBody->size(); + } + curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, static_cast(NULL)); + curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDSIZE, data_size); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Transfer-Encoding: chunked"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); + } + break; + + case HOR_PUT: + { + curl_easy_setopt(mCurlHandle, CURLOPT_UPLOAD, 1); + long data_size(0); + if (mReqBody) + { + mReqBody->seek(0); + data_size = mReqBody->size(); + } + curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Transfer-Encoding: chunked"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); + } + break; + + default: + // *FIXME: fail out here + break; + } + if (mReqHeaders) { mCurlHeaders = append_headers_to_slist(mReqHeaders, mCurlHeaders); } mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); - if (mReqOffset || mReqLength) + if ((mReqOffset || mReqLength) && HOR_GET == mReqMethod) { static const char * fmt1("Range: bytes=%d-%d"); static const char * fmt2("Range: bytes=%d-"); @@ -347,6 +430,32 @@ size_t HttpOpRequest::writeCallback(void * data, size_t size, size_t nmemb, void } +size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void * userdata) +{ + CURL * handle(static_cast(userdata)); + HttpOpRequest * op(NULL); + curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); + // *FIXME: check the pointer + + if (! op->mReqBody) + { + return 0; + } + const size_t req_size(size * nmemb); + const size_t body_size(op->mReqBody->size()); + if (body_size <= op->mCurlBodyPos) + { + // *FIXME: should probably log this event - unexplained + return 0; + } + + const size_t do_size((std::min)(req_size, body_size - op->mCurlBodyPos)); + op->mReqBody->read(static_cast(data), do_size); + op->mCurlBodyPos += do_size; + return do_size; +} + + size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, void * userdata) { static const char status_line[] = "HTTP/"; diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 601937a943..7efed0b1d9 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -84,12 +84,20 @@ public: HttpOptions * options, HttpHeaders * headers); - HttpStatus prepareForGet(HttpService * service); + HttpStatus setupPost(unsigned int policy_id, + float priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers); + + HttpStatus prepareRequest(HttpService * service); virtual HttpStatus cancel(); protected: static size_t writeCallback(void * data, size_t size, size_t nmemb, void * userdata); + static size_t readCallback(void * data, size_t size, size_t nmemb, void * userdata); static size_t headerCallback(void * data, size_t size, size_t nmemb, void * userdata); protected: @@ -112,6 +120,7 @@ public: CURL * mCurlHandle; HttpService * mCurlService; curl_slist * mCurlHeaders; + size_t mCurlBodyPos; // Result data HttpStatus mStatus; diff --git a/indra/llcorehttp/_httpopsetpriority.cpp b/indra/llcorehttp/_httpopsetpriority.cpp new file mode 100644 index 0000000000..dc609421ed --- /dev/null +++ b/indra/llcorehttp/_httpopsetpriority.cpp @@ -0,0 +1,77 @@ +/** + * @file _httpopsetpriority.cpp + * @brief Definitions for internal classes based on HttpOpSetPriority + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpopsetpriority.h" + +#include "httpresponse.h" +#include "httphandler.h" +#include "_httpservice.h" + + +namespace LLCore +{ + + +HttpOpSetPriority::HttpOpSetPriority(HttpHandle handle, unsigned int priority) + : HttpOperation(), + mHandle(handle), + mPriority(priority) +{} + + +HttpOpSetPriority::~HttpOpSetPriority() +{} + + +void HttpOpSetPriority::stageFromRequest(HttpService * service) +{ + // Do operations + if (! service->changePriority(mHandle, mPriority)) + { + // Request not found, fail the final status + mStatus = HttpStatus(HttpStatus::LLCORE, HE_HANDLE_NOT_FOUND); + } + + // Move directly to response queue + addAsReply(); +} + + +void HttpOpSetPriority::visitNotifier(HttpRequest * request) +{ + if (mLibraryHandler) + { + HttpResponse * response = new HttpResponse(); + + response->setStatus(mStatus); + mLibraryHandler->onCompleted(static_cast(this), response); + + response->release(); + } +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httpopsetpriority.h b/indra/llcorehttp/_httpopsetpriority.h new file mode 100644 index 0000000000..e5d8e5fc1f --- /dev/null +++ b/indra/llcorehttp/_httpopsetpriority.h @@ -0,0 +1,70 @@ +/** + * @file _httpsetpriority.h + * @brief Internal declarations for HttpSetPriority + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_SETPRIORITY_H_ +#define _LLCORE_HTTP_SETPRIORITY_H_ + + +#include "httpcommon.h" + +#include "_httpoperation.h" +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// HttpOpSetPriority is an immediate request that +/// searches the various queues looking for a given +/// request handle and changing it's priority if +/// found. + +class HttpOpSetPriority : public HttpOperation +{ +public: + HttpOpSetPriority(HttpHandle handle, unsigned int priority); + virtual ~HttpOpSetPriority(); + +private: + HttpOpSetPriority(const HttpOpSetPriority &); // Not defined + void operator=(const HttpOpSetPriority &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + + virtual void visitNotifier(HttpRequest * request); + +protected: + HttpStatus mStatus; + HttpHandle mHandle; + unsigned int mPriority; +}; // end class HttpOpSetPriority + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_SETPRIORITY_H_ + diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index d965a6cf3a..873b519c51 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -28,48 +28,113 @@ #include "_httpoprequest.h" #include "_httpservice.h" +#include "_httplibcurl.h" namespace LLCore { - HttpPolicy::HttpPolicy(HttpService * service) : mService(service) -{} +{ + for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) + { + mReadyInClass[policy_class] = 0; + } +} HttpPolicy::~HttpPolicy() { - for (ready_queue_t::reverse_iterator i(mReadyQueue.rbegin()); - mReadyQueue.rend() != i;) + for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) { - ready_queue_t::reverse_iterator cur(i++); - - (*cur)->cancel(); - (*cur)->release(); + HttpReadyQueue & readyq(mReadyQueue[policy_class]); + + while (! readyq.empty()) + { + HttpOpRequest * op(readyq.top()); + + op->cancel(); + op->release(); + mReadyInClass[policy_class]--; + readyq.pop(); + } } - mService = NULL; } void HttpPolicy::addOp(HttpOpRequest * op) { - mReadyQueue.push_back(op); + const int policy_class(op->mReqPolicy); + + mReadyQueue[policy_class].push(op); + ++mReadyInClass[policy_class]; } -void HttpPolicy::processReadyQueue() +HttpService::ELoopSpeed HttpPolicy::processReadyQueue() { - while (! mReadyQueue.empty()) + HttpService::ELoopSpeed result(HttpService::REQUEST_SLEEP); + HttpLibcurl * pTransport(mService->getTransport()); + + for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) { - HttpOpRequest * op(mReadyQueue.front()); - mReadyQueue.erase(mReadyQueue.begin()); + HttpReadyQueue & readyq(mReadyQueue[policy_class]); + int active(pTransport->getActiveCountInClass(policy_class)); + int needed(8 - active); + + if (needed > 0 && mReadyInClass[policy_class] > 0) + { + // Scan ready queue for requests that match policy + + while (! readyq.empty() && needed > 0 && mReadyInClass[policy_class] > 0) + { + HttpOpRequest * op(readyq.top()); + readyq.pop(); + + op->stageFromReady(mService); + op->release(); + + --mReadyInClass[policy_class]; + --needed; + } + } + + if (! readyq.empty()) + { + // If anything is ready, continue looping... + result = (std::min)(result, HttpService::NORMAL); + } + } + + return result; +} - op->stageFromReady(mService); - op->release(); + +bool HttpPolicy::changePriority(HttpHandle handle, unsigned int priority) +{ + for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) + { + HttpReadyQueue::container_type & c(mReadyQueue[policy_class].get_container()); + + // Scan ready queue for requests that match policy + for (HttpReadyQueue::container_type::iterator iter(c.begin()); c.end() != iter;) + { + HttpReadyQueue::container_type::iterator cur(iter++); + + if (static_cast(*cur) == handle) + { + HttpOpRequest * op(*cur); + c.erase(cur); // All iterators are now invalidated + op->mReqPriority = priority; + mReadyQueue[policy_class].push(op); // Re-insert using adapter class + return true; + } + } } + + return false; } diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 192bc73b31..c5e82d0a65 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -28,18 +28,23 @@ #define _LLCORE_HTTP_POLICY_H_ -#include +#include "httprequest.h" +#include "_httpservice.h" +#include "_httpreadyqueue.h" namespace LLCore { - -class HttpService; +class HttpReadyQueue; class HttpOpRequest; /// Implements class-based queuing policies for an HttpService instance. +/// +/// Threading: Single-threaded. Other than for construction/destruction, +/// all methods are expected to be invoked in a single thread, typically +/// a worker thread of some sort. class HttpPolicy { public: @@ -51,16 +56,23 @@ private: void operator=(const HttpPolicy &); // Not defined public: - void processReadyQueue(); + /// Give the policy layer some cycles to scan the ready + /// queue promoting higher-priority requests to active + /// as permited. + HttpService::ELoopSpeed processReadyQueue(); + /// Add request to a ready queue. Caller is expected to have + /// provided us with a reference count to hold the request. (No + /// additional references will be added.) void addOp(HttpOpRequest *); + + // Shadows HttpService's method + bool changePriority(HttpHandle handle, unsigned int priority); protected: - typedef std::vector ready_queue_t; - -protected: + int mReadyInClass[HttpRequest::POLICY_CLASS_LIMIT]; + HttpReadyQueue mReadyQueue[HttpRequest::POLICY_CLASS_LIMIT]; HttpService * mService; // Naked pointer, not refcounted, not owner - ready_queue_t mReadyQueue; }; // end class HttpPolicy diff --git a/indra/llcorehttp/_httpreadyqueue.h b/indra/llcorehttp/_httpreadyqueue.h new file mode 100644 index 0000000000..283e868b4c --- /dev/null +++ b/indra/llcorehttp/_httpreadyqueue.h @@ -0,0 +1,85 @@ +/** + * @file _httpreadyqueue.h + * @brief Internal declaration for the operation ready queue + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_READY_QUEUE_H_ +#define _LLCORE_HTTP_READY_QUEUE_H_ + + +#include + +#include "_httpoperation.h" + + +namespace LLCore +{ + +class HttpOpRequest; + +/// HttpReadyQueue provides a simple priority queue for HttpOpRequest objects. +/// +/// This uses the priority_queue adaptor class to provide the queue +/// as well as the ordering scheme while allowing us access to the +/// raw container if we follow a few simple rules. One of the more +/// important of those rules is that any iterator becomes invalid +/// on element erasure. So pay attention. +/// +/// Threading: not thread-safe. Expected to be used entirely by +/// a single thread, typically a worker thread of some sort. + +class HttpReadyQueue : public std::priority_queue, + LLCore::HttpOpCompare> +{ +public: + HttpReadyQueue() + : priority_queue() + {} + + ~HttpReadyQueue() + {} + +protected: + HttpReadyQueue(const HttpReadyQueue &); // Not defined + void operator=(const HttpReadyQueue &); // Not defined + +public: + const container_type & get_container() const + { + return c; + } + + container_type & get_container() + { + return c; + } + +}; // end class HttpReadyQueue + + +} // end namespace LLCore + + +#endif // _LLCORE_HTTP_READY_QUEUE_H_ diff --git a/indra/llcorehttp/_httprequestqueue.cpp b/indra/llcorehttp/_httprequestqueue.cpp index c36814aee3..92bb5ec5c1 100644 --- a/indra/llcorehttp/_httprequestqueue.cpp +++ b/indra/llcorehttp/_httprequestqueue.cpp @@ -57,7 +57,7 @@ HttpRequestQueue::~HttpRequestQueue() void HttpRequestQueue::init() { - LLINT_ASSERT(! sInstance); + llassert_always(! sInstance); sInstance = new HttpRequestQueue(); } diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index 6ebc0ec6cb..48884ca060 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -35,6 +35,8 @@ #include "_httplibcurl.h" #include "_thread.h" +#include "lltimer.h" + namespace LLCore { @@ -89,8 +91,8 @@ HttpService::~HttpService() void HttpService::init(HttpRequestQueue * queue) { - LLINT_ASSERT(! sInstance); - LLINT_ASSERT(NOT_INITIALIZED == sState); + llassert_always(! sInstance); + llassert_always(NOT_INITIALIZED == sState); sInstance = new HttpService(); queue->addRef(); @@ -103,7 +105,7 @@ void HttpService::init(HttpRequestQueue * queue) void HttpService::term() { - LLINT_ASSERT(RUNNING != sState); + llassert_always(RUNNING != sState); if (sInstance) { delete sInstance; @@ -132,8 +134,8 @@ bool HttpService::isStopped() void HttpService::startThread() { - LLINT_ASSERT(! mThread || STOPPED == sState); - LLINT_ASSERT(INITIALIZED == sState || STOPPED == sState); + llassert_always(! mThread || STOPPED == sState); + llassert_always(INITIALIZED == sState || STOPPED == sState); if (mThread) { @@ -150,6 +152,20 @@ void HttpService::stopRequested() mExitRequested = true; } +bool HttpService::changePriority(HttpHandle handle, unsigned int priority) +{ + bool found(false); + + // Skip the request queue as we currently don't leave earlier + // requests sitting there. Start with the ready queue... + found = mPolicy->changePriority(handle, priority); + + // If not there, we could try the transport/active queue but priority + // doesn't really have much effect there so we don't waste cycles. + + return found; +} + void HttpService::shutdown() { @@ -157,38 +173,46 @@ void HttpService::shutdown() } +// Working thread loop-forever method. Gives time to +// each of the request queue, policy layer and transport +// layer pieces and then either sleeps for a small time +// or waits for a request to come in. Repeats until +// requested to stop. void HttpService::threadRun(LLCoreInt::HttpThread * thread) { boost::this_thread::disable_interruption di; - + ELoopSpeed loop(REQUEST_SLEEP); + while (! mExitRequested) { - processRequestQueue(); + loop = processRequestQueue(loop); // Process ready queue issuing new requests as needed - mPolicy->processReadyQueue(); + ELoopSpeed new_loop = mPolicy->processReadyQueue(); + loop = (std::min)(loop, new_loop); // Give libcurl some cycles - mTransport->processTransport(); + new_loop = mTransport->processTransport(); + loop = (std::min)(loop, new_loop); // Determine whether to spin, sleep briefly or sleep for next request - // *FIXME: For now, do this -#if defined(WIN32) - Sleep(50); -#else - usleep(5000); -#endif + if (REQUEST_SLEEP != loop) + { + ms_sleep(50); + } } + shutdown(); sState = STOPPED; } -void HttpService::processRequestQueue() +HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop) { HttpRequestQueue::OpContainer ops; - - mRequestQueue->fetchAll(false, ops); + const bool wait_for_req(REQUEST_SLEEP == loop); + + mRequestQueue->fetchAll(wait_for_req, ops); while (! ops.empty()) { HttpOperation * op(ops.front()); @@ -203,6 +227,9 @@ void HttpService::processRequestQueue() // Done with operation op->release(); } + + // Queue emptied, allow polling loop to sleep + return REQUEST_SLEEP; } diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index ba76e1eeca..3e5a5457d7 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -28,6 +28,9 @@ #define _LLCORE_HTTP_SERVICE_H_ +#include "httpcommon.h" + + namespace LLCoreInt { @@ -86,7 +89,17 @@ public: RUNNING, ///< thread created and running STOPPED ///< thread has committed to exiting }; - + + // Ordered enumeration of idling strategies available to + // threadRun's loop. Ordered so that std::min on values + // produces the most conservative result of multiple + // requests. + enum ELoopSpeed + { + NORMAL, ///< continuous polling of request, ready, active queues + REQUEST_SLEEP ///< can sleep indefinitely waiting for request queue write + }; + static void init(HttpRequestQueue *); static void term(); @@ -124,6 +137,15 @@ public: /// Threading: callable by worker thread. void shutdown(); + + /// Try to find the given request handle on any of the request + /// queues and reset the priority (and queue position) of the + /// request if found. + /// + /// @return True if the request was found somewhere. + /// + /// Threading: callable by worker thread. + bool changePriority(HttpHandle handle, unsigned int priority); HttpPolicy * getPolicy() { @@ -138,7 +160,7 @@ public: protected: void threadRun(LLCoreInt::HttpThread * thread); - void processRequestQueue(); + ELoopSpeed processRequestQueue(ELoopSpeed loop); protected: static HttpService * sInstance; diff --git a/indra/llcorehttp/_refcounted.h b/indra/llcorehttp/_refcounted.h index 4a6ce8420a..72cef6b342 100644 --- a/indra/llcorehttp/_refcounted.h +++ b/indra/llcorehttp/_refcounted.h @@ -30,7 +30,7 @@ #include -#include "_assert.h" +#include "linden_common.h" namespace LLCoreInt @@ -71,7 +71,7 @@ private: inline void RefCounted::addRef() const { boost::mutex::scoped_lock lock(mRefLock); - LLINT_ASSERT(mRefCount >= 0); + llassert_always(mRefCount >= 0); ++mRefCount; } @@ -82,8 +82,8 @@ inline void RefCounted::release() const { // CRITICAL SECTION boost::mutex::scoped_lock lock(mRefLock); - LLINT_ASSERT(mRefCount != NOT_REF_COUNTED); - LLINT_ASSERT(mRefCount > 0); + llassert_always(mRefCount != NOT_REF_COUNTED); + llassert_always(mRefCount > 0); count = --mRefCount; // CRITICAL SECTION } @@ -104,8 +104,8 @@ inline bool RefCounted::isLastRef() const // CRITICAL SECTION boost::mutex::scoped_lock lock(mRefLock); - LLINT_ASSERT(mRefCount != NOT_REF_COUNTED); - LLINT_ASSERT(mRefCount >= 1); + llassert_always(mRefCount != NOT_REF_COUNTED); + llassert_always(mRefCount >= 1); count = mRefCount; // CRITICAL SECTION } @@ -125,7 +125,7 @@ inline int RefCounted::getRefCount() const inline void RefCounted::noRef() const { boost::mutex::scoped_lock lock(mRefLock); - LLINT_ASSERT(mRefCount <= 1); + llassert_always(mRefCount <= 1); mRefCount = NOT_REF_COUNTED; } diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp index b5872606b8..273acae132 100644 --- a/indra/llcorehttp/httpcommon.cpp +++ b/indra/llcorehttp/httpcommon.cpp @@ -28,6 +28,7 @@ #include #include +#include namespace LLCore @@ -46,6 +47,16 @@ HttpStatus::operator unsigned long() const } +std::string HttpStatus::toHex() const +{ + std::ostringstream result; + result.width(8); + result.fill('0'); + result << std::hex << operator unsigned long(); + return result.str(); + +} + std::string HttpStatus::toString() const { static const char * llcore_errors[] = @@ -54,7 +65,8 @@ std::string HttpStatus::toString() const "HTTP error reply status", "Services shutting down", "Operation canceled", - "Invalid Content-Range header encountered" + "Invalid Content-Range header encountered", + "Request handle not found" }; static const int llcore_errors_count(sizeof(llcore_errors) / sizeof(llcore_errors[0])); @@ -131,6 +143,7 @@ std::string HttpStatus::toString() const default: if (isHttpStatus()) { + // Binary search for the error code and string int bottom(0), top(http_errors_count); while (true) { diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index f81be7103e..c01a5f85d3 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -134,7 +134,10 @@ enum HttpError HE_OP_CANCELED = 3, // Invalid content range header received. - HE_INV_CONTENT_RANGE_HDR = 4 + HE_INV_CONTENT_RANGE_HDR = 4, + + // Request handle not found + HE_HANDLE_NOT_FOUND = 5 }; // end enum HttpError @@ -229,6 +232,9 @@ struct HttpStatus { return operator unsigned long(); } + + /// And to convert to a hex string. + std::string toHex() const; /// Convert status to a string representation. For /// success, returns an empty string. For failure diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 6c62f931ff..a06b859a91 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -31,6 +31,7 @@ #include "_httpservice.h" #include "_httpoperation.h" #include "_httpoprequest.h" +#include "_httpopsetpriority.h" #include "_httpopcancel.h" @@ -162,7 +163,7 @@ HttpStatus HttpRequest::getStatus() const HttpHandle HttpRequest::requestGetByteRange(unsigned int policy_id, - float priority, + unsigned int priority, const std::string & url, size_t offset, size_t len, @@ -190,6 +191,34 @@ HttpHandle HttpRequest::requestGetByteRange(unsigned int policy_id, } +HttpHandle HttpRequest::requestPost(unsigned int policy_id, + unsigned int priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpRequest * op = new HttpOpRequest(); + if (! (status = op->setupPost(policy_id, priority, url, body, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + mRequestQueue->addOp(op); // transfers refcount + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + + HttpHandle HttpRequest::requestCancel(HttpHandle handle, HttpHandler * user_handler) { HttpStatus status; @@ -222,6 +251,23 @@ HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) } +HttpHandle HttpRequest::requestSetPriority(HttpHandle request, unsigned int priority, + HttpHandler * handler) +{ + HttpStatus status; + HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpSetPriority * op = new HttpOpSetPriority(request, priority); + op->setHandlers(mReplyQueue, mSelfHandler, handler); + mRequestQueue->addOp(op); // transfer refcount as well + + mLastReqStatus = status; + ret_handle = static_cast(op); + + return ret_handle; +} + + HttpStatus HttpRequest::update(long millis) { HttpStatus status; @@ -259,7 +305,7 @@ HttpStatus HttpRequest::createService() { HttpStatus status; - LLINT_ASSERT(! has_inited); + llassert_always(! has_inited); HttpRequestQueue::init(); HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); HttpService::init(rq); @@ -273,7 +319,7 @@ HttpStatus HttpRequest::destroyService() { HttpStatus status; - LLINT_ASSERT(has_inited); + llassert_always(has_inited); HttpService::term(); HttpRequestQueue::term(); has_inited = false; diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 4bbd13a13a..e2ab9be533 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -41,6 +41,7 @@ class HttpService; class HttpOptions; class HttpHeaders; class HttpOperation; +class BufferArray; /// HttpRequest supplies the entry into the HTTP transport /// services in the LLCore libraries. Services provided include: @@ -96,6 +97,10 @@ public: /// Represents a default, catch-all policy class that guarantees /// eventual service for any HTTP request. static const int DEFAULT_POLICY_ID = 0; + + /// Maximum number of policies that may be defined. No policy + /// ID will equal or exceed this value. + static const int POLICY_CLASS_LIMIT = 1; enum EGlobalPolicy { @@ -177,7 +182,7 @@ public: /// @param policy_id Default or user-defined policy class under /// which this request is to be serviced. /// @param priority Standard priority scheme inherited from - /// Indra code base. + /// Indra code base (U32-type scheme). /// @param url /// @param offset /// @param len @@ -190,7 +195,7 @@ public: /// case, @see getStatus() will return more info. /// HttpHandle requestGetByteRange(unsigned int policy_id, - float priority, + unsigned int priority, const std::string & url, size_t offset, size_t len, @@ -199,6 +204,32 @@ public: HttpHandler * handler); + /// + /// @param policy_id Default or user-defined policy class under + /// which this request is to be serviced. + /// @param priority Standard priority scheme inherited from + /// Indra code base. + /// @param url + /// @param body Byte stream to be sent as the body. No + /// further encoding or escaping will be done + /// to the content. + /// @param options (optional) + /// @param headers (optional) + /// @param handler (optional) + /// @return The handle of the request if successfully + /// queued or LLCORE_HTTP_HANDLE_INVALID if the + /// request could not be queued. In the latter + /// case, @see getStatus() will return more info. + /// + HttpHandle requestPost(unsigned int policy_id, + unsigned int priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * handler); + + /// Queue a NoOp request. /// The request is queued and serviced by the working thread which /// immediately processes it and returns the request to the reply @@ -235,6 +266,20 @@ public: HttpHandle requestCancel(HttpHandle request, HttpHandler *); + /// Request that a previously-issued request be reprioritized. + /// The status of whether the change itself succeeded arrives + /// via notification. + /// + /// @param request Handle of previously-issued request to + /// be changed. + /// @param priority New priority value. + /// @param handler (optional) + /// @return The handle of the request if successfully + /// queued or LLCORE_HTTP_HANDLE_INVALID if the + /// request could not be queued. + /// + HttpHandle requestSetPriority(HttpHandle request, unsigned int priority, HttpHandler * handler); + /// @} /// @name UtilityMethods diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index a73d90957e..0e9d7d8979 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -370,7 +370,7 @@ void HttpRequestTestObjectType::test<5>() // Issue a GET that can't connect mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, - 0.0f, + 0U, "http://127.0.0.1:2/nothing/here", 0, 0, diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 17c68f7c22..381364b5c3 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -51,6 +51,7 @@ #include "llviewerstats.h" #include "llviewerassetstats.h" #include "llworld.h" +#include "llsdserialize.h" #include "httprequest.h" #include "httphandler.h" @@ -748,7 +749,7 @@ void LLTextureFetchWorker::setImagePriority(F32 priority) { mImagePriority = priority; calcWorkPriority(); - U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_LOW; + U32 work_priority = mWorkPriority | (getPriority() & LLWorkerThread::PRIORITY_HIGHBITS); setPriority(work_priority); } } @@ -855,7 +856,6 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == LOAD_FROM_TEXTURE_CACHE) { - setPriority(0); // Set priority first since Responder may change it if (mCacheReadHandle == LLTextureCache::nullHandle()) { U32 cache_priority = mWorkPriority; @@ -871,6 +871,8 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mUrl.compare(0, 7, "file://") == 0) { + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + // read file from local disk std::string filename = mUrl.substr(7, std::string::npos); CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); @@ -879,6 +881,8 @@ bool LLTextureFetchWorker::doWork(S32 param) } else if (mUrl.empty()) { + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority, offset, size, responder); @@ -890,12 +894,12 @@ bool LLTextureFetchWorker::doWork(S32 param) // *TODO:?remove this warning llwarns << "Unknown URL Type: " << mUrl << llendl; } - setPriority(LLWorkerThread::PRIORITY_HIGH); + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mState = SEND_HTTP_REQ; } else { - setPriority(LLWorkerThread::PRIORITY_HIGH); + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mState = LOAD_FROM_NETWORK; } } @@ -907,7 +911,6 @@ bool LLTextureFetchWorker::doWork(S32 param) { mCacheReadHandle = LLTextureCache::nullHandle(); mState = CACHE_POST; - setPriority(LLWorkerThread::PRIORITY_HIGH); // fall through } else @@ -931,7 +934,6 @@ bool LLTextureFetchWorker::doWork(S32 param) llassert_always(mFormattedImage->getDataSize() > 0); mLoadedDiscard = mDesiredDiscard; mState = DECODE_IMAGE; - setPriority(LLWorkerThread::PRIORITY_HIGH); mWriteToCacheState = NOT_WRITE ; LL_DEBUGS("Texture") << mID << ": Cached. Bytes: " << mFormattedImage->getDataSize() << " Size: " << llformat("%dx%d",mFormattedImage->getWidth(),mFormattedImage->getHeight()) @@ -949,7 +951,6 @@ bool LLTextureFetchWorker::doWork(S32 param) else { LL_DEBUGS("Texture") << mID << ": Not in Cache" << LL_ENDL; - setPriority(LLWorkerThread::PRIORITY_HIGH); mState = LOAD_FROM_NETWORK; } // fall through @@ -960,7 +961,6 @@ bool LLTextureFetchWorker::doWork(S32 param) { static LLCachedControl use_http(gSavedSettings,"ImagePipelineUseHTTP"); - setPriority(0); // if (mHost != LLHost::invalid) get_url = false; if ( use_http && mCanUseHTTP && mUrl.empty())//get http url. { @@ -993,7 +993,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mCanUseHTTP && !mUrl.empty()) { mState = SEND_HTTP_REQ; - setPriority(LLWorkerThread::PRIORITY_HIGH); + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); if(mWriteToCacheState != NOT_WRITE) { mWriteToCacheState = CAN_WRITE ; @@ -1010,6 +1010,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mSentRequest = QUEUED; mFetcher->addToNetworkQueue(this); recordTextureStart(false); + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } @@ -1027,7 +1028,6 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == LOAD_FROM_SIMULATOR) { - setPriority(0); if (mFormattedImage.isNull()) { mFormattedImage = new LLImageJ2C; @@ -1042,7 +1042,7 @@ bool LLTextureFetchWorker::doWork(S32 param) // llwarns << "processSimulatorPackets() failed to load buffer" << llendl; return true; // failed } - setPriority(LLWorkerThread::PRIORITY_HIGH); + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mState = DECODE_IMAGE; mWriteToCacheState = SHOULD_WRITE; recordTextureDone(false); @@ -1050,6 +1050,7 @@ bool LLTextureFetchWorker::doWork(S32 param) else { mFetcher->addToNetworkQueue(this); // failsafe + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); recordTextureStart(false); } return false; @@ -1057,7 +1058,6 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == SEND_HTTP_REQ) { - setPriority(0); if(mCanUseHTTP) { //NOTE: @@ -1065,11 +1065,13 @@ bool LLTextureFetchWorker::doWork(S32 param) //1, not openning too many file descriptors at the same time; //2, control the traffic of http so udp gets bandwidth. // - static const S32 MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE = 8 ; - if(mFetcher->getNumHTTPRequests() > MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE) - { - return false ; //wait. - } + static const S32 MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE = 8; + // *FIXME: For the moment, allow everything to transition into HTTP + // and have the new library order and throttle. + //if(mFetcher->getNumHTTPRequests() > MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE) + //{ + //return false ; //wait. + //} mFetcher->removeFromNetworkQueue(this, false); @@ -1083,7 +1085,6 @@ bool LLTextureFetchWorker::doWork(S32 param) { // We already have all the data, just decode it mLoadedDiscard = mFormattedImage->getDiscardLevel(); - setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DECODE_IMAGE; return false; } @@ -1135,7 +1136,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mHttpActive = true; mFetcher->addToHTTPQueue(mID); recordTextureStart(true); - setPriority(LLWorkerThread::PRIORITY_HIGH); + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); mState = WAIT_HTTP_REQ; // fall through @@ -1148,7 +1149,6 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == WAIT_HTTP_REQ) { - setPriority(0); if (mLoaded) { S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; @@ -1165,7 +1165,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { mState = INIT; mCanUseHTTP = false; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); return false; } } @@ -1184,7 +1184,8 @@ bool LLTextureFetchWorker::doWork(S32 param) max_attempts = HTTP_MAX_RETRY_COUNT + 1; ++mHTTPFailCount; llinfos << "HTTP GET failed for: " << mUrl - << " Status: " << mGetStatus.toULong() << " Reason: '" << mGetReason << "'" + << " Status: " << mGetStatus.toHex() + << " Reason: '" << mGetReason << "'" << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl; } @@ -1194,14 +1195,12 @@ bool LLTextureFetchWorker::doWork(S32 param) { // Use available data mLoadedDiscard = mFormattedImage->getDiscardLevel(); - setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DECODE_IMAGE; return false; } else { resetFormattedData(); - setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DONE; return true; // failed } @@ -1223,7 +1222,6 @@ bool LLTextureFetchWorker::doWork(S32 param) } // abort. - setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DONE; return true; } @@ -1268,29 +1266,30 @@ bool LLTextureFetchWorker::doWork(S32 param) mHttpBufferArray = NULL; mLoadedDiscard = mRequestedDiscard; - setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DECODE_IMAGE; if (mWriteToCacheState != NOT_WRITE) { mWriteToCacheState = SHOULD_WRITE ; } + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); return false; } else { + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } } if (mState == DECODE_IMAGE) { - setPriority(0); static LLCachedControl textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled"); + + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it if (textures_decode_disabled) { // for debug use, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_HIGH); return true; } @@ -1298,7 +1297,6 @@ bool LLTextureFetchWorker::doWork(S32 param) { // We aborted, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_HIGH); return true; } @@ -1308,7 +1306,6 @@ bool LLTextureFetchWorker::doWork(S32 param) //abort, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_HIGH); return true; } if (mLoadedDiscard < 0) @@ -1317,7 +1314,6 @@ bool LLTextureFetchWorker::doWork(S32 param) //abort, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_HIGH); return true; } @@ -1337,7 +1333,6 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == DECODE_IMAGE_UPDATE) { - setPriority(0); if (mDecoded) { if (mDecodedDiscard < 0) @@ -1350,14 +1345,13 @@ bool LLTextureFetchWorker::doWork(S32 param) llassert_always(mDecodeHandle == 0); mFormattedImage = NULL; ++mRetryAttempt; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mState = INIT; return false; } else { // llwarns << "UNABLE TO LOAD TEXTURE: " << mID << " RETRIES: " << mRetryAttempt << llendl; - setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DONE; // failed } } @@ -1366,7 +1360,7 @@ bool LLTextureFetchWorker::doWork(S32 param) llassert_always(mRawImage.notNull()); LL_DEBUGS("Texture") << mID << ": Decoded. Discard: " << mDecodedDiscard << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL; - setPriority(LLWorkerThread::PRIORITY_HIGH); + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mState = WRITE_TO_CACHE; } // fall through @@ -1379,12 +1373,10 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == WRITE_TO_CACHE) { - setPriority(0); if (mWriteToCacheState != SHOULD_WRITE || mFormattedImage.isNull()) { // If we're in a local cache or we didn't actually receive any new data, // or we failed to load anything, skip - setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DONE; return false; } @@ -1404,7 +1396,6 @@ bool LLTextureFetchWorker::doWork(S32 param) setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it U32 cache_priority = mWorkPriority; mWritten = FALSE; - setPriority(LLWorkerThread::PRIORITY_HIGH); mState = WAIT_ON_WRITE; CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID); mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority, @@ -1415,10 +1406,8 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == WAIT_ON_WRITE) { - setPriority(0); if (writeToCacheComplete()) { - setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DONE; // fall through } @@ -1437,15 +1426,16 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == DONE) { - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); if (mDecodedDiscard >= 0 && mDesiredDiscard < mDecodedDiscard) { // More data was requested, return to INIT mState = INIT; + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); return false; } else { + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return true; } } @@ -1477,7 +1467,8 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe LLCore::HttpStatus status(response->getStatus()); lldebugs << "HTTP COMPLETE: " << mID - << " status: " << status.toULong() << " '" << status.toString() << "'" + << " status: " << status.toHex() + << " '" << status.toString() << "'" << llendl; unsigned int offset(0), length(0); response->getRange(&offset, &length); @@ -1492,7 +1483,8 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe success = false; std::string reason(status.toString()); setGetStatus(status, reason); - llwarns << "CURL GET FAILED, status:" << status.toULong() << " reason:" << reason << llendl; + llwarns << "CURL GET FAILED, status: " << status.toHex() + << " reason: " << reason << llendl; } else { @@ -1727,7 +1719,7 @@ S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, mRequestedSize = -1; // error } mLoaded = TRUE; - setPriority(LLWorkerThread::PRIORITY_HIGH); + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); return data_size ; } @@ -1756,7 +1748,7 @@ void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* ima } } mLoaded = TRUE; - setPriority(LLWorkerThread::PRIORITY_HIGH); + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); } void LLTextureFetchWorker::callbackCacheWrite(bool success) @@ -1768,7 +1760,7 @@ void LLTextureFetchWorker::callbackCacheWrite(bool success) return; } mWritten = TRUE; - setPriority(LLWorkerThread::PRIORITY_HIGH); + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); } ////////////////////////////////////////////////////////////////////////////// @@ -1806,7 +1798,7 @@ void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImag } mDecoded = TRUE; // llinfos << mID << " : DECODE COMPLETE " << llendl; - setPriority(LLWorkerThread::PRIORITY_HIGH); + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); } ////////////////////////////////////////////////////////////////////////////// @@ -1883,7 +1875,6 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mHttpOptions(NULL), mHttpHeaders(NULL) { - mCurlPOSTRequestCount = 0; mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold")); @@ -2253,7 +2244,6 @@ S32 LLTextureFetch::getPending() LLMutexLock lock(&mQueueMutex); res = mRequestQueue.size(); - res += mCurlPOSTRequestCount; res += mCommands.size(); } unlockData(); @@ -2279,10 +2269,7 @@ bool LLTextureFetch::runCondition() have_no_commands = mCommands.empty(); } - bool have_no_curl_requests(0 == mCurlPOSTRequestCount); - return ! (have_no_commands - && have_no_curl_requests && (mRequestQueue.empty() && mIdleThread)); // From base class } @@ -2690,7 +2677,7 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 llassert_always(totalbytes > 0); llassert_always(data_size == FIRST_PACKET_SIZE || data_size == worker->mFileSize); res = worker->insertPacket(0, data, data_size); - worker->setPriority(LLWorkerThread::PRIORITY_HIGH); + worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; worker->unlockWorkMutex(); return res; @@ -2734,7 +2721,7 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 if ((worker->mState == LLTextureFetchWorker::LOAD_FROM_SIMULATOR) || (worker->mState == LLTextureFetchWorker::LOAD_FROM_NETWORK)) { - worker->setPriority(LLWorkerThread::PRIORITY_HIGH); + worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; } else @@ -2930,6 +2917,35 @@ void LLTextureFetch::cmdDoWork() namespace { + +// Example of a simple notification handler for metrics +// delivery notification. Earlier versions of the code used +// a Responder that tried harder to detect delivery breaks +// but it really isn't that important. If someone wants to +// revisit that effort, here is a place to start. +class AssetReportHandler : public LLCore::HttpHandler +{ +public: + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) + { + LLCore::HttpStatus status(response->getStatus()); + + if (status) + { + LL_WARNS("Texture") << "Successfully delivered asset metrics to grid." + << LL_ENDL; + } + else + { + LL_WARNS("Texture") << "Error delivering asset metrics to grid. Reason: " + << status.toString() << LL_ENDL; + } + } +}; // end class AssetReportHandler + +AssetReportHandler stats_handler; + + /** * Implements the 'Set Region' command. * @@ -2960,73 +2976,9 @@ TFReqSendMetrics::~TFReqSendMetrics() bool TFReqSendMetrics::doWork(LLTextureFetch * fetcher) { - /* - * HTTP POST responder. Doesn't do much but tries to - * detect simple breaks in recording the metrics stream. - * - * The 'volatile' modifiers don't indicate signals, - * mmap'd memory or threads, really. They indicate that - * the referenced data is part of a pseudo-closure for - * this responder rather than being required for correct - * operation. - * - * We don't try very hard with the POST request. We give - * it one shot and that's more-or-less it. With a proper - * refactoring of the LLQueuedThread usage, these POSTs - * could be put in a request object and made more reliable. - */ - class lcl_responder : public LLCurl::Responder - { - public: - lcl_responder(LLTextureFetch * fetcher, - S32 expected_sequence, - volatile const S32 & live_sequence, - volatile bool & reporting_break, - volatile bool & reporting_started) - : LLCurl::Responder(), - mFetcher(fetcher), - mExpectedSequence(expected_sequence), - mLiveSequence(live_sequence), - mReportingBreak(reporting_break), - mReportingStarted(reporting_started) - { - mFetcher->incrCurlPOSTCount(); - } - - ~lcl_responder() - { - mFetcher->decrCurlPOSTCount(); - } - - // virtual - void error(U32 status_num, const std::string & reason) - { - if (mLiveSequence == mExpectedSequence) - { - mReportingBreak = true; - } - LL_WARNS("Texture") << "Break in metrics stream due to POST failure to metrics collection service. Reason: " - << reason << LL_ENDL; - } - - // virtual - void result(const LLSD & content) - { - if (mLiveSequence == mExpectedSequence) - { - mReportingBreak = false; - mReportingStarted = true; - } - } - - private: - LLTextureFetch * mFetcher; - S32 mExpectedSequence; - volatile const S32 & mLiveSequence; - volatile bool & mReportingBreak; - volatile bool & mReportingStarted; - - }; // class lcl_responder + static const U32 report_priority(LLWorkerThread::PRIORITY_LOW); + static const int report_policy_class(LLCore::HttpRequest::DEFAULT_POLICY_ID); + static LLCore::HttpHandler * const handler(fetcher->isQAMode() || true ? &stats_handler : NULL); if (! gViewerAssetStatsThread1) return true; @@ -3054,24 +3006,37 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) // Update sequence number if (S32_MAX == ++report_sequence) report_sequence = 0; - + reporting_started = true; + // Limit the size of the stats report if necessary. merged_llsd["truncated"] = truncate_viewer_metrics(10, merged_llsd); if (! mCapsURL.empty()) { - LLCurlRequest::headers_t headers; -#if 0 - // *FIXME: Going to need a post op after all... - fetcher->getCurlRequest().post(mCapsURL, - headers, - merged_llsd, - new lcl_responder(fetcher, - report_sequence, - report_sequence, - LLTextureFetch::svMetricsDataBreak, - reporting_started)); -#endif + // *FIXME: This mess to get an llsd into a string though + // it's actually no worse than what we currently do... + std::stringstream body; + LLSDSerialize::toXML(merged_llsd, body); + std::string body_str(body.str()); + body.clear(); + + LLCore::HttpHeaders * headers = new LLCore::HttpHeaders; + headers->mHeaders.push_back("Content-Type: application/llsd+xml"); + + LLCore::BufferArray * ba = new LLCore::BufferArray; + ba->append(body_str.c_str(), body_str.length()); + body_str.clear(); + + fetcher->getHttpRequest().requestPost(report_policy_class, + report_priority, + mCapsURL, + ba, + NULL, + headers, + handler); + ba->release(); + headers->release(); + LLTextureFetch::svMetricsDataBreak = false; } else { @@ -3079,7 +3044,7 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) } // In QA mode, Metrics submode, log the result for ease of testing - if (fetcher->isQAMode()) + if (fetcher->isQAMode() || true) { LL_INFOS("Textures") << merged_llsd << LL_ENDL; } diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 402b198246..cfea3aad9d 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -39,7 +39,6 @@ class LLViewerTexture; class LLTextureFetchWorker; -class HTTPGetResponder; class LLTextureCache; class LLImageDecodeThread; class LLHost; @@ -49,7 +48,6 @@ class LLViewerAssetStats; class LLTextureFetch : public LLWorkerThread { friend class LLTextureFetchWorker; - friend class HTTPGetResponder; public: LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded, bool qa_mode); @@ -90,8 +88,6 @@ public: LLTextureFetchWorker* getWorker(const LLUUID& id); LLTextureFetchWorker* getWorkerAfterLock(const LLUUID& id); - LLTextureInfo* getTextureInfo() { return &mTextureInfo; } - // Commands available to other threads to control metrics gathering operations. void commandSetRegion(U64 region_handle); void commandSendMetrics(const std::string & caps_url, @@ -104,10 +100,6 @@ public: bool isQAMode() const { return mQAMode; } - // Curl POST counter maintenance - inline void incrCurlPOSTCount() { mCurlPOSTRequestCount++; } - inline void decrCurlPOSTCount() { mCurlPOSTRequestCount--; } - protected: void addToNetworkQueue(LLTextureFetchWorker* worker); void removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel); @@ -199,13 +191,6 @@ private: // If true, modifies some behaviors that help with QA tasks. const bool mQAMode; - // Count of POST requests outstanding. We maintain the count - // indirectly in the CURL request responder's ctor and dtor and - // use it when determining whether or not to sleep the thread. Can't - // use the LLCurl module's request counter as it isn't thread compatible. - // *NOTE: Don't mix Atomic and static, apr_initialize must be called first. - LLAtomic32 mCurlPOSTRequestCount; - // Interfaces and objects into the core http library used // to make our HTTP requests. These replace the various // LLCurl interfaces used in the past. -- cgit v1.2.3 From f67084337cf8673d7d3fa76dbf3ace3b02b93e2c Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 1 Jun 2012 16:24:02 -0400 Subject: Delete a file, edit cmakelists.txt. --- indra/llcorehttp/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 85df5364db..9a073eb850 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -49,7 +49,6 @@ set(llcorehttp_HEADER_FILES httpoptions.h httprequest.h httpresponse.h - _assert.h _httplibcurl.h _httpopcancel.h _httpoperation.h -- cgit v1.2.3 From 7b9da4eeda7505162f37cbfa52591f7adff032e7 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 1 Jun 2012 17:23:51 -0400 Subject: Missed two instances of priority typed as 'float'. Became an excuse to go through an use a typedef for priority and policy class id. --- indra/llcorehttp/_httpoperation.h | 13 ++++++------- indra/llcorehttp/_httpoprequest.cpp | 8 ++++---- indra/llcorehttp/_httpoprequest.h | 12 ++++++------ indra/llcorehttp/_httpopsetpriority.cpp | 2 +- indra/llcorehttp/_httpopsetpriority.h | 10 +++++----- indra/llcorehttp/_httppolicy.cpp | 2 +- indra/llcorehttp/_httppolicy.h | 2 +- indra/llcorehttp/_httpservice.cpp | 2 +- indra/llcorehttp/_httpservice.h | 3 ++- indra/llcorehttp/httprequest.cpp | 18 +++++++++--------- indra/llcorehttp/httprequest.h | 20 ++++++++++++-------- 11 files changed, 48 insertions(+), 44 deletions(-) diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index 6c0c3183b7..4d9298d801 100644 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -29,7 +29,7 @@ #include "httpcommon.h" - +#include "httprequest.h" #include "_refcounted.h" @@ -39,7 +39,6 @@ namespace LLCore class HttpReplyQueue; class HttpHandler; class HttpService; -class HttpRequest; /// HttpOperation is the base class for all request/reply /// pairs. @@ -102,13 +101,13 @@ protected: void addAsReply(); protected: - HttpReplyQueue * mReplyQueue; // Have refcount - HttpHandler * mLibraryHandler; // Have refcount - HttpHandler * mUserHandler; // Have refcount + HttpReplyQueue * mReplyQueue; // Have refcount + HttpHandler * mLibraryHandler; // Have refcount + HttpHandler * mUserHandler; // Have refcount public: - unsigned int mReqPolicy; - unsigned int mReqPriority; + HttpRequest::policy_t mReqPolicy; + HttpRequest::priority_t mReqPriority; }; // end class HttpOperation diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 54b9990057..791ee5f860 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -233,8 +233,8 @@ HttpStatus HttpOpRequest::cancel() } -HttpStatus HttpOpRequest::setupGetByteRange(unsigned int policy_id, - float priority, +HttpStatus HttpOpRequest::setupGetByteRange(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, const std::string & url, size_t offset, size_t len, @@ -268,8 +268,8 @@ HttpStatus HttpOpRequest::setupGetByteRange(unsigned int policy_id, } -HttpStatus HttpOpRequest::setupPost(unsigned int policy_id, - float priority, +HttpStatus HttpOpRequest::setupPost(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, const std::string & url, BufferArray * body, HttpOptions * options, diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 7efed0b1d9..e973f6ad26 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -30,10 +30,10 @@ #include "linden_common.h" // Modifies curl/curl.h interfaces -#include "httpcommon.h" - #include +#include "httpcommon.h" +#include "httprequest.h" #include "_httpoperation.h" #include "_refcounted.h" @@ -76,16 +76,16 @@ public: public: // Setup Methods - HttpStatus setupGetByteRange(unsigned int policy_id, - float priority, + HttpStatus setupGetByteRange(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, const std::string & url, size_t offset, size_t len, HttpOptions * options, HttpHeaders * headers); - HttpStatus setupPost(unsigned int policy_id, - float priority, + HttpStatus setupPost(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, const std::string & url, BufferArray * body, HttpOptions * options, diff --git a/indra/llcorehttp/_httpopsetpriority.cpp b/indra/llcorehttp/_httpopsetpriority.cpp index dc609421ed..b0ee577087 100644 --- a/indra/llcorehttp/_httpopsetpriority.cpp +++ b/indra/llcorehttp/_httpopsetpriority.cpp @@ -35,7 +35,7 @@ namespace LLCore { -HttpOpSetPriority::HttpOpSetPriority(HttpHandle handle, unsigned int priority) +HttpOpSetPriority::HttpOpSetPriority(HttpHandle handle, HttpRequest::priority_t priority) : HttpOperation(), mHandle(handle), mPriority(priority) diff --git a/indra/llcorehttp/_httpopsetpriority.h b/indra/llcorehttp/_httpopsetpriority.h index e5d8e5fc1f..b972f50fff 100644 --- a/indra/llcorehttp/_httpopsetpriority.h +++ b/indra/llcorehttp/_httpopsetpriority.h @@ -29,7 +29,7 @@ #include "httpcommon.h" - +#include "httprequest.h" #include "_httpoperation.h" #include "_refcounted.h" @@ -46,7 +46,7 @@ namespace LLCore class HttpOpSetPriority : public HttpOperation { public: - HttpOpSetPriority(HttpHandle handle, unsigned int priority); + HttpOpSetPriority(HttpHandle handle, HttpRequest::priority_t priority); virtual ~HttpOpSetPriority(); private: @@ -59,9 +59,9 @@ public: virtual void visitNotifier(HttpRequest * request); protected: - HttpStatus mStatus; - HttpHandle mHandle; - unsigned int mPriority; + HttpStatus mStatus; + HttpHandle mHandle; + HttpRequest::priority_t mPriority; }; // end class HttpOpSetPriority } // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 873b519c51..1d28f23d56 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -112,7 +112,7 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() } -bool HttpPolicy::changePriority(HttpHandle handle, unsigned int priority) +bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t priority) { for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) { diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index c5e82d0a65..2bc03c531f 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -67,7 +67,7 @@ public: void addOp(HttpOpRequest *); // Shadows HttpService's method - bool changePriority(HttpHandle handle, unsigned int priority); + bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); protected: int mReadyInClass[HttpRequest::POLICY_CLASS_LIMIT]; diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index 48884ca060..337493ca12 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -152,7 +152,7 @@ void HttpService::stopRequested() mExitRequested = true; } -bool HttpService::changePriority(HttpHandle handle, unsigned int priority) +bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t priority) { bool found(false); diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index 3e5a5457d7..095316c8a7 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -29,6 +29,7 @@ #include "httpcommon.h" +#include "httprequest.h" namespace LLCoreInt @@ -145,7 +146,7 @@ public: /// @return True if the request was found somewhere. /// /// Threading: callable by worker thread. - bool changePriority(HttpHandle handle, unsigned int priority); + bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); HttpPolicy * getPolicy() { diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index a06b859a91..0e512d97ed 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -83,7 +83,7 @@ protected: // ==================================== -unsigned int HttpRequest::sNextPolicyID(1); +HttpRequest::policy_t HttpRequest::sNextPolicyID(1); HttpRequest::HttpRequest() @@ -133,15 +133,15 @@ HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value) } -unsigned int HttpRequest::createPolicyClass() +HttpRequest::policy_t HttpRequest::createPolicyClass() { - unsigned int policy_id = 1; + policy_t policy_id = 1; return policy_id; } -HttpStatus HttpRequest::setPolicyClassOption(unsigned int policy_id, +HttpStatus HttpRequest::setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value) { @@ -162,8 +162,8 @@ HttpStatus HttpRequest::getStatus() const } -HttpHandle HttpRequest::requestGetByteRange(unsigned int policy_id, - unsigned int priority, +HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id, + priority_t priority, const std::string & url, size_t offset, size_t len, @@ -191,8 +191,8 @@ HttpHandle HttpRequest::requestGetByteRange(unsigned int policy_id, } -HttpHandle HttpRequest::requestPost(unsigned int policy_id, - unsigned int priority, +HttpHandle HttpRequest::requestPost(policy_t policy_id, + priority_t priority, const std::string & url, BufferArray * body, HttpOptions * options, @@ -251,7 +251,7 @@ HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) } -HttpHandle HttpRequest::requestSetPriority(HttpHandle request, unsigned int priority, +HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priority, HttpHandler * handler) { HttpStatus status; diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index e2ab9be533..57d2da245b 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -90,6 +90,10 @@ private: HttpRequest(const HttpRequest &); // Disallowed void operator=(const HttpRequest &); // Disallowed +public: + typedef unsigned int policy_t; + typedef unsigned int priority_t; + public: /// @name PolicyMethods /// @{ @@ -125,7 +129,7 @@ public: /// the class in other methods. If -1, an error /// occurred and @see getStatus() may provide more /// detail on the reason. - unsigned int createPolicyClass(); + policy_t createPolicyClass(); enum EClassPolicy { @@ -149,7 +153,7 @@ public: /// @param opt Enum of option to be set. /// @param value Desired value of option. /// @return Standard status code. - HttpStatus setPolicyClassOption(unsigned int policy_id, + HttpStatus setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value); @@ -194,8 +198,8 @@ public: /// request could not be queued. In the latter /// case, @see getStatus() will return more info. /// - HttpHandle requestGetByteRange(unsigned int policy_id, - unsigned int priority, + HttpHandle requestGetByteRange(policy_t policy_id, + priority_t priority, const std::string & url, size_t offset, size_t len, @@ -221,8 +225,8 @@ public: /// request could not be queued. In the latter /// case, @see getStatus() will return more info. /// - HttpHandle requestPost(unsigned int policy_id, - unsigned int priority, + HttpHandle requestPost(policy_t policy_id, + priority_t priority, const std::string & url, BufferArray * body, HttpOptions * options, @@ -278,7 +282,7 @@ public: /// queued or LLCORE_HTTP_HANDLE_INVALID if the /// request could not be queued. /// - HttpHandle requestSetPriority(HttpHandle request, unsigned int priority, HttpHandler * handler); + HttpHandle requestSetPriority(HttpHandle request, priority_t priority, HttpHandler * handler); /// @} @@ -345,7 +349,7 @@ private: /// Must be established before any threading is allowed to /// start. /// - static unsigned int sNextPolicyID; + static policy_t sNextPolicyID; /// @} // End Global State -- cgit v1.2.3 From 640798bb9951bc512bcbcffbe136d42372c99322 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 1 Jun 2012 18:18:53 -0400 Subject: Platform fixups: typedef for priority_queue, more specific comparator functor. --- indra/llcorehttp/_httpoperation.h | 13 ------------- indra/llcorehttp/_httpoprequest.h | 13 +++++++++++++ indra/llcorehttp/_httpreadyqueue.h | 12 +++++++----- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index 4d9298d801..01e26029d2 100644 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -159,19 +159,6 @@ public: }; // end class HttpOpNull -/// HttpOpCompare isn't an operation but a uniform comparison -/// functor for STL containers that order by priority. Mainly -/// used for the ready queue container but defined here. -class HttpOpCompare -{ -public: - bool operator()(const HttpOperation * lhs, const HttpOperation * rhs) - { - return lhs->mReqPriority > rhs->mReqPriority; - } -}; // end class HttpOpCompare - - } // end namespace LLCore #endif // _LLCORE_HTTP_OPERATION_H_ diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index e973f6ad26..0cad4e8459 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -131,6 +131,19 @@ public: }; // end class HttpOpRequest +/// HttpOpRequestCompare isn't an operation but a uniform comparison +/// functor for STL containers that order by priority. Mainly +/// used for the ready queue container but defined here. +class HttpOpRequestCompare +{ +public: + bool operator()(const HttpOpRequest * lhs, const HttpOpRequest * rhs) + { + return lhs->mReqPriority > rhs->mReqPriority; + } +}; // end class HttpOpRequestCompare + + } // end namespace LLCore #endif // _LLCORE_HTTP_OPREQUEST_H_ diff --git a/indra/llcorehttp/_httpreadyqueue.h b/indra/llcorehttp/_httpreadyqueue.h index 283e868b4c..2cd96aefe3 100644 --- a/indra/llcorehttp/_httpreadyqueue.h +++ b/indra/llcorehttp/_httpreadyqueue.h @@ -30,7 +30,7 @@ #include -#include "_httpoperation.h" +#include "_httpoprequest.h" namespace LLCore @@ -49,13 +49,15 @@ class HttpOpRequest; /// Threading: not thread-safe. Expected to be used entirely by /// a single thread, typically a worker thread of some sort. -class HttpReadyQueue : public std::priority_queue, - LLCore::HttpOpCompare> +typedef std::priority_queue, + LLCore::HttpOpRequestCompare> HttpReadyQueueBase; + +class HttpReadyQueue : public HttpReadyQueueBase { public: HttpReadyQueue() - : priority_queue() + : HttpReadyQueueBase() {} ~HttpReadyQueue() -- cgit v1.2.3 From fb5a29c069d27611b6328fbc313382ef0914ffe9 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 1 Jun 2012 23:06:24 +0000 Subject: Platform fixups Linux: unused variables, make error strings constant. --- indra/llcommon/llsdserialize.cpp | 5 ++++- indra/llcommon/tests/bitpack_test.cpp | 1 + indra/llcommon/tests/reflection_test.cpp | 2 +- indra/llcorehttp/httpcommon.cpp | 2 +- indra/llmessage/llhttpassetstorage.cpp | 2 +- indra/newview/lltexturefetch.cpp | 10 +++++----- 6 files changed, 13 insertions(+), 9 deletions(-) diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp index bf62600514..f29c96f17c 100644 --- a/indra/llcommon/llsdserialize.cpp +++ b/indra/llcommon/llsdserialize.cpp @@ -1447,9 +1447,12 @@ S32 LLSDBinaryFormatter::format(const LLSD& data, std::ostream& ostr, U32 option } case LLSD::TypeUUID: + { ostr.put('u'); - ostr.write((const char*)(&(data.asUUID().mData)), UUID_BYTES); + LLSD::UUID value = data.asUUID(); + ostr.write((const char*)(&value.mData), UUID_BYTES); break; + } case LLSD::TypeString: ostr.put('s'); diff --git a/indra/llcommon/tests/bitpack_test.cpp b/indra/llcommon/tests/bitpack_test.cpp index 05289881d0..4c3bc674af 100644 --- a/indra/llcommon/tests/bitpack_test.cpp +++ b/indra/llcommon/tests/bitpack_test.cpp @@ -95,6 +95,7 @@ namespace tut ensure("bitPack: individual unpack: 5", unpackbuffer[0] == (U8) str[5]); unpack_bufsize = bitunpack.bitUnpack(unpackbuffer, 8*4); // Life ensure_memory_matches("bitPack: 4 bytes unpack:", unpackbuffer, 4, str+6, 4); + ensure("keep compiler quiet", unpack_bufsize == unpack_bufsize); } // U32 packing diff --git a/indra/llcommon/tests/reflection_test.cpp b/indra/llcommon/tests/reflection_test.cpp index 59491cd1fe..8980ebb1f1 100644 --- a/indra/llcommon/tests/reflection_test.cpp +++ b/indra/llcommon/tests/reflection_test.cpp @@ -207,7 +207,7 @@ namespace tut const LLReflective* reflective = property->get(aggregated_data); // Wrong reflective type, should throw exception. // useless op to get rid of compiler warning. - reflective = NULL; + reflective = reflective; } catch(...) { diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp index 273acae132..a01182cf23 100644 --- a/indra/llcorehttp/httpcommon.cpp +++ b/indra/llcorehttp/httpcommon.cpp @@ -73,7 +73,7 @@ std::string HttpStatus::toString() const static const struct { type_enum_t mCode; - char * mText; + const char * mText; } http_errors[] = { diff --git a/indra/llmessage/llhttpassetstorage.cpp b/indra/llmessage/llhttpassetstorage.cpp index 612d765969..d6ed08055e 100644 --- a/indra/llmessage/llhttpassetstorage.cpp +++ b/indra/llmessage/llhttpassetstorage.cpp @@ -749,7 +749,7 @@ LLAssetRequest* LLHTTPAssetStorage::findNextRequest(LLAssetStorage::request_list request_list_t::iterator pending_iter = pending.begin(); request_list_t::iterator pending_end = pending.end(); // Loop over all pending requests until we miss finding it in the running list. - for (; pending_iter != pending.end(); ++pending_iter) + for (; pending_iter != pending_end; ++pending_iter) { LLAssetRequest* req = *pending_iter; // Look for this pending request in the running list. diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 381364b5c3..daad463e0d 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1065,13 +1065,13 @@ bool LLTextureFetchWorker::doWork(S32 param) //1, not openning too many file descriptors at the same time; //2, control the traffic of http so udp gets bandwidth. // - static const S32 MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE = 8; + // static const S32 MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE = 8; // *FIXME: For the moment, allow everything to transition into HTTP // and have the new library order and throttle. - //if(mFetcher->getNumHTTPRequests() > MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE) - //{ - //return false ; //wait. - //} + // if(mFetcher->getNumHTTPRequests() > MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE) + // { + // return false ; //wait. + // } mFetcher->removeFromNetworkQueue(this, false); -- cgit v1.2.3 From fe5c1683f0e13e8a3f0523095c1c7e3a3fd17cf3 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 1 Jun 2012 20:49:00 -0400 Subject: Another float/int issue and move the POST priority in line with what normal requests do... --- indra/newview/lltexturefetch.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index daad463e0d..97d7ec5531 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1117,7 +1117,7 @@ bool LLTextureFetchWorker::doWork(S32 param) // Will call callbackHttpGet when curl request completes // *FIXME: enable redirection follow mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass, - mRequestedPriority, + mWorkPriority, mUrl, mRequestedOffset, mRequestedSize, @@ -2976,7 +2976,7 @@ TFReqSendMetrics::~TFReqSendMetrics() bool TFReqSendMetrics::doWork(LLTextureFetch * fetcher) { - static const U32 report_priority(LLWorkerThread::PRIORITY_LOW); + static const U32 report_priority(1); static const int report_policy_class(LLCore::HttpRequest::DEFAULT_POLICY_ID); static LLCore::HttpHandler * const handler(fetcher->isQAMode() || true ? &stats_handler : NULL); -- cgit v1.2.3 From 4155301015525a242a79b9b3134e66d366bc0ebd Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 1 Jun 2012 21:30:45 -0400 Subject: Do some work on BufferArray to make it a bit less naive about chunking data. Remove the stateful use of a seek pointer so that shared read is possible (though maybe not interesting). --- indra/llcorehttp/_httpoprequest.cpp | 14 ++-- indra/llcorehttp/bufferarray.cpp | 126 +++++++++++++++++++--------- indra/llcorehttp/bufferarray.h | 53 ++---------- indra/llcorehttp/tests/test_bufferarray.hpp | 89 ++++++++------------ indra/newview/lltexturefetch.cpp | 3 +- 5 files changed, 133 insertions(+), 152 deletions(-) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 791ee5f860..196011f953 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -340,7 +340,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) long data_size(0); if (mReqBody) { - mReqBody->seek(0); data_size = mReqBody->size(); } curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, static_cast(NULL)); @@ -356,7 +355,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) long data_size(0); if (mReqBody) { - mReqBody->seek(0); data_size = mReqBody->size(); } curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size); @@ -423,10 +421,8 @@ size_t HttpOpRequest::writeCallback(void * data, size_t size, size_t nmemb, void op->mReplyBody = new BufferArray(); } const size_t req_size(size * nmemb); - char * lump(op->mReplyBody->appendBufferAlloc(req_size)); - memcpy(lump, data, req_size); - - return req_size; + const size_t write_size(op->mReplyBody->append(static_cast(data), req_size)); + return write_size; } @@ -450,9 +446,9 @@ size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void } const size_t do_size((std::min)(req_size, body_size - op->mCurlBodyPos)); - op->mReqBody->read(static_cast(data), do_size); - op->mCurlBodyPos += do_size; - return do_size; + const size_t read_size(op->mReqBody->read(op->mCurlBodyPos, static_cast(data), do_size)); + op->mCurlBodyPos += read_size; + return read_size; } diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index 4c20350b13..c13b1a3540 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -27,6 +27,18 @@ #include "bufferarray.h" +// BufferArray is a list of chunks, each a BufferArray::Block, of contiguous +// data presented as a single array. Chunks are at least BufferArray::BLOCK_ALLOC_SIZE +// in length and can be larger. Any chunk may be partially filled or even +// empty. +// +// The BufferArray itself is sharable as a RefCounted entity. As shared +// reads don't work with the concept of a current position/seek value, +// none is kept with the object. Instead, the read and write operations +// all take position arguments. Single write/shared read isn't supported +// directly and any such attempts have to be serialized outside of this +// implementation. + namespace LLCore { @@ -58,7 +70,8 @@ public: static Block * alloc(size_t len); public: - size_t mLen; + size_t mUsed; + size_t mAlloced; // *NOTE: Must be last member of the object. We'll // overallocate as requested via operator new and index @@ -74,7 +87,6 @@ public: BufferArray::BufferArray() : LLCoreInt::RefCounted(true), - mPos(0), mLen(0) {} @@ -94,19 +106,45 @@ BufferArray::~BufferArray() size_t BufferArray::append(const char * src, size_t len) { - if (len) + const size_t ret(len); + + // First, try to copy into the last block + if (len && ! mBlocks.empty()) + { + Block & last(*mBlocks.back()); + if (last.mUsed < last.mAlloced) + { + // Some will fit... + const size_t copy_len((std::min)(len, (last.mAlloced - last.mUsed))); + + memcpy(&last.mData[last.mUsed], src, copy_len); + last.mUsed += copy_len; + llassert_always(last.mUsed <= last.mAlloced); + mLen += copy_len; + src += copy_len; + len -= copy_len; + } + } + + // Then get new blocks as needed + while (len) { + const size_t copy_len((std::min)(len, BLOCK_ALLOC_SIZE)); + if (mBlocks.size() >= mBlocks.capacity()) { mBlocks.reserve(mBlocks.size() + 5); } - Block * block = Block::alloc(len); - memcpy(block->mData, src, len); + Block * block = Block::alloc(BLOCK_ALLOC_SIZE); + memcpy(block->mData, src, copy_len); + block->mUsed = copy_len; + llassert_always(block->mUsed <= block->mAlloced); mBlocks.push_back(block); - mLen += len; - mPos = mLen; + mLen += copy_len; + src += copy_len; + len -= copy_len; } - return len; + return ret; } @@ -117,41 +155,33 @@ char * BufferArray::appendBufferAlloc(size_t len) { mBlocks.reserve(mBlocks.size() + 5); } - Block * block = Block::alloc(len); + Block * block = Block::alloc((std::max)(BLOCK_ALLOC_SIZE, len)); + block->mUsed = len; mBlocks.push_back(block); mLen += len; - mPos = mLen; return block->mData; } -size_t BufferArray::seek(size_t pos) +size_t BufferArray::read(size_t pos, char * dst, size_t len) { - if (pos > mLen) - pos = mLen; - mPos = pos; - return mPos; -} - - -size_t BufferArray::read(char * dst, size_t len) -{ - size_t result(0), offset(0); - size_t len_limit(mLen - mPos); + if (pos >= mLen) + return 0; + size_t len_limit(mLen - pos); len = std::min(len, len_limit); - - if (mPos >= mLen || 0 == len) + if (0 == len) return 0; + size_t result(0), offset(0); const int block_limit(mBlocks.size()); - int block_start(findBlock(mPos, &offset)); + int block_start(findBlock(pos, &offset)); if (block_start < 0) return 0; do { Block & block(*mBlocks[block_start]); - size_t block_limit(block.mLen - offset); + size_t block_limit(block.mUsed - offset); size_t block_len(std::min(block_limit, len)); memcpy(dst, &block.mData[offset], block_len); @@ -163,19 +193,18 @@ size_t BufferArray::read(char * dst, size_t len) } while (len && block_start < block_limit); - mPos += result; return result; } -size_t BufferArray::write(const char * src, size_t len) +size_t BufferArray::write(size_t pos, const char * src, size_t len) { - size_t result(0), offset(0); - if (mPos > mLen || 0 == len) + if (pos > mLen || 0 == len) return 0; + size_t result(0), offset(0); const int block_limit(mBlocks.size()); - int block_start(findBlock(mPos, &offset)); + int block_start(findBlock(pos, &offset)); if (block_start >= 0) { @@ -184,20 +213,39 @@ size_t BufferArray::write(const char * src, size_t len) do { Block & block(*mBlocks[block_start]); - size_t block_limit(block.mLen - offset); + size_t block_limit(block.mUsed - offset); size_t block_len(std::min(block_limit, len)); memcpy(&block.mData[offset], src, block_len); result += block_len; - len -= block_len; src += block_len; + len -= block_len; offset = 0; ++block_start; } while (len && block_start < block_limit); } - mPos += result; + // Something left, see if it will fit in the free + // space of the last block. + if (len && ! mBlocks.empty()) + { + Block & last(*mBlocks.back()); + if (last.mUsed < last.mAlloced) + { + // Some will fit... + const size_t copy_len((std::min)(len, (last.mAlloced - last.mUsed))); + + memcpy(&last.mData[last.mUsed], src, copy_len); + last.mUsed += copy_len; + result += copy_len; + llassert_always(last.mUsed <= last.mAlloced); + mLen += copy_len; + src += copy_len; + len -= copy_len; + } + } + if (len) { // Some or all of the remaining write data will @@ -218,12 +266,12 @@ int BufferArray::findBlock(size_t pos, size_t * ret_offset) const int block_limit(mBlocks.size()); for (int i(0); i < block_limit; ++i) { - if (pos < mBlocks[i]->mLen) + if (pos < mBlocks[i]->mUsed) { *ret_offset = pos; return i; } - pos -= mBlocks[i]->mLen; + pos -= mBlocks[i]->mUsed; } // Shouldn't get here but... @@ -237,7 +285,8 @@ int BufferArray::findBlock(size_t pos, size_t * ret_offset) BufferArray::Block::Block(size_t len) - : mLen(len) + : mUsed(0), + mAlloced(len) { memset(mData, 0, len); } @@ -245,7 +294,8 @@ BufferArray::Block::Block(size_t len) BufferArray::Block::~Block() { - mLen = 0; + mUsed = 0; + mAlloced = 0; } diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h index b26ad1b297..9ccd85d4f8 100644 --- a/indra/llcorehttp/bufferarray.h +++ b/indra/llcorehttp/bufferarray.h @@ -73,6 +73,9 @@ private: void operator=(const BufferArray &); // Not defined public: + // Internal magic number, may be used by unit tests. + static const size_t BLOCK_ALLOC_SIZE = 1504; + /// Appends the indicated data to the BufferArray /// modifying current position and total size. New /// position is one beyond the final byte of the buffer. @@ -96,24 +99,16 @@ public: return mLen; } - /// Set the current position for subsequent read and - /// write operations. 'pos' values before the beginning - /// or greater than the size of the buffer are coerced - /// to a value within the buffer. - /// - /// @return Actual current position after seek. - size_t seek(size_t pos); - - /// Copies data from the current position in the instance + /// Copies data from the given position in the instance /// to the caller's buffer. Will return a short count of /// bytes copied if the 'len' extends beyond the data. - size_t read(char * dst, size_t len); + size_t read(size_t pos, char * dst, size_t len); /// Copies data from the caller's buffer to the instance /// at the current position. May overwrite existing data, /// append data when current position is equal to the /// size of the instance or do a mix of both. - size_t write(const char * src, size_t len); + size_t write(size_t pos, const char * src, size_t len); protected: int findBlock(size_t pos, size_t * ret_offset); @@ -123,46 +118,10 @@ protected: typedef std::vector container_t; container_t mBlocks; - size_t mPos; size_t mLen; }; // end class BufferArray -#if 0 - -// Conceptual for now. Another possibility is going with -// something like Boost::asio's buffers interface. They're -// trying to achieve the same thing above and below.... - -class BufferStream : public std::streambuf -{ -public: - BufferStream(BufferArray * buffer); - virtual ~BufferStream(); - -private: - BufferStream(const BufferStream &); // Not defined - void operator=(const BufferStream &); // Not defined - -public: - // Types - typedef std::streambuf::pos_type pos_type; - typedef std::streambuf::off_type off_type; - - virtual int underflow(); - - virtual int overflow(int c); - - virtual int sync(); - - virtual pos_type seekoff(off_type off, std::ios::seekdir way, std::ios::openmode which); - -protected: - BufferArray * mBufferArray; -}; // end class BufferStream - -#endif // 0 - } // end namespace LLCore #endif // _LLCORE_BUFFER_ARRAY_H_ diff --git a/indra/llcorehttp/tests/test_bufferarray.hpp b/indra/llcorehttp/tests/test_bufferarray.hpp index ecbb5ef250..2ad9391d1c 100644 --- a/indra/llcorehttp/tests/test_bufferarray.hpp +++ b/indra/llcorehttp/tests/test_bufferarray.hpp @@ -67,7 +67,7 @@ void BufferArrayTestObjectType::test<1>() // Try to read char buffer[20]; - size_t read_len(ba->read(buffer, sizeof(buffer))); + size_t read_len(ba->read(0, buffer, sizeof(buffer))); ensure("Read returns empty", 0 == read_len); // release the implicit reference, causing the object to be released @@ -92,16 +92,13 @@ void BufferArrayTestObjectType::test<2>() char str1[] = "abcdefghij"; char buffer[256]; - size_t len = ba->write(str1, strlen(str1)); + size_t len = ba->write(0, str1, strlen(str1)); ensure("Wrote length correct", strlen(str1) == len); ensure("Recorded size correct", strlen(str1) == ba->size()); // read some data back - len = ba->seek(2); - ensure("Seek worked", 2 == len); - memset(buffer, 'X', sizeof(buffer)); - len = ba->read(buffer, 2); + len = ba->read(2, buffer, 2); ensure("Read length correct", 2 == len); ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]); ensure("Read didn't overwrite", 'X' == buffer[2]); @@ -130,32 +127,26 @@ void BufferArrayTestObjectType::test<3>() size_t str1_len(strlen(str1)); char buffer[256]; - size_t len = ba->write(str1, str1_len); + size_t len = ba->write(0, str1, str1_len); ensure("Wrote length correct", str1_len == len); ensure("Recorded size correct", str1_len == ba->size()); // again... - len = ba->write(str1, strlen(str1)); + len = ba->write(str1_len, str1, strlen(str1)); ensure("Wrote length correct", str1_len == len); ensure("Recorded size correct", (2 * str1_len) == ba->size()); // read some data back - len = ba->seek(8); - ensure("Seek worked", 8 == len); - memset(buffer, 'X', sizeof(buffer)); - len = ba->read(buffer, 4); + len = ba->read(8, buffer, 4); ensure("Read length correct", 4 == len); ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]); ensure("Read content correct", 'a' == buffer[2] && 'b' == buffer[3]); ensure("Read didn't overwrite", 'X' == buffer[4]); // Read whole thing - len = ba->seek(0); - ensure("Seek worked (2)", 0 == len); - memset(buffer, 'X', sizeof(buffer)); - len = ba->read(buffer, sizeof(buffer)); + len = ba->read(0, buffer, sizeof(buffer)); ensure("Read length correct", (2 * str1_len) == len); ensure("Read content correct (3)", 0 == strncmp(buffer, str1, str1_len)); ensure("Read content correct (4)", 0 == strncmp(&buffer[str1_len], str1, str1_len)); @@ -185,33 +176,29 @@ void BufferArrayTestObjectType::test<4>() char str2[] = "ABCDEFGHIJ"; char buffer[256]; - size_t len = ba->write(str1, str1_len); + size_t len = ba->write(0, str1, str1_len); ensure("Wrote length correct", str1_len == len); ensure("Recorded size correct", str1_len == ba->size()); // again... - len = ba->write(str1, strlen(str1)); + len = ba->write(str1_len, str1, strlen(str1)); ensure("Wrote length correct", str1_len == len); ensure("Recorded size correct", (2 * str1_len) == ba->size()); // reposition and overwrite - len = ba->seek(8); - ensure("Seek worked", 8 == len); - len = ba->write(str2, 4); + len = ba->write(8, str2, 4); ensure("Overwrite length correct", 4 == len); - // Leave position and read verifying content + // Leave position and read verifying content (stale really from seek() days) memset(buffer, 'X', sizeof(buffer)); - len = ba->read(buffer, 4); + len = ba->read(12, buffer, 4); ensure("Read length correct", 4 == len); ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]); ensure("Read content correct.2", 'e' == buffer[2] && 'f' == buffer[3]); ensure("Read didn't overwrite", 'X' == buffer[4]); // reposition and check - len = ba->seek(6); - memset(buffer, 'X', sizeof(buffer)); - len = ba->read(buffer, 8); + len = ba->read(6, buffer, 8); ensure("Read length correct.2", 8 == len); ensure("Read content correct.3", 'g' == buffer[0] && 'h' == buffer[1]); ensure("Read content correct.4", 'A' == buffer[2] && 'B' == buffer[3]); @@ -242,21 +229,18 @@ void BufferArrayTestObjectType::test<5>() size_t str1_len(strlen(str1)); char buffer[256]; - size_t len = ba->write(str1, str1_len); + size_t len = ba->write(0, str1, str1_len); ensure("Wrote length correct", str1_len == len); ensure("Recorded size correct", str1_len == ba->size()); // again... - len = ba->write(str1, strlen(str1)); + len = ba->write(str1_len, str1, str1_len); ensure("Wrote length correct", str1_len == len); ensure("Recorded size correct", (2 * str1_len) == ba->size()); // read some data back - len = ba->seek(8); - ensure("Seek worked", 8 == len); - memset(buffer, 'X', sizeof(buffer)); - len = ba->read(buffer, 4); + len = ba->read(8, buffer, 4); ensure("Read length correct", 4 == len); ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]); ensure("Read content correct.2", 'a' == buffer[2] && 'b' == buffer[3]); @@ -264,7 +248,7 @@ void BufferArrayTestObjectType::test<5>() // Read some more without repositioning memset(buffer, 'X', sizeof(buffer)); - len = ba->read(buffer, sizeof(buffer)); + len = ba->read(12, buffer, sizeof(buffer)); ensure("Read length correct", (str1_len - 2) == len); ensure("Read content correct.3", 0 == strncmp(buffer, str1+2, str1_len-2)); ensure("Read didn't overwrite.2", 'X' == buffer[str1_len-1]); @@ -294,31 +278,27 @@ void BufferArrayTestObjectType::test<6>() size_t str2_len(strlen(str2)); char buffer[256]; - size_t len = ba->write(str1, str1_len); + size_t len = ba->write(0, str1, str1_len); ensure("Wrote length correct", str1_len == len); ensure("Recorded size correct", str1_len == ba->size()); // again... - len = ba->write(str1, strlen(str1)); + len = ba->write(str1_len, str1, strlen(str1)); ensure("Wrote length correct", str1_len == len); ensure("Recorded size correct", (2 * str1_len) == ba->size()); // reposition and overwrite - len = ba->seek(8); - ensure("Seek worked", 8 == len); - len = ba->write(str2, str2_len); + len = ba->write(8, str2, str2_len); ensure("Overwrite length correct", str2_len == len); // Leave position and read verifying content memset(buffer, 'X', sizeof(buffer)); - len = ba->read(buffer, 0); + len = ba->read(8 + str2_len, buffer, 0); ensure("Read length correct", 0 == len); ensure("Read didn't overwrite", 'X' == buffer[0]); // reposition and check - len = ba->seek(0); - memset(buffer, 'X', sizeof(buffer)); - len = ba->read(buffer, sizeof(buffer)); + len = ba->read(0, buffer, sizeof(buffer)); ensure("Read length correct.2", (str1_len + str2_len - 2) == len); ensure("Read content correct", 0 == strncmp(buffer, str1, str1_len-2)); ensure("Read content correct.2", 0 == strncmp(buffer+str1_len-2, str2, str2_len)); @@ -350,18 +330,17 @@ void BufferArrayTestObjectType::test<7>() char buffer[256]; // 2x str1 - size_t len = ba->write(str1, str1_len); - len = ba->write(str1, strlen(str1)); + size_t len = ba->write(0, str1, str1_len); + len = ba->write(str1_len, str1, str1_len); // reposition and overwrite - len = ba->seek(6); - len = ba->write(str2, 2); + len = ba->write(6, str2, 2); ensure("Overwrite length correct", 2 == len); - len = ba->write(str2, 2); + len = ba->write(8, str2, 2); ensure("Overwrite length correct.2", 2 == len); - len = ba->write(str2, 2); + len = ba->write(10, str2, 2); ensure("Overwrite length correct.3", 2 == len); // append some data @@ -373,13 +352,12 @@ void BufferArrayTestObjectType::test<7>() memcpy(out_buf, str1, str1_len); // And some final writes - len = ba->write(str2, 2); + len = ba->write(3 * str1_len + str2_len, str2, 2); ensure("Write length correct.2", 2 == len); // Check contents memset(buffer, 'X', sizeof(buffer)); - ba->seek(0); - len = ba->read(buffer, sizeof(buffer)); + len = ba->read(0, buffer, sizeof(buffer)); ensure("Final buffer length correct", (3 * str1_len + str2_len + 2) == len); ensure("Read content correct", 0 == strncmp(buffer, str1, 6)); ensure("Read content correct.2", 0 == strncmp(buffer + 6, str2, 2)); @@ -417,8 +395,8 @@ void BufferArrayTestObjectType::test<8>() char buffer[256]; // 2x str1 - size_t len = ba->write(str1, str1_len); - len = ba->write(str1, strlen(str1)); + size_t len = ba->write(0, str1, str1_len); + len = ba->write(str1_len, str1, str1_len); // zero-length allocate (we allow this with a valid pointer returned) char * out_buf(ba->appendBufferAlloc(0)); @@ -430,12 +408,11 @@ void BufferArrayTestObjectType::test<8>() ensure("Two zero-length appendBufferAlloc buffers distinct", out_buf != out_buf2); // And some final writes - len = ba->write(str2, str2_len); + len = ba->write(2 * str1_len, str2, str2_len); // Check contents memset(buffer, 'X', sizeof(buffer)); - ba->seek(0); - len = ba->read(buffer, sizeof(buffer)); + len = ba->read(0, buffer, sizeof(buffer)); ensure("Final buffer length correct", (2 * str1_len + str2_len) == len); ensure("Read content correct.1", 0 == strncmp(buffer, str1, str1_len)); ensure("Read content correct.2", 0 == strncmp(buffer + str1_len, str1, str1_len)); diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index daad463e0d..e27f0aec1e 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1255,8 +1255,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { memcpy(buffer, mFormattedImage->getData(), cur_size); } - mHttpBufferArray->seek(0); - mHttpBufferArray->read((char *) buffer + cur_size, append_size); + mHttpBufferArray->read(0, (char *) buffer + cur_size, append_size); // NOTE: setData releases current data and owns new data (buffer) mFormattedImage->setData(buffer, total_size); -- cgit v1.2.3 From c7d5b0dced7f17f77fa00c816286cdbd7a0e9d4b Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Sat, 2 Jun 2012 02:09:33 +0000 Subject: More platform fixes for linux/mac. --- indra/llcorehttp/bufferarray.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index c13b1a3540..47389da0be 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -85,6 +85,8 @@ public: // ================================== +const size_t BufferArray::BLOCK_ALLOC_SIZE; + BufferArray::BufferArray() : LLCoreInt::RefCounted(true), mLen(0) -- cgit v1.2.3 From 3a9c182d7fe3fdacf5923e3b1b51456472e60bb1 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Sat, 2 Jun 2012 02:22:07 +0000 Subject: Stupid windows tricks. --- indra/llcorehttp/bufferarray.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index 47389da0be..36a8006bce 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -85,7 +85,9 @@ public: // ================================== +#if ! defined(WIN32) const size_t BufferArray::BLOCK_ALLOC_SIZE; +#endif // ! defined(WIN32) BufferArray::BufferArray() : LLCoreInt::RefCounted(true), -- cgit v1.2.3 From 9a11a2946f4dec334ce1ac449b355ba16eaae23a Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 5 Jun 2012 12:06:42 -0400 Subject: Faster spin in worker thread when doing I/O and a priority bump needed when fixing priorities. --- indra/llcorehttp/_httpservice.cpp | 6 +++++- indra/newview/lltexturefetch.cpp | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index 337493ca12..b038bdb720 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -38,6 +38,10 @@ #include "lltimer.h" +// Tuning parameters +static const int LOOP_SLEEP_NORMAL_MS = 10; // Normal per-loop sleep in milliseconds + + namespace LLCore { @@ -198,7 +202,7 @@ void HttpService::threadRun(LLCoreInt::HttpThread * thread) // Determine whether to spin, sleep briefly or sleep for next request if (REQUEST_SLEEP != loop) { - ms_sleep(50); + ms_sleep(LOOP_SLEEP_NORMAL_MS); } } diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 08f3ca1583..34fb21798f 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -736,7 +736,7 @@ void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size) if ((prioritize && mState == INIT) || mState == DONE) { mState = INIT; - U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_LOW; + U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH; setPriority(work_priority); } } -- cgit v1.2.3 From 6c6d1c8338b15828278d27912bb9fe3b0d133b12 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 5 Jun 2012 16:49:39 -0400 Subject: Format/data type mismatch lead to a 'Range: bytes=0-0' header which gave me 1 byte of data. Shouldn't be making that kind of mistake. --- indra/llcorehttp/_httpoprequest.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 196011f953..f52ff5a44c 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -376,19 +376,19 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) if ((mReqOffset || mReqLength) && HOR_GET == mReqMethod) { - static const char * fmt1("Range: bytes=%d-%d"); - static const char * fmt2("Range: bytes=%d-"); + static const char * const fmt1("Range: bytes=%lu-%lu"); + static const char * const fmt2("Range: bytes=%lu-"); char range_line[64]; #if defined(WIN32) _snprintf_s(range_line, sizeof(range_line), sizeof(range_line) - 1, (mReqLength ? fmt1 : fmt2), - mReqOffset, mReqOffset + mReqLength - 1); + (unsigned long) mReqOffset, (unsigned long) (mReqOffset + mReqLength - 1)); #else snprintf(range_line, sizeof(range_line), (mReqLength ? fmt1 : fmt2), - mReqOffset, mReqOffset + mReqLength - 1); + (unsigned long) mReqOffset, (unsigned long) (mReqOffset + mReqLength - 1)); #endif // defined(WIN32) range_line[sizeof(range_line) - 1] = '\0'; mCurlHeaders = curl_slist_append(mCurlHeaders, range_line); -- cgit v1.2.3 From 05af16a23abe37210e0b880aa27387d8994419dd Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 6 Jun 2012 13:52:38 -0400 Subject: Policy + caching fixes + https support + POST working Implemented first global policy definitions to support SSL CA certificate configuration to support https: operations. Fixed HTTP 206 status handling to match what is currently being done by grid services and to lay a foundation for fixes that will be a response to ER-1824. More libcurl CURLOPT options set on easy handles to do peer verification in the traditional way. HTTP POST working and now reporting asset metrics back to grid for the viewer's asset system. This uses LLSD so that is also showing as compatible with the new library. --- indra/llcorehttp/CMakeLists.txt | 2 ++ indra/llcorehttp/_httplibcurl.cpp | 1 + indra/llcorehttp/_httpoprequest.cpp | 45 ++++++++++++++++++++++++++++++++----- indra/llcorehttp/_httppolicy.cpp | 4 ++-- indra/llcorehttp/_httppolicy.h | 10 +++++++++ indra/llcorehttp/_httpservice.h | 8 +++---- indra/llcorehttp/httpcommon.cpp | 5 ++++- indra/llcorehttp/httpcommon.h | 11 ++++++++- indra/llcorehttp/httprequest.cpp | 13 +++++++++-- indra/llcorehttp/httprequest.h | 16 +++++++------ indra/newview/llappviewer.cpp | 12 +++++++++- indra/newview/lltexturefetch.cpp | 37 +++++++++++++++++------------- 12 files changed, 126 insertions(+), 38 deletions(-) diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 9a073eb850..3fda524ddf 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -33,6 +33,7 @@ set(llcorehttp_SOURCE_FILES _httpoprequest.cpp _httpopsetpriority.cpp _httppolicy.cpp + _httppolicyglobal.cpp _httpreplyqueue.cpp _httprequestqueue.cpp _httpservice.cpp @@ -55,6 +56,7 @@ set(llcorehttp_HEADER_FILES _httpoprequest.h _httpopsetpriority.h _httppolicy.h + _httppolicyglobal.h _httpreadyqueue.h _httpreplyqueue.h _httprequestqueue.h diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 704f9baac9..5272c391e8 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -27,6 +27,7 @@ #include "_httplibcurl.h" #include "httpheaders.h" +#include "bufferarray.h" #include "_httpoprequest.h" diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index f52ff5a44c..4bdc4a5257 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -40,8 +40,10 @@ #include "_httpreplyqueue.h" #include "_httpservice.h" #include "_httppolicy.h" +#include "_httppolicyglobal.h" #include "_httplibcurl.h" +#include "llhttpstatuscodes.h" namespace { @@ -153,14 +155,14 @@ HttpOpRequest::~HttpOpRequest() void HttpOpRequest::stageFromRequest(HttpService * service) { addRef(); - service->getPolicy()->addOp(this); // transfers refcount + service->getPolicy().addOp(this); // transfers refcount } void HttpOpRequest::stageFromReady(HttpService * service) { addRef(); - service->getTransport()->addOp(this); // transfers refcount + service->getTransport().addOp(this); // transfers refcount } @@ -195,6 +197,8 @@ void HttpOpRequest::stageFromActive(HttpService * service) void HttpOpRequest::visitNotifier(HttpRequest * request) { + static const HttpStatus partial_content(HTTP_PARTIAL_CONTENT, HE_SUCCESS); + if (mLibraryHandler) { HttpResponse * response = new HttpResponse(); @@ -208,9 +212,15 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) offset = mReplyOffset; length = mReplyLength; } - else if (mReplyBody) + else if (mReplyBody && partial_content == mStatus) { - // Provide implicit offset/length from request/response + // Legacy grid services did not provide a 'Content-Range' + // header in responses to full- or partly-satisfyiable + // 'Range' requests. For these, we have to hope that + // the data starts where requested and the length is simply + // whatever we received. A bit of sanity could be provided + // by overlapping ranged requests and verifying that the + // overlap matches. offset = mReqOffset; length = mReplyBody->size(); } @@ -306,6 +316,9 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) // *FIXME: better error handling later HttpStatus status; + // Get policy options + HttpPolicyGlobal & policy(service->getPolicy().getGlobalOptions()); + mCurlHandle = curl_easy_init(); // curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1); curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, 30); @@ -322,21 +335,40 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1); curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, 10); + curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, 10); // *FIXME: parameterize this later curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback); curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, mCurlHandle); curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback); curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, mCurlHandle); + curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0); + std::string opt_value; + if (policy.get(HttpRequest::GP_CA_PATH, opt_value)) + { + curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value.c_str()); + } + if (policy.get(HttpRequest::GP_CA_FILE, opt_value)) + { + curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value.c_str()); + } + if (policy.get(HttpRequest::GP_HTTP_PROXY, opt_value)) + { + curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value.c_str()); + } + switch (mReqMethod) { case HOR_GET: curl_easy_setopt(mCurlHandle, CURLOPT_HTTPGET, 1); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); break; case HOR_POST: { curl_easy_setopt(mCurlHandle, CURLOPT_POST, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); long data_size(0); if (mReqBody) { @@ -358,8 +390,11 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) data_size = mReqBody->size(); } curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size); + curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL); mCurlHeaders = curl_slist_append(mCurlHeaders, "Transfer-Encoding: chunked"); mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); } break; diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 1d28f23d56..51f5e487dc 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -76,12 +76,12 @@ void HttpPolicy::addOp(HttpOpRequest * op) HttpService::ELoopSpeed HttpPolicy::processReadyQueue() { HttpService::ELoopSpeed result(HttpService::REQUEST_SLEEP); - HttpLibcurl * pTransport(mService->getTransport()); + HttpLibcurl & transport(mService->getTransport()); for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) { HttpReadyQueue & readyq(mReadyQueue[policy_class]); - int active(pTransport->getActiveCountInClass(policy_class)); + int active(transport.getActiveCountInClass(policy_class)); int needed(8 - active); if (needed > 0 && mReadyInClass[policy_class] > 0) diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 2bc03c531f..425079ec63 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -31,6 +31,7 @@ #include "httprequest.h" #include "_httpservice.h" #include "_httpreadyqueue.h" +#include "_httppolicyglobal.h" namespace LLCore @@ -68,11 +69,20 @@ public: // Shadows HttpService's method bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); + + // Get pointer to global policy options. Caller is expected + // to do context checks like no setting once running. + HttpPolicyGlobal & getGlobalOptions() + { + return mGlobalOptions; + } + protected: int mReadyInClass[HttpRequest::POLICY_CLASS_LIMIT]; HttpReadyQueue mReadyQueue[HttpRequest::POLICY_CLASS_LIMIT]; HttpService * mService; // Naked pointer, not refcounted, not owner + HttpPolicyGlobal mGlobalOptions; }; // end class HttpPolicy diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index 095316c8a7..748354a8e4 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -148,14 +148,14 @@ public: /// Threading: callable by worker thread. bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); - HttpPolicy * getPolicy() + HttpPolicy & getPolicy() { - return mPolicy; + return *mPolicy; } - HttpLibcurl * getTransport() + HttpLibcurl & getTransport() { - return mTransport; + return *mTransport; } protected: diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp index a01182cf23..9f17b5c842 100644 --- a/indra/llcorehttp/httpcommon.cpp +++ b/indra/llcorehttp/httpcommon.cpp @@ -66,7 +66,10 @@ std::string HttpStatus::toString() const "Services shutting down", "Operation canceled", "Invalid Content-Range header encountered", - "Request handle not found" + "Request handle not found", + "Invalid datatype for argument or option", + "Option has not been explicitly set", + "Option is not dynamic and must be set early" }; static const int llcore_errors_count(sizeof(llcore_errors) / sizeof(llcore_errors[0])); diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index c01a5f85d3..fd2661b700 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -137,7 +137,16 @@ enum HttpError HE_INV_CONTENT_RANGE_HDR = 4, // Request handle not found - HE_HANDLE_NOT_FOUND = 5 + HE_HANDLE_NOT_FOUND = 5, + + // Invalid datatype for option/setting + HE_INVALID_ARG = 6, + + // Option hasn't been explicitly set + HE_OPT_NOT_SET = 7, + + // Option not dynamic, must be set during init phase + HE_OPT_NOT_DYNAMIC = 8 }; // end enum HttpError diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 0e512d97ed..baa0fe1a84 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -29,6 +29,7 @@ #include "_httprequestqueue.h" #include "_httpreplyqueue.h" #include "_httpservice.h" +#include "_httppolicy.h" #include "_httpoperation.h" #include "_httpoprequest.h" #include "_httpopsetpriority.h" @@ -127,9 +128,17 @@ HttpRequest::~HttpRequest() HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value) { - HttpStatus status; + // *FIXME: Fail if thread is running. - return status; + return HttpService::instanceOf()->getPolicy().getGlobalOptions().set(opt, value); +} + + +HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value) +{ + // *FIXME: Fail if thread is running. + + return HttpService::instanceOf()->getPolicy().getGlobalOptions().set(opt, value); } diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 57d2da245b..3592d5c6a3 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -111,7 +111,10 @@ public: /// Maximum number of connections the library will use to /// perform operations. This is somewhat soft as the underlying /// transport will cache some connections (up to 5). - GLOBAL_CONNECTION_LIMIT + GP_CONNECTION_LIMIT, ///< Takes long giving number of connections + GP_CA_PATH, ///< System path/directory where SSL certs are stored. + GP_CA_FILE, ///< System path/file containing certs. + GP_HTTP_PROXY ///< String giving host/port to use for HTTP proxy }; /// Set a parameter on a global policy option. Calls @@ -122,6 +125,7 @@ public: /// @param value Desired value of option. /// @return Standard status code. HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value); + HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value); /// Create a new policy class into which requests can be made. /// @@ -134,15 +138,15 @@ public: enum EClassPolicy { /// Limits the number of connections used for the class. - CLASS_CONNECTION_LIMIT, + CP_CONNECTION_LIMIT, /// Limits the number of connections used for a single /// literal address/port pair within the class. - PER_HOST_CONNECTION_LIMIT, + CP_PER_HOST_CONNECTION_LIMIT, /// Suitable requests are allowed to pipeline on their /// connections when they ask for it. - ENABLE_PIPELINING + CP_ENABLE_PIPELINING }; /// Set a parameter on a class-based policy option. Calls @@ -153,9 +157,7 @@ public: /// @param opt Enum of option to be set. /// @param value Desired value of option. /// @return Standard status code. - HttpStatus setPolicyClassOption(policy_t policy_id, - EClassPolicy opt, - long value); + HttpStatus setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value); /// @} diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 8e6deb9cce..7a44415fba 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5356,6 +5356,17 @@ void CoreHttp::init() << LL_ENDL; } + mRequest = new LLCore::HttpRequest; + + status = mRequest->setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE, + gDirUtilp->getCAFile()); + if (! status) + { + LL_ERRS("Init") << "Failed to set CA File for HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } + status = LLCore::HttpRequest::startThread(); if (! status) { @@ -5364,7 +5375,6 @@ void CoreHttp::init() << LL_ENDL; } - mRequest = new LLCore::HttpRequest; } diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 34fb21798f..f9294b4cd1 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -816,14 +816,6 @@ bool LLTextureFetchWorker::doWork(S32 param) mFetchTimer.reset(); } - static LLUUID last_id; - if (mID != last_id) - { - // LL_WARNS("Texture") << "DOWORK SWITCH: " << last_id << " to: " << mID - // << LL_ENDL; - last_id = mID; - } - if (mState == INIT) { mRawImage = NULL ; @@ -1109,10 +1101,6 @@ bool LLTextureFetchWorker::doWork(S32 param) << " Bytes: " << mRequestedSize << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth << LL_ENDL; -// LL_WARNS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset -// << " Bytes: " << mRequestedSize -// << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth -// << LL_ENDL; // Will call callbackHttpGet when curl request completes // *FIXME: enable redirection follow @@ -1241,7 +1229,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } } - if (mHaveAllData && mRequestedDiscard == 0) //the image file is fully loaded. + if (mHaveAllData /* && mRequestedDiscard == 0*/) //the image file is fully loaded. { mFileSize = total_size; } @@ -1692,13 +1680,32 @@ S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, body->addRef(); mHttpBufferArray = body; - if (data_size < mRequestedSize && mRequestedDiscard == 0) + if (! partial) + { + // Response indicates this is the entire asset regardless + // of our asking for a byte range. Mark it so and drop + // any partial data we might have so that the current + // response body becomes the entire dataset. + if (data_size <= mRequestedOffset) + { + LL_WARNS("Texture") << "Fetched entire texture " << mID + << " when it was expected to be marked complete. mImageSize: " + << mFileSize << " datasize: " << mFormattedImage->getDataSize() + << LL_ENDL; + } + mHaveAllData = TRUE; + llassert_always(mDecodeHandle == 0); + mFormattedImage = NULL; // discard any previous data we had + } + else if (data_size < mRequestedSize && mRequestedDiscard == 0) { + // *FIXME: I think we can treat this as complete regardless + // of requested discard level. Revisit this... mHaveAllData = TRUE; } else if (data_size > mRequestedSize) { - // *TODO: This shouldn't be happening any more + // *TODO: This shouldn't be happening any more (REALLY don't expect this anymore) llwarns << "data_size = " << data_size << " > requested: " << mRequestedSize << llendl; mHaveAllData = TRUE; llassert_always(mDecodeHandle == 0); -- cgit v1.2.3 From 96b2430d8daaab692068e278f0048fdd47ed3e7e Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 6 Jun 2012 14:18:44 -0400 Subject: I am getting really bad about adding files. For preceding commit. --- indra/llcorehttp/_httppolicyglobal.cpp | 137 +++++++++++++++++++++++++++++++++ indra/llcorehttp/_httppolicyglobal.h | 63 +++++++++++++++ 2 files changed, 200 insertions(+) create mode 100644 indra/llcorehttp/_httppolicyglobal.cpp create mode 100644 indra/llcorehttp/_httppolicyglobal.h diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp new file mode 100644 index 0000000000..877b85896f --- /dev/null +++ b/indra/llcorehttp/_httppolicyglobal.cpp @@ -0,0 +1,137 @@ +/** + * @file _httppolicyglobal.cpp + * @brief Definitions for internal class defining global policy option. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httppolicyglobal.h" + + +namespace LLCore +{ + + +HttpPolicyGlobal::HttpPolicyGlobal() + : mValidMask(0UL), + mConnectionLimit(32L) +{} + + +HttpPolicyGlobal::~HttpPolicyGlobal() +{} + + +HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value) +{ + switch (opt) + { + case HttpRequest::GP_CONNECTION_LIMIT: + mConnectionLimit = value; + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + mValidMask |= 1UL << int(opt); + return HttpStatus(); +} + + +HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::string & value) +{ + switch (opt) + { + case HttpRequest::GP_CA_PATH: + mCAPath = value; + break; + + case HttpRequest::GP_CA_FILE: + mCAFile = value; + break; + + case HttpRequest::GP_HTTP_PROXY: + mCAFile = value; + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + mValidMask |= 1UL << int(opt); + return HttpStatus(); +} + + +HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long & value) +{ + static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); + + switch (opt) + { + case HttpRequest::GP_CONNECTION_LIMIT: + if (! (mValidMask & (1UL << int(opt)))) + return not_set; + value = mConnectionLimit; + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + return HttpStatus(); +} + + +HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, std::string & value) +{ + static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); + + switch (opt) + { + case HttpRequest::GP_CA_PATH: + if (! (mValidMask & (1UL << int(opt)))) + return not_set; + value = mCAPath; + break; + + case HttpRequest::GP_CA_FILE: + if (! (mValidMask & (1UL << int(opt)))) + return not_set; + value = mCAFile; + break; + + case HttpRequest::GP_HTTP_PROXY: + if (! (mValidMask & (1UL << int(opt)))) + return not_set; + value = mHttpProxy; + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + return HttpStatus(); +} + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicyglobal.h b/indra/llcorehttp/_httppolicyglobal.h new file mode 100644 index 0000000000..39ffbcb9bb --- /dev/null +++ b/indra/llcorehttp/_httppolicyglobal.h @@ -0,0 +1,63 @@ +/** + * @file _httppolicyglobal.h + * @brief Declarations for internal class defining global policy option. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_POLICY_GLOBAL_H_ +#define _LLCORE_HTTP_POLICY_GLOBAL_H_ + + +#include "httprequest.h" + + +namespace LLCore +{ + +class HttpPolicyGlobal +{ +public: + HttpPolicyGlobal(); + ~HttpPolicyGlobal(); + +private: + HttpPolicyGlobal(const HttpPolicyGlobal &); // Not defined + void operator=(const HttpPolicyGlobal &); // Not defined + +public: + HttpStatus set(HttpRequest::EGlobalPolicy opt, long value); + HttpStatus set(HttpRequest::EGlobalPolicy opt, const std::string & value); + HttpStatus get(HttpRequest::EGlobalPolicy opt, long & value); + HttpStatus get(HttpRequest::EGlobalPolicy opt, std::string & value); + +public: + unsigned long mValidMask; + long mConnectionLimit; + std::string mCAPath; + std::string mCAFile; + std::string mHttpProxy; +}; // end class HttpPolicyGlobal + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_POLICY_GLOBAL_H_ -- cgit v1.2.3 From 196e1d46bdc8b35b2e79d8a9d6a693ebeeeb19d3 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 7 Jun 2012 17:46:22 -0400 Subject: Lock/Mutex fixes, documentation, new resource wait state, dtor cleanups Went through all the code and tried to document lock and thread usage in the module. There's a huge comment block introducing all of this at the beginning and I believe it's correct (though not quite complete). Keep it updated, people. Added a new state, WAIT_HTTP_RESOURCE, that's sort of a side-state of SEND_HTTP_REQ. If we hit a high-water mark for HTTP requests, the extra are shunted to the new state once. Once levels fall to a low-water mark, we run through a wait list of UUIDs, sort the valid ones by priority and release them for service. This keeps the HTTP layer busy while leaving the active queue shallow enough that requests can still be re-prioritzed cheaply. Priority model changed. The new state uses the PRIORITY_LOW mask, the old users of _LOW are now at PRIORITY_NORMAL and sleepers woken up after an external event are kicked off at PRIORITY_HIGH. This combination along with the new state should avoid priority inversion and keep things running without resorting to an infinite pipeline. New state displays as "HTW" with green text in the texture console. Request cancelation and worker run-down should now be more correct but this edge case may need more attention. --- indra/newview/lltexturefetch.cpp | 942 ++++++++++++++++++++++++++------------- indra/newview/lltexturefetch.h | 153 ++++++- indra/newview/lltextureview.cpp | 3 +- 3 files changed, 777 insertions(+), 321 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index f9294b4cd1..23232cb590 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -52,6 +52,7 @@ #include "llviewerassetstats.h" #include "llworld.h" #include "llsdserialize.h" +#include "llviewertexturelist.h" // debug #include "httprequest.h" #include "httphandler.h" @@ -60,6 +61,102 @@ ////////////////////////////////////////////////////////////////////////////// +// +// Introduction +// +// This is an attempt to document what's going on in here after-the-fact. +// It's a sincere attempt to be accurate but there will be mistakes. +// +// Purpose +// +// (What is this solving?) +// +// Threads +// +// Several threads are actively invoking code in this module. They +// include: +// +// 1. Tmain Main thread of execution +// 2. Ttf LLTextureFetch's worker thread provided by LLQueuedThread +// 3. Tcurl LLCurl's worker thread (should disappear over time) +// 4. Ttc LLTextureCache's worker thread +// 5. Tid Image decoder's worker thread +// 6. Thl HTTP library's worker thread +// +// Mutexes/Condition Variables +// +// 1. Mt Mutex defined for LLThread's condition variable (base class of +// LLTextureFetch) +// 2. Ct Condition variable for LLThread and used by lock/unlockData(). +// 3. Mwtd Special LLWorkerThread mutex used for request deletion +// operations (base class of LLTextureFetch) +// 4. Mfq LLTextureFetch's mutex covering request and command queue +// data. +// 5. Mfnq LLTextureFetch's mutex covering udp and http request +// queue data. +// 6. Mwc Mutex covering LLWorkerClass's members (base class of +// LLTextureFetchWorker). One per request. +// 7. Mw LLTextureFetchWorker's mutex. One per request. +// +// Lock Ordering Rules +// +// Not an exhaustive list but shows the order of lock acquisition +// needed to prevent deadlocks. 'A < B' means acquire 'A' before +// acquiring 'B'. +// +// 1. Mw < Mfnq +// +// Method and Member Definitions +// +// With the above, we'll try to document what threads can call what +// methods (using T* for any), what locks must be held on entry and +// are taken out during execution and what data is covered by which +// lock (if any). This latter category will be especially prone to +// error so be skeptical. +// +// A line like: "// Locks: M" indicates a method that must +// be invoked by a caller holding the 'M' lock. Similarly, +// "// Threads: T" means that a caller should be running in +// the indicated thread. +// +// For data members, a trailing comment like "// M" means that +// the data member is covered by the specified lock. Absence of a +// comment can mean the member is unlocked or that I didn't bother +// to do the archaeology. In the case of LLTextureFetchWorker, +// most data members added by the leaf class are actually covered +// by the Mw lock. +// +// In code, a trailing comment like "// [-+]M" indicates a +// lock acquision or release point. +// +// +// Worker Lifecycle +// +// (Can't unilaterally delete, cleanup is two-phase, etc.) +// +// Worker State Machine +// +// (ASCII art needed) +// +// Priority Scheme +// +// [PRIORITY_LOW, PRIORITY_NORMAL) - for WAIT_HTTP_RESOURCE state +// [PRIORITY_NORMAL, PRIORITY_HIGH) - waiting for external event +// [PRIORITY_HIGH, PRIORITY_URGENT) - rapidly transitioning through states, +// no waiting allowed +// +// + +////////////////////////////////////////////////////////////////////////////// + +// Tuning/Parameterization Constants + +static const S32 HTTP_REQUESTS_IN_QUEUE_HIGH_WATER = 40; +static const S32 HTTP_REQUESTS_IN_QUEUE_LOW_WATER = 20; + + +////////////////////////////////////////////////////////////////////////////// + class LLTextureFetchWorker : public LLWorkerClass, public LLCore::HttpHandler { @@ -69,11 +166,15 @@ private: class CacheReadResponder : public LLTextureCache::ReadResponder { public: + + // Threads: Ttf CacheReadResponder(LLTextureFetch* fetcher, const LLUUID& id, LLImageFormatted* image) : mFetcher(fetcher), mID(id) { setImage(image); } + + // Threads: Ttc virtual void completed(bool success) { LLTextureFetchWorker* worker = mFetcher->getWorker(mID); @@ -90,10 +191,14 @@ private: class CacheWriteResponder : public LLTextureCache::WriteResponder { public: + + // Threads: Ttf CacheWriteResponder(LLTextureFetch* fetcher, const LLUUID& id) : mFetcher(fetcher), mID(id) { } + + // Threads: Ttc virtual void completed(bool success) { LLTextureFetchWorker* worker = mFetcher->getWorker(mID); @@ -110,10 +215,14 @@ private: class DecodeResponder : public LLImageDecodeThread::Responder { public: + + // Threads: Ttf DecodeResponder(LLTextureFetch* fetcher, const LLUUID& id, LLTextureFetchWorker* worker) : mFetcher(fetcher), mID(id), mWorker(worker) { } + + // Threads: Tid virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux) { LLTextureFetchWorker* worker = mFetcher->getWorker(mID); @@ -146,20 +255,34 @@ private: }; public: + + // Threads: Ttf /*virtual*/ bool doWork(S32 param); // Called from LLWorkerThread::processRequest() + + // Threads: Ttf /*virtual*/ void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD) - /*virtual*/ bool deleteOK(); // called from update() (WORK THREAD) + + // Threads: Tmain + /*virtual*/ bool deleteOK(); // called from update() ~LLTextureFetchWorker(); - // void relese() { --mActiveCount; } + // Threads: Ttf + // Locks: Mw S32 callbackHttpGet(LLCore::HttpResponse * response, bool partial, bool success); + + // Threads: Ttc void callbackCacheRead(bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal); + + // Threads: Ttc void callbackCacheWrite(bool success); + + // Threads: Tid void callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux); + // Threads: T* void setGetStatus(LLCore::HttpStatus status, const std::string& reason) { LLMutexLock lock(&mWorkMutex); @@ -174,6 +297,7 @@ public: LLTextureFetch & getFetcher() { return *mFetcher; } // Inherited from LLCore::HttpHandler + // Threads: Ttf virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); protected: @@ -181,27 +305,54 @@ protected: F32 priority, S32 discard, S32 size); private: + + // Threads: Tmain /*virtual*/ void startWork(S32 param); // called from addWork() (MAIN THREAD) + + // Threads: Tmain /*virtual*/ void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD) + // Locks: Mw void resetFormattedData(); + // Locks: Mw void setImagePriority(F32 priority); + + // Locks: Mw (ctor invokes without lock) void setDesiredDiscard(S32 discard, S32 size); + + // Threads: T* + // Locks: Mw bool insertPacket(S32 index, U8* data, S32 size); + + // Locks: Mw void clearPackets(); + + // Locks: Mw void setupPacketData(); + + // Locks: Mw (ctor invokes without lock) U32 calcWorkPriority(); + + // Locks: Mw void removeFromCache(); + + // Threads: Ttf + // Locks: Mw bool processSimulatorPackets(); + + // Threads: Ttf bool writeToCacheComplete(); - void lockWorkMutex() { mWorkMutex.lock(); } - void unlockWorkMutex() { mWorkMutex.unlock(); } - + // Threads: Ttf void recordTextureStart(bool is_http); + + // Threads: Ttf void recordTextureDone(bool is_http); + void lockWorkMutex() { mWorkMutex.lock(); } + void unlockWorkMutex() { mWorkMutex.unlock(); } + private: enum e_state // mState { @@ -215,8 +366,9 @@ private: CACHE_POST, LOAD_FROM_NETWORK, LOAD_FROM_SIMULATOR, - SEND_HTTP_REQ, - WAIT_HTTP_REQ, + SEND_HTTP_REQ, // Commit to sending as HTTP + WAIT_HTTP_RESOURCE, // Waiting for HTTP resources + WAIT_HTTP_REQ, // Request sent, wait for completion DECODE_IMAGE, DECODE_IMAGE_UPDATE, WRITE_TO_CACHE, @@ -297,17 +449,15 @@ private: LLViewerAssetStats::duration_t mMetricsStartTime; - LLCore::HttpHandle mHttpHandle; - LLCore::BufferArray * mHttpBufferArray; + LLCore::HttpHandle mHttpHandle; // Handle of any active request + LLCore::BufferArray * mHttpBufferArray; // Refcounted pointer to response data int mHttpPolicyClass; - bool mHttpActive; + bool mHttpActive; // Active request to http library unsigned int mHttpReplySize; unsigned int mHttpReplyOffset; + bool mHttpReleased; // Has been released from resource wait once }; -////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////// // Cross-thread messaging for asset metrics. @@ -542,12 +692,13 @@ const char* LLTextureFetchWorker::sStateDescs[] = { "LOAD_FROM_NETWORK", "LOAD_FROM_SIMULATOR", "SEND_HTTP_REQ", + "WAIT_HTTP_RESOURCE", "WAIT_HTTP_REQ", "DECODE_IMAGE", "DECODE_IMAGE_UPDATE", "WRITE_TO_CACHE", "WAIT_ON_WRITE", - "DONE", + "DONE" }; // static @@ -608,7 +759,8 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), mHttpActive(false), mHttpReplySize(0U), - mHttpReplyOffset(0U) + mHttpReplyOffset(0U), + mHttpReleased(true) { mCanUseNET = mUrl.empty() ; @@ -630,12 +782,13 @@ LLTextureFetchWorker::~LLTextureFetchWorker() // << " Requested=" << mRequestedDiscard // << " Desired=" << mDesiredDiscard << llendl; llassert_always(!haveWork()); + + lockWorkMutex(); // +Mw (should be useless) if (mHttpActive) { - LL_WARNS("Texture") << "Deleting worker object while HTTP request is active." - << LL_ENDL; + // Issue a cancel on a live request... + mFetcher->getHttpRequest().requestCancel(mHttpHandle, NULL); } - lockWorkMutex(); if (mCacheReadHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache) { mFetcher->mTextureCache->readComplete(mCacheReadHandle, true); @@ -646,15 +799,17 @@ LLTextureFetchWorker::~LLTextureFetchWorker() } mFormattedImage = NULL; clearPackets(); - unlockWorkMutex(); - mFetcher->removeFromHTTPQueue(mID); if (mHttpBufferArray) { mHttpBufferArray->release(); mHttpBufferArray = NULL; } + unlockWorkMutex(); // -Mw + mFetcher->removeFromHTTPQueue(mID); + mFetcher->removeHttpWaiter(mID); } +// Locks: Mw void LLTextureFetchWorker::clearPackets() { for_each(mPackets.begin(), mPackets.end(), DeletePointer()); @@ -664,6 +819,7 @@ void LLTextureFetchWorker::clearPackets() mFirstPacket = 0; } +// Locks: Mw void LLTextureFetchWorker::setupPacketData() { S32 data_size = 0; @@ -696,6 +852,7 @@ void LLTextureFetchWorker::setupPacketData() } } +// Locks: Mw (ctor invokes without lock) U32 LLTextureFetchWorker::calcWorkPriority() { //llassert_always(mImagePriority >= 0 && mImagePriority <= LLViewerFetchedTexture::maxDecodePriority()); @@ -705,7 +862,7 @@ U32 LLTextureFetchWorker::calcWorkPriority() return mWorkPriority; } -// mWorkMutex is locked +// Locks: Mw (ctor invokes without lock) void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size) { bool prioritize = false; @@ -741,6 +898,7 @@ void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size) } } +// Locks: Mw void LLTextureFetchWorker::setImagePriority(F32 priority) { // llassert_always(priority >= 0 && priority <= LLViewerTexture::maxDecodePriority()); @@ -754,6 +912,7 @@ void LLTextureFetchWorker::setImagePriority(F32 priority) } } +// Locks: Mw void LLTextureFetchWorker::resetFormattedData() { if (mHttpBufferArray) @@ -768,18 +927,19 @@ void LLTextureFetchWorker::resetFormattedData() mHaveAllData = FALSE; } -// Called from MAIN thread +// Threads: Tmain void LLTextureFetchWorker::startWork(S32 param) { llassert(mFormattedImage.isNull()); } -#include "llviewertexturelist.h" // debug - -// Called from LLWorkerThread::processRequest() +// Threads: Ttf bool LLTextureFetchWorker::doWork(S32 param) { - LLMutexLock lock(&mWorkMutex); + // Release waiters while we aren't holding the Mw lock. + mFetcher->releaseHttpWaiters(); + + LLMutexLock lock(&mWorkMutex); // +Mw if ((mFetcher->isQuitting() || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) { @@ -863,7 +1023,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mUrl.compare(0, 7, "file://") == 0) { - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); // Set priority first since Responder may change it // read file from local disk std::string filename = mUrl.substr(7, std::string::npos); @@ -873,7 +1033,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } else if (mUrl.empty()) { - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); // Set priority first since Responder may change it CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority, @@ -1002,7 +1162,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mSentRequest = QUEUED; mFetcher->addToNetworkQueue(this); recordTextureStart(false); - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); return false; } @@ -1013,7 +1173,7 @@ bool LLTextureFetchWorker::doWork(S32 param) // Make certain this is in the network queue //mFetcher->addToNetworkQueue(this); //recordTextureStart(false); - //setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + //setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); return false; } } @@ -1042,7 +1202,7 @@ bool LLTextureFetchWorker::doWork(S32 param) else { mFetcher->addToNetworkQueue(this); // failsafe - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); recordTextureStart(false); } return false; @@ -1050,89 +1210,100 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == SEND_HTTP_REQ) { - if(mCanUseHTTP) - { - //NOTE: - //control the number of the http requests issued for: - //1, not openning too many file descriptors at the same time; - //2, control the traffic of http so udp gets bandwidth. - // - // static const S32 MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE = 8; - // *FIXME: For the moment, allow everything to transition into HTTP - // and have the new library order and throttle. - // if(mFetcher->getNumHTTPRequests() > MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE) - // { - // return false ; //wait. - // } + if (! mCanUseHTTP) + { + return true; // abort + } - mFetcher->removeFromNetworkQueue(this, false); - - S32 cur_size = 0; - if (mFormattedImage.notNull()) + // NOTE: + // control the number of the http requests issued for: + // 1, not openning too many file descriptors at the same time; + // 2, control the traffic of http so udp gets bandwidth. + // + if (! mHttpReleased) + { + // If this request hasn't been released before and it looks like + // we're busy, put this request into resource wait and allow something + // else to come to the front. + if (mFetcher->getNumHTTPRequests() >= HTTP_REQUESTS_IN_QUEUE_HIGH_WATER || + mFetcher->getHttpWaitersCount()) { - cur_size = mFormattedImage->getDataSize(); // amount of data we already have - if (mFormattedImage->getDiscardLevel() == 0) - { - if(cur_size > 0) - { - // We already have all the data, just decode it - mLoadedDiscard = mFormattedImage->getDiscardLevel(); - mState = DECODE_IMAGE; - return false; - } - else - { - return true ; //abort. - } - } + mState = WAIT_HTTP_RESOURCE; + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + mFetcher->addHttpWaiter(this->mID); + return false; } - mRequestedSize = mDesiredSize; - mRequestedDiscard = mDesiredDiscard; - mRequestedSize -= cur_size; - mRequestedOffset = cur_size; + } + + mFetcher->removeFromNetworkQueue(this, false); - mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; - if (!mUrl.empty()) - { - mLoaded = FALSE; - mGetStatus = LLCore::HttpStatus(); - mGetReason.clear(); - LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset - << " Bytes: " << mRequestedSize - << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth - << LL_ENDL; - - // Will call callbackHttpGet when curl request completes - // *FIXME: enable redirection follow - mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass, - mWorkPriority, - mUrl, - mRequestedOffset, - mRequestedSize, - mFetcher->mHttpOptions, - mFetcher->mHttpHeaders, - this); - } - if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle) + S32 cur_size = 0; + if (mFormattedImage.notNull()) + { + cur_size = mFormattedImage->getDataSize(); // amount of data we already have + if (mFormattedImage->getDiscardLevel() == 0) { - llwarns << "HTTP GET request failed for " << mID << llendl; - resetFormattedData(); - ++mHTTPFailCount; - return true; // failed + if (cur_size > 0) + { + // We already have all the data, just decode it + mLoadedDiscard = mFormattedImage->getDiscardLevel(); + mState = DECODE_IMAGE; + return false; + } + else + { + return true; // abort. + } } - - mHttpActive = true; - mFetcher->addToHTTPQueue(mID); - recordTextureStart(true); - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - mState = WAIT_HTTP_REQ; - - // fall through } - else //can not use http fetch. - { - return true ; //abort + mRequestedSize = mDesiredSize; + mRequestedDiscard = mDesiredDiscard; + mRequestedSize -= cur_size; + mRequestedOffset = cur_size; + + mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; + if (!mUrl.empty()) + { + mLoaded = FALSE; + mGetStatus = LLCore::HttpStatus(); + mGetReason.clear(); + LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset + << " Bytes: " << mRequestedSize + << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth + << LL_ENDL; + + // Will call callbackHttpGet when curl request completes + mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass, + mWorkPriority, + mUrl, + mRequestedOffset, + mRequestedSize, + mFetcher->mHttpOptions, + mFetcher->mHttpHeaders, + this); + } + if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle) + { + llwarns << "HTTP GET request failed for " << mID << llendl; + resetFormattedData(); + ++mHTTPFailCount; + return true; // failed } + + mHttpActive = true; + mFetcher->addToHTTPQueue(mID); + recordTextureStart(true); + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); + mState = WAIT_HTTP_REQ; + + // fall through + } + + if (mState == WAIT_HTTP_RESOURCE) + { + // Nothing to do until releaseHttpWaiters() puts us back + // into the flow... + return false; } if (mState == WAIT_HTTP_REQ) @@ -1148,7 +1319,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mHTTPFailCount = max_attempts = 1; // Don't retry llwarns << "Texture missing from server (404): " << mUrl << llendl; - //roll back to try UDP + // roll back to try UDP if (mCanUseNET) { mState = INIT; @@ -1263,7 +1434,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } else { - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); return false; } } @@ -1272,7 +1443,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { static LLCachedControl textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled"); - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); // Set priority first since Responder may change it if (textures_decode_disabled) { // for debug use, don't decode @@ -1380,7 +1551,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } } llassert_always(datasize); - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); // Set priority first since Responder may change it U32 cache_priority = mWorkPriority; mWritten = FALSE; mState = WAIT_ON_WRITE; @@ -1422,14 +1593,15 @@ bool LLTextureFetchWorker::doWork(S32 param) } else { - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); return true; } } return false; -} +} // -Mw +// Threads: Ttf // virtual void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) { @@ -1437,6 +1609,8 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe static LLCachedControl log_to_sim(gSavedSettings, "LogTextureDownloadsToSimulator"); static LLCachedControl log_texture_traffic(gSavedSettings, "LogTextureNetworkTraffic") ; + LLMutexLock lock(&mWorkMutex); // +Mw + mHttpActive = false; if (log_to_viewer_log || log_to_sim) @@ -1494,10 +1668,10 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe mFetcher->removeFromHTTPQueue(mID); recordTextureDone(true); -} +} // -Mw -// Called from MAIN thread +// Threads: Tmain void LLTextureFetchWorker::endWork(S32 param, bool aborted) { if (mDecodeHandle != 0) @@ -1510,6 +1684,8 @@ void LLTextureFetchWorker::endWork(S32 param, bool aborted) ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttf + // virtual void LLTextureFetchWorker::finishWork(S32 param, bool completed) { @@ -1526,6 +1702,13 @@ void LLTextureFetchWorker::finishWork(S32 param, bool completed) } } +// LLQueuedThread's update() method is asking if it's okay to +// delete this worker. You'll notice we're not locking in here +// which is a slight concern. Caller is expected to have made +// this request 'quiet' by whatever means... +// +// Threads: Tmain + // virtual bool LLTextureFetchWorker::deleteOK() { @@ -1572,6 +1755,7 @@ bool LLTextureFetchWorker::deleteOK() return delete_ok; } +// Threads: Ttf void LLTextureFetchWorker::removeFromCache() { if (!mInLocalCache) @@ -1583,6 +1767,8 @@ void LLTextureFetchWorker::removeFromCache() ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttf +// Locks: Mw bool LLTextureFetchWorker::processSimulatorPackets() { if (mFormattedImage.isNull() || mRequestedSize < 0) @@ -1643,13 +1829,13 @@ bool LLTextureFetchWorker::processSimulatorPackets() ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttf +// Locks: Mw S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, bool partial, bool success) { S32 data_size = 0 ; - LLMutexLock lock(&mWorkMutex); - if (mState != WAIT_HTTP_REQ) { llwarns << "callbackHttpGet for unrequested fetch worker: " << mID @@ -1732,10 +1918,11 @@ S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttc void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal) { - LLMutexLock lock(&mWorkMutex); + LLMutexLock lock(&mWorkMutex); // +Mw if (mState != LOAD_FROM_TEXTURE_CACHE) { // llwarns << "Read callback for " << mID << " with state = " << mState << llendl; @@ -1755,11 +1942,12 @@ void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* ima } mLoaded = TRUE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -} +} // -Mw +// Threads: Ttc void LLTextureFetchWorker::callbackCacheWrite(bool success) { - LLMutexLock lock(&mWorkMutex); + LLMutexLock lock(&mWorkMutex); // +Mw if (mState != WAIT_ON_WRITE) { // llwarns << "Write callback for " << mID << " with state = " << mState << llendl; @@ -1767,13 +1955,14 @@ void LLTextureFetchWorker::callbackCacheWrite(bool success) } mWritten = TRUE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -} +} // -Mw ////////////////////////////////////////////////////////////////////////////// +// Threads: Tid void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux) { - LLMutexLock lock(&mWorkMutex); + LLMutexLock lock(&mWorkMutex); // +Mw if (mDecodeHandle == 0) { return; // aborted, ignore @@ -1805,10 +1994,11 @@ void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImag mDecoded = TRUE; // llinfos << mID << " : DECODE COMPLETE " << llendl; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -} +} // -Mw ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttf bool LLTextureFetchWorker::writeToCacheComplete() { // Complete write to cache @@ -1831,6 +2021,7 @@ bool LLTextureFetchWorker::writeToCacheComplete() } +// Threads: Ttf void LLTextureFetchWorker::recordTextureStart(bool is_http) { if (! mMetricsStartTime) @@ -1843,6 +2034,7 @@ void LLTextureFetchWorker::recordTextureStart(bool is_http) } +// Threads: Ttf void LLTextureFetchWorker::recordTextureDone(bool is_http) { if (mMetricsStartTime) @@ -1892,8 +2084,6 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image LLTextureFetch::~LLTextureFetch() { - cancelHttpRequests(); - clearDeleteList(); while (! mCommands.empty()) @@ -1915,6 +2105,8 @@ LLTextureFetch::~LLTextureFetch() mHttpHeaders = NULL; } + mHttpWaitResource.clear(); + delete mHttpRequest; mHttpRequest = NULL; @@ -1978,7 +2170,7 @@ bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, con { return false; // need to wait for previous aborted request to complete } - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw worker->mActiveCount++; worker->mNeedsAux = needs_aux; worker->setImagePriority(priority); @@ -1987,41 +2179,44 @@ bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, con if (!worker->haveWork()) { worker->mState = LLTextureFetchWorker::INIT; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); } else { - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } } else { worker = new LLTextureFetchWorker(this, url, id, host, priority, desired_discard, desired_size); - lockQueue() ; + lockQueue(); // +Mfq mRequestMap[id] = worker; - unlockQueue() ; + unlockQueue(); // -Mfq - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw worker->mActiveCount++; worker->mNeedsAux = needs_aux; worker->setCanUseHTTP(can_use_http) ; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } // llinfos << "REQUESTED: " << id << " Discard: " << desired_discard << llendl; return true; } + +// Threads: T* (but Ttf in practice) + // protected void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker) { - lockQueue() ; + lockQueue(); // +Mfq bool in_request_map = (mRequestMap.find(worker->mID) != mRequestMap.end()) ; - unlockQueue() ; + unlockQueue(); // -Mfq - LLMutexLock lock(&mNetworkQueueMutex); + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq if (in_request_map) { // only add to the queue if in the request map @@ -2033,63 +2228,50 @@ void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker) { iter1->second.erase(worker->mID); } -} +} // -Mfnq +// Threads: T* void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel) { - LLMutexLock lock(&mNetworkQueueMutex); + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq size_t erased = mNetworkQueue.erase(worker->mID); if (cancel && erased > 0) { mCancelQueue[worker->mHost].insert(worker->mID); } -} +} // -Mfnq +// Threads: T* +// // protected void LLTextureFetch::addToHTTPQueue(const LLUUID& id) { - LLMutexLock lock(&mNetworkQueueMutex); + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq mHTTPTextureQueue.insert(id); mTotalHTTPRequests++; -} +} // -Mfnq +// Threads: T* void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id, S32 received_size) { - LLMutexLock lock(&mNetworkQueueMutex); + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq mHTTPTextureQueue.erase(id); mHTTPTextureBits += received_size * 8; // Approximate - does not include header bits -} - -void LLTextureFetch::cancelHttpRequests() -{ - for (queue_t::iterator iter(mHTTPTextureQueue.begin()); - mHTTPTextureQueue.end() != iter; - ++iter) - { - LLTextureFetchWorker* worker = getWorker(*iter); - if (worker && worker->mHttpActive) - { - mHttpRequest->requestCancel(worker->mHttpHandle, NULL); - } - } - - // *FIXME: Do this better with less time wasting. - int tries(10); - while (! mHTTPTextureQueue.empty() && --tries) - { - mHttpRequest->update(100); - ms_sleep(100); - } -} +} // -Mfnq +// NB: If you change deleteRequest() you should probably make +// parallel changes in removeRequest(). They're functionally +// identical with only argument variations. +// +// Threads: T* void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) { - lockQueue() ; + lockQueue(); // +Mfq LLTextureFetchWorker* worker = getWorkerAfterLock(id); if (worker) { size_t erased_1 = mRequestMap.erase(worker->mID); - unlockQueue() ; + unlockQueue(); // -Mfq llassert_always(erased_1 > 0) ; @@ -2100,15 +2282,20 @@ void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) } else { - unlockQueue() ; + unlockQueue(); // -Mfq } } +// NB: If you change removeRequest() you should probably make +// parallel changes in deleteRequest(). They're functionally +// identical with only argument variations. +// +// Threads: T* void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel) { - lockQueue() ; + lockQueue(); // +Mfq size_t erased_1 = mRequestMap.erase(worker->mID); - unlockQueue() ; + unlockQueue(); // -Mfq llassert_always(erased_1 > 0) ; removeFromNetworkQueue(worker, cancel); @@ -2117,34 +2304,39 @@ void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel) worker->scheduleDelete(); } +// Threads: T* S32 LLTextureFetch::getNumRequests() { - lockQueue() ; + lockQueue(); // +Mfq S32 size = (S32)mRequestMap.size(); - unlockQueue() ; + unlockQueue(); // -Mfq - return size ; + return size; } +// Threads: T* S32 LLTextureFetch::getNumHTTPRequests() { - mNetworkQueueMutex.lock() ; + mNetworkQueueMutex.lock(); // +Mfq S32 size = (S32)mHTTPTextureQueue.size(); - mNetworkQueueMutex.unlock() ; + mNetworkQueueMutex.unlock(); // -Mfq - return size ; + return size; } +// Threads: T* U32 LLTextureFetch::getTotalNumHTTPRequests() { - mNetworkQueueMutex.lock() ; - U32 size = mTotalHTTPRequests ; - mNetworkQueueMutex.unlock() ; + mNetworkQueueMutex.lock(); // +Mfq + U32 size = mTotalHTTPRequests; + mNetworkQueueMutex.unlock(); // -Mfq - return size ; + return size; } // call lockQueue() first! +// Threads: T* +// Locks: Mfq LLTextureFetchWorker* LLTextureFetch::getWorkerAfterLock(const LLUUID& id) { LLTextureFetchWorker* res = NULL; @@ -2156,14 +2348,16 @@ LLTextureFetchWorker* LLTextureFetch::getWorkerAfterLock(const LLUUID& id) return res; } +// Threads: T* LLTextureFetchWorker* LLTextureFetch::getWorker(const LLUUID& id) { - LLMutexLock lock(&mQueueMutex) ; + LLMutexLock lock(&mQueueMutex); // +Mfq - return getWorkerAfterLock(id) ; -} + return getWorkerAfterLock(id); +} // -Mfq +// Threads: T* bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, LLPointer& raw, LLPointer& aux) { @@ -2186,17 +2380,17 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, } else if (worker->checkWork()) { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw discard_level = worker->mDecodedDiscard; raw = worker->mRawImage; aux = worker->mAuxImage; res = true; LL_DEBUGS("Texture") << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } else { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw if ((worker->mDecodedDiscard >= 0) && (worker->mDecodedDiscard < discard_level || discard_level < 0) && (worker->mState >= LLTextureFetchWorker::WAIT_ON_WRITE)) @@ -2206,7 +2400,7 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, raw = worker->mRawImage; aux = worker->mAuxImage; } - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } } else @@ -2216,15 +2410,16 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, return res; } +// Threads: T* bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) { bool res = false; LLTextureFetchWorker* worker = getWorker(id); if (worker) { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw worker->setImagePriority(priority); - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw res = true; } return res; @@ -2239,23 +2434,24 @@ bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) // in step, at least until this can be refactored and // the redundancy eliminated. // -// May be called from any thread +// Threads: T* //virtual S32 LLTextureFetch::getPending() { S32 res; - lockData(); + lockData(); // +Ct { - LLMutexLock lock(&mQueueMutex); + LLMutexLock lock(&mQueueMutex); // +Mfq res = mRequestQueue.size(); res += mCommands.size(); - } - unlockData(); + } // -Mfq + unlockData(); // -Ct return res; } +// Locks: Ct // virtual bool LLTextureFetch::runCondition() { @@ -2270,10 +2466,10 @@ bool LLTextureFetch::runCondition() bool have_no_commands(false); { - LLMutexLock lock(&mQueueMutex); + LLMutexLock lock(&mQueueMutex); // +Mfq have_no_commands = mCommands.empty(); - } + } // -Mfq return ! (have_no_commands && (mRequestQueue.empty() && mIdleThread)); // From base class @@ -2281,7 +2477,7 @@ bool LLTextureFetch::runCondition() ////////////////////////////////////////////////////////////////////////////// -// MAIN THREAD (unthreaded envs), WORKER THREAD (threaded envs) +// Threads: Ttf void LLTextureFetch::commonUpdate() { // Run a cross-thread command, if any. @@ -2306,20 +2502,21 @@ void LLTextureFetch::commonUpdate() } -// MAIN THREAD +// Threads: Tmain + //virtual S32 LLTextureFetch::update(F32 max_time_ms) { static LLCachedControl band_width(gSavedSettings,"ThrottleBandwidthKBPS"); { - mNetworkQueueMutex.lock() ; - mMaxBandwidth = band_width ; + mNetworkQueueMutex.lock(); // +Mfnq + mMaxBandwidth = band_width; - gTextureList.sTextureBits += mHTTPTextureBits ; - mHTTPTextureBits = 0 ; + gTextureList.sTextureBits += mHTTPTextureBits; + mHTTPTextureBits = 0; - mNetworkQueueMutex.unlock() ; + mNetworkQueueMutex.unlock(); // -Mfnq } S32 res = LLWorkerThread::update(max_time_ms); @@ -2337,7 +2534,9 @@ S32 LLTextureFetch::update(F32 max_time_ms) return res; } -//called in the MAIN thread after the TextureCacheThread shuts down. +// called in the MAIN thread after the TextureCacheThread shuts down. +// +// Threads: Tmain void LLTextureFetch::shutDownTextureCacheThread() { if(mTextureCache) @@ -2347,7 +2546,9 @@ void LLTextureFetch::shutDownTextureCacheThread() } } -//called in the MAIN thread after the ImageDecodeThread shuts down. +// called in the MAIN thread after the ImageDecodeThread shuts down. +// +// Threads: Tmain void LLTextureFetch::shutDownImageDecodeThread() { if(mImageDecodeThread) @@ -2357,22 +2558,21 @@ void LLTextureFetch::shutDownImageDecodeThread() } } -// WORKER THREAD +// Threads: Ttf void LLTextureFetch::startThread() { } -// WORKER THREAD +// Threads: Ttf // // This detaches the texture fetch thread from the LLCore // HTTP library but doesn't stop the thread running in that // library... void LLTextureFetch::endThread() { - cancelHttpRequests(); } -// WORKER THREAD +// Threads: Ttf void LLTextureFetch::threadedUpdate() { llassert_always(mHttpRequest); @@ -2406,6 +2606,7 @@ void LLTextureFetch::threadedUpdate() ////////////////////////////////////////////////////////////////////////////// +// Threads: Tmain void LLTextureFetch::sendRequestListToSimulators() { // All requests @@ -2431,48 +2632,48 @@ void LLTextureFetch::sendRequestListToSimulators() typedef std::map< LLHost, request_list_t > work_request_map_t; work_request_map_t requests; { - LLMutexLock lock2(&mNetworkQueueMutex); - for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); ) - { - queue_t::iterator curiter = iter++; - LLTextureFetchWorker* req = getWorker(*curiter); - if (!req) - { - mNetworkQueue.erase(curiter); - continue; // paranoia - } - if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) && - (req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR)) + LLMutexLock lock2(&mNetworkQueueMutex); // +Mfnq + for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); ) { - // We already received our URL, remove from the queue - llwarns << "Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << llendl; - mNetworkQueue.erase(curiter); - continue; - } - if (req->mID == mDebugID) - { - mDebugCount++; // for setting breakpoints - } - if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM && - req->mTotalPackets > 0 && - req->mLastPacket >= req->mTotalPackets-1) - { - // We have all the packets... make sure this is high priority + queue_t::iterator curiter = iter++; + LLTextureFetchWorker* req = getWorker(*curiter); + if (!req) + { + mNetworkQueue.erase(curiter); + continue; // paranoia + } + if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) && + (req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR)) + { + // We already received our URL, remove from the queue + llwarns << "Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << llendl; + mNetworkQueue.erase(curiter); + continue; + } + if (req->mID == mDebugID) + { + mDebugCount++; // for setting breakpoints + } + if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM && + req->mTotalPackets > 0 && + req->mLastPacket >= req->mTotalPackets-1) + { + // We have all the packets... make sure this is high priority // req->setPriority(LLWorkerThread::PRIORITY_HIGH | req->mWorkPriority); - continue; - } - F32 elapsed = req->mRequestedTimer.getElapsedTimeF32(); - { - F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority); - if ((req->mSimRequestedDiscard != req->mDesiredDiscard) || - (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) || - (elapsed >= SIM_LAZY_FLUSH_TIMEOUT)) + continue; + } + F32 elapsed = req->mRequestedTimer.getElapsedTimeF32(); { - requests[req->mHost].insert(req); + F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority); + if ((req->mSimRequestedDiscard != req->mDesiredDiscard) || + (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) || + (elapsed >= SIM_LAZY_FLUSH_TIMEOUT)) + { + requests[req->mHost].insert(req); + } } } - } - } + } // -Mfnq for (work_request_map_t::iterator iter1 = requests.begin(); iter1 != requests.end(); ++iter1) @@ -2495,9 +2696,9 @@ void LLTextureFetch::sendRequestListToSimulators() if (req->mSentRequest != LLTextureFetchWorker::SENT_SIM) { // Initialize packet data based on data read from cache - req->lockWorkMutex(); + req->lockWorkMutex(); // +Mw req->setupPacketData(); - req->unlockWorkMutex(); + req->unlockWorkMutex(); // -Mw } if (0 == sim_request_count) { @@ -2526,12 +2727,12 @@ void LLTextureFetch::sendRequestListToSimulators() mTextureInfo.setRequestType(req->mID, LLTextureInfoDetails::REQUEST_TYPE_UDP); } - req->lockWorkMutex(); + req->lockWorkMutex(); // +Mw req->mSentRequest = LLTextureFetchWorker::SENT_SIM; req->mSimRequestedDiscard = req->mDesiredDiscard; req->mRequestedPriority = req->mImagePriority; req->mRequestedTimer.reset(); - req->unlockWorkMutex(); + req->unlockWorkMutex(); // -Mw sim_request_count++; if (sim_request_count >= IMAGES_PER_REQUEST) { @@ -2552,55 +2753,57 @@ void LLTextureFetch::sendRequestListToSimulators() // Send cancelations { - LLMutexLock lock2(&mNetworkQueueMutex); - if (gMessageSystem && !mCancelQueue.empty()) - { - for (cancel_queue_t::iterator iter1 = mCancelQueue.begin(); - iter1 != mCancelQueue.end(); ++iter1) + LLMutexLock lock2(&mNetworkQueueMutex); // +Mfnq + if (gMessageSystem && !mCancelQueue.empty()) { - LLHost host = iter1->first; - if (host == LLHost::invalid) - { - host = gAgent.getRegionHost(); - } - S32 request_count = 0; - for (queue_t::iterator iter2 = iter1->second.begin(); - iter2 != iter1->second.end(); ++iter2) + for (cancel_queue_t::iterator iter1 = mCancelQueue.begin(); + iter1 != mCancelQueue.end(); ++iter1) { - if (0 == request_count) + LLHost host = iter1->first; + if (host == LLHost::invalid) { - gMessageSystem->newMessageFast(_PREHASH_RequestImage); - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + host = gAgent.getRegionHost(); } - gMessageSystem->nextBlockFast(_PREHASH_RequestImage); - gMessageSystem->addUUIDFast(_PREHASH_Image, *iter2); - gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, -1); - gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, 0); - gMessageSystem->addU32Fast(_PREHASH_Packet, 0); - gMessageSystem->addU8Fast(_PREHASH_Type, 0); + S32 request_count = 0; + for (queue_t::iterator iter2 = iter1->second.begin(); + iter2 != iter1->second.end(); ++iter2) + { + if (0 == request_count) + { + gMessageSystem->newMessageFast(_PREHASH_RequestImage); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + } + gMessageSystem->nextBlockFast(_PREHASH_RequestImage); + gMessageSystem->addUUIDFast(_PREHASH_Image, *iter2); + gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, -1); + gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, 0); + gMessageSystem->addU32Fast(_PREHASH_Packet, 0); + gMessageSystem->addU8Fast(_PREHASH_Type, 0); // llinfos << "CANCELING IMAGE REQUEST: " << (*iter2) << llendl; - request_count++; - if (request_count >= IMAGES_PER_REQUEST) + request_count++; + if (request_count >= IMAGES_PER_REQUEST) + { + gMessageSystem->sendSemiReliable(host, NULL, NULL); + request_count = 0; + } + } + if (request_count > 0 && request_count < IMAGES_PER_REQUEST) { gMessageSystem->sendSemiReliable(host, NULL, NULL); - request_count = 0; } } - if (request_count > 0 && request_count < IMAGES_PER_REQUEST) - { - gMessageSystem->sendSemiReliable(host, NULL, NULL); - } + mCancelQueue.clear(); } - mCancelQueue.clear(); - } - } + } // -Mfnq } ////////////////////////////////////////////////////////////////////////////// +// Threads: T* +// Locks: Mw bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size) { mRequestedTimer.reset(); @@ -2633,6 +2836,7 @@ bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size) return true; } +// Threads: T* bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, U16 data_size, U8* data) { @@ -2667,14 +2871,14 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 } if (!res) { + mNetworkQueueMutex.lock(); // +Mfnq ++mBadPacketCount; - mNetworkQueueMutex.lock() ; mCancelQueue[host].insert(id); - mNetworkQueueMutex.unlock() ; + mNetworkQueueMutex.unlock(); // -Mfnq return false; } - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw // Copy header data into image object worker->mImageCodec = codec; @@ -2685,10 +2889,12 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 res = worker->insertPacket(0, data, data_size); worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw return res; } + +// Threads: T* bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data) { LLTextureFetchWorker* worker = getWorker(id); @@ -2713,14 +2919,14 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 } if (!res) { + mNetworkQueueMutex.lock(); // +Mfnq ++mBadPacketCount; - mNetworkQueueMutex.lock() ; mCancelQueue[host].insert(id); - mNetworkQueueMutex.unlock() ; + mNetworkQueueMutex.unlock(); // -Mfnq return false; } - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw res = worker->insertPacket(packet_num, data, data_size); @@ -2737,7 +2943,7 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 removeFromNetworkQueue(worker, true); // failsafe } - if(packet_num >= (worker->mTotalPackets - 1)) + if (packet_num >= (worker->mTotalPackets - 1)) { static LLCachedControl log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog"); static LLCachedControl log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator"); @@ -2749,12 +2955,14 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 mTextureInfo.setRequestCompleteTimeAndLog(id, timeNow); } } - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw return res; } ////////////////////////////////////////////////////////////////////////////// + +// Threads: T* BOOL LLTextureFetch::isFromLocalCache(const LLUUID& id) { BOOL from_cache = FALSE ; @@ -2762,14 +2970,15 @@ BOOL LLTextureFetch::isFromLocalCache(const LLUUID& id) LLTextureFetchWorker* worker = getWorker(id); if (worker) { - worker->lockWorkMutex() ; - from_cache = worker->mInLocalCache ; - worker->unlockWorkMutex() ; + worker->lockWorkMutex(); // +Mw + from_cache = worker->mInLocalCache; + worker->unlockWorkMutex(); // -Mw } return from_cache ; } +// Threads: T* S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& requested_priority_p, U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http) { @@ -2783,7 +2992,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r LLTextureFetchWorker* worker = getWorker(id); if (worker && worker->haveWork()) { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw state = worker->mState; fetch_dtime = worker->mFetchTimer.getElapsedTimeF32(); request_dtime = worker->mRequestedTimer.getElapsedTimeF32(); @@ -2810,7 +3019,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r } fetch_priority = worker->getPriority(); can_use_http = worker->getCanUseHTTP() ; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } data_progress_p = data_progress; requested_priority_p = requested_priority; @@ -2842,12 +3051,136 @@ void LLTextureFetch::dump() { llinfos << " ID: " << (*iter) << llendl; } + + llinfos << "LLTextureFetch WAIT_HTTP_RESOURCE:" << llendl; + for (wait_http_res_queue_t::const_iterator iter(mHttpWaitResource.begin()); + mHttpWaitResource.end() != iter; + ++iter) + { + llinfos << " ID: " << (*iter) << llendl; + } } +////////////////////////////////////////////////////////////////////////////// + +// HTTP Resource Waiting Methods + +// Threads: Ttf +void LLTextureFetch::addHttpWaiter(const LLUUID & tid) +{ + mNetworkQueueMutex.lock(); // +Mfnq + mHttpWaitResource.insert(tid); + mNetworkQueueMutex.unlock(); // -Mfnq +} + +// Threads: Ttf +void LLTextureFetch::removeHttpWaiter(const LLUUID & tid) +{ + mNetworkQueueMutex.lock(); // +Mfnq + wait_http_res_queue_t::iterator iter(mHttpWaitResource.find(tid)); + if (mHttpWaitResource.end() != iter) + { + mHttpWaitResource.erase(iter); + } + mNetworkQueueMutex.unlock(); // -Mfnq +} + +// Threads: Ttf +// Locks: -Mw (must not hold any worker when called) +void LLTextureFetch::releaseHttpWaiters() +{ + if (HTTP_REQUESTS_IN_QUEUE_LOW_WATER < getNumHTTPRequests()) + return; + + // Quickly make a copy of all the LLUIDs. Get off the + // mutex as early as possible. + + typedef std::vector uuid_vec_t; + uuid_vec_t tids; + + { + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq + + if (mHttpWaitResource.empty()) + return; + + const size_t limit(mHttpWaitResource.size()); + tids.resize(limit); + wait_http_res_queue_t::iterator iter(mHttpWaitResource.begin()); + for (int i(0); + i < limit && mHttpWaitResource.end() != iter; + ++i, ++iter) + { + tids[i] = *iter; + } + } // -Mfnq + + // Now lookup the UUUIDs to find valid requests and sort + // them in priority order, highest to lowest. + typedef std::set worker_set_t; + worker_set_t tids2; + + for (uuid_vec_t::const_iterator iter(tids.begin()); + tids.end() != iter; + ++iter) + { + LLTextureFetchWorker * worker(getWorker(* iter)); + if (worker) + { + tids2.insert(worker); + } + } + + // Release workers up to the high water mark. Since we aren't + // holding any locks at this point, we can be in competition + // with other callers. Do defensive things like getting + // refreshed counts of requests and checking if someone else + // has moved any worker state around.... + tids.clear(); + for (worker_set_t::iterator iter2(tids2.begin()); + tids2.end() != iter2 && 0 < (HTTP_REQUESTS_IN_QUEUE_HIGH_WATER - getNumHTTPRequests()); + ++iter2) + { + LLTextureFetchWorker * worker(* iter2); + + worker->lockWorkMutex(); // +Mw + if (LLTextureFetchWorker::WAIT_HTTP_RESOURCE != worker->mState) + { + worker->unlockWorkMutex(); // -Mw + continue; + } + worker->mHttpReleased = true; + worker->mState = LLTextureFetchWorker::SEND_HTTP_REQ; + worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); + worker->unlockWorkMutex(); // -Mw + + removeHttpWaiter(worker->mID); + } +} + +// Threads: T* +void LLTextureFetch::cancelHttpWaiters() +{ + mNetworkQueueMutex.lock(); // +Mfnq + mHttpWaitResource.clear(); + mNetworkQueueMutex.unlock(); // -Mfnq +} + +// Threads: T* +int LLTextureFetch::getHttpWaitersCount() +{ + mNetworkQueueMutex.lock(); // +Mfnq + int ret(mHttpWaitResource.size()); + mNetworkQueueMutex.unlock(); // -Mfnq + return ret; +} + + ////////////////////////////////////////////////////////////////////////////// // cross-thread command methods +// Threads: T* void LLTextureFetch::commandSetRegion(U64 region_handle) { TFReqSetRegion * req = new TFReqSetRegion(region_handle); @@ -2855,6 +3188,7 @@ void LLTextureFetch::commandSetRegion(U64 region_handle) cmdEnqueue(req); } +// Threads: T* void LLTextureFetch::commandSendMetrics(const std::string & caps_url, const LLUUID & session_id, const LLUUID & agent_id, @@ -2865,6 +3199,7 @@ void LLTextureFetch::commandSendMetrics(const std::string & caps_url, cmdEnqueue(req); } +// Threads: T* void LLTextureFetch::commandDataBreak() { // The pedantically correct way to implement this is to create a command @@ -2875,30 +3210,33 @@ void LLTextureFetch::commandDataBreak() LLTextureFetch::svMetricsDataBreak = true; } +// Threads: T* void LLTextureFetch::cmdEnqueue(TFRequest * req) { - lockQueue(); + lockQueue(); // +Mfq mCommands.push_back(req); - unlockQueue(); + unlockQueue(); // -Mfq unpause(); } +// Threads: T* LLTextureFetch::TFRequest * LLTextureFetch::cmdDequeue() { TFRequest * ret = 0; - lockQueue(); + lockQueue(); // +Mfq if (! mCommands.empty()) { ret = mCommands.front(); mCommands.erase(mCommands.begin()); } - unlockQueue(); + unlockQueue(); // -Mfq return ret; } +// Threads: Ttf void LLTextureFetch::cmdDoWork() { if (mDebugPause) @@ -2932,6 +3270,8 @@ namespace class AssetReportHandler : public LLCore::HttpHandler { public: + + // Threads: Ttf virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) { LLCore::HttpStatus status(response->getStatus()); @@ -3100,5 +3440,3 @@ truncate_viewer_metrics(int max_regions, LLSD & metrics) } // end of anonymous namespace - - diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index cfea3aad9d..53b0f7885f 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -45,6 +45,7 @@ class LLHost; class LLViewerAssetStats; // Interface class + class LLTextureFetch : public LLWorkerThread { friend class LLTextureFetchWorker; @@ -55,67 +56,174 @@ public: class TFRequest; - /*virtual*/ S32 update(F32 max_time_ms); - void shutDownTextureCacheThread() ; //called in the main thread after the TextureCacheThread shuts down. - void shutDownImageDecodeThread() ; //called in the main thread after the ImageDecodeThread shuts down. + // Threads: Tmain + /*virtual*/ S32 update(F32 max_time_ms); + + // called in the main thread after the TextureCacheThread shuts down. + // Threads: Tmain + void shutDownTextureCacheThread(); + + //called in the main thread after the ImageDecodeThread shuts down. + // Threads: Tmain + void shutDownImageDecodeThread(); + // Threads: T* (but Tmain mostly) bool createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, S32 w, S32 h, S32 c, S32 discard, bool needs_aux, bool can_use_http); + + // Requests that a fetch operation be deleted from the queue. + // If @cancel is true, also stops any I/O operations pending. + // Actual delete will be scheduled and performed later. + // + // Note: This *looks* like an override/variant of the + // base class's deleteRequest() but is functionally quite + // different. + // + // Threads: T* void deleteRequest(const LLUUID& id, bool cancel); + + // Threads: T* bool getRequestFinished(const LLUUID& id, S32& discard_level, LLPointer& raw, LLPointer& aux); + + // Threads: T* bool updateRequestPriority(const LLUUID& id, F32 priority); + // Threads: T* bool receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, U16 data_size, U8* data); + + // Threads: T* bool receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data); + // Threads: T* (but not safe) void setTextureBandwidth(F32 bandwidth) { mTextureBandwidth = bandwidth; } + + // Threads: T* (but not safe) F32 getTextureBandwidth() { return mTextureBandwidth; } - // Debug + // Threads: T* BOOL isFromLocalCache(const LLUUID& id); + + // @return Magic number giving the internal state of the + // request. We should make these codes public if we're + // going to return them as a status value. + // + // Threads: T* S32 getFetchState(const LLUUID& id, F32& decode_progress_p, F32& requested_priority_p, U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http); + + // Debug utility - generally not safe void dump(); - S32 getNumRequests() ; - S32 getNumHTTPRequests() ; - U32 getTotalNumHTTPRequests() ; + + // Threads: T* + S32 getNumRequests(); + + // Threads: T* + S32 getNumHTTPRequests(); + + // Threads: T* + U32 getTotalNumHTTPRequests(); - // Public for access by callbacks + // Threads: T* S32 getPending(); + + // Threads: T* void lockQueue() { mQueueMutex.lock(); } + + // Threads: T* void unlockQueue() { mQueueMutex.unlock(); } + + // Threads: T* LLTextureFetchWorker* getWorker(const LLUUID& id); + + // Threads: T* + // Locks: Mfq LLTextureFetchWorker* getWorkerAfterLock(const LLUUID& id); // Commands available to other threads to control metrics gathering operations. + + // Threads: T* void commandSetRegion(U64 region_handle); + + // Threads: T* void commandSendMetrics(const std::string & caps_url, const LLUUID & session_id, const LLUUID & agent_id, LLViewerAssetStats * main_stats); + + // Threads: T* void commandDataBreak(); + // Threads: T* LLCore::HttpRequest & getHttpRequest() { return *mHttpRequest; } bool isQAMode() const { return mQAMode; } + // ---------------------------------- + // HTTP resource waiting methods + + // Threads: T* + void addHttpWaiter(const LLUUID & tid); + + // Threads: T* + void removeHttpWaiter(const LLUUID & tid); + + // If there are slots, release one or more LLTextureFetchWorker + // requests from resource wait state (WAIT_HTTP_RESOURCE) to + // active (SEND_HTTP_REQ). + // + // Because this will modify state of many workers, you may not + // hold any Mw lock while calling. This makes it a little + // inconvenient to use but that's the rule. + // + // Threads: T* + // Locks: -Mw (must not hold any worker when called) + void releaseHttpWaiters(); + + // Threads: T* + void cancelHttpWaiters(); + + // Threads: T* + int getHttpWaitersCount(); + // ---------------------------------- + protected: + // Threads: T* (but Ttf in practice) void addToNetworkQueue(LLTextureFetchWorker* worker); + + // Threads: T* void removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel); + + // Threads: T* void addToHTTPQueue(const LLUUID& id); + + // Threads: T* void removeFromHTTPQueue(const LLUUID& id, S32 received_size = 0); + + // Identical to @deleteRequest but with different arguments + // (caller already has the worker pointer). + // + // Threads: T* void removeRequest(LLTextureFetchWorker* worker, bool cancel); - void cancelHttpRequests(); // Overrides from the LLThread tree + // Locks: Ct bool runCondition(); private: + // Threads: Tmain void sendRequestListToSimulators(); + + // Threads: Ttf /*virtual*/ void startThread(void); + + // Threads: Ttf /*virtual*/ void endThread(void); + + // Threads: Ttf /*virtual*/ void threadedUpdate(void); + + // Threads: Ttf void commonUpdate(); // Metrics command helpers @@ -126,6 +234,8 @@ private: * Takes ownership of the TFRequest object. * * Method locks the command queue. + * + * Threads: T* */ void cmdEnqueue(TFRequest *); @@ -136,6 +246,8 @@ private: * Caller acquires ownership of the object and must dispose of it. * * Method locks the command queue. + * + * Threads: T* */ TFRequest * cmdDequeue(); @@ -145,6 +257,8 @@ private: * additional commands. * * Method locks the command queue. + * + * Threads: Ttf */ void cmdDoWork(); @@ -164,29 +278,29 @@ private: // Map of all requests by UUID typedef std::map map_t; - map_t mRequestMap; + map_t mRequestMap; // Mfq // Set of requests that require network data typedef std::set queue_t; - queue_t mNetworkQueue; - queue_t mHTTPTextureQueue; + queue_t mNetworkQueue; // Mfnq + queue_t mHTTPTextureQueue; // Mfnq typedef std::map > cancel_queue_t; - cancel_queue_t mCancelQueue; - F32 mTextureBandwidth; - F32 mMaxBandwidth; + cancel_queue_t mCancelQueue; // Mfnq + F32 mTextureBandwidth; // + F32 mMaxBandwidth; // Mfnq LLTextureInfo mTextureInfo; - U32 mHTTPTextureBits; + U32 mHTTPTextureBits; // Mfnq //debug use - U32 mTotalHTTPRequests ; + U32 mTotalHTTPRequests; // Out-of-band cross-thread command queue. This command queue // is logically tied to LLQueuedThread's list of // QueuedRequest instances and so must be covered by the // same locks. typedef std::vector command_queue_t; - command_queue_t mCommands; + command_queue_t mCommands; // Mfq // If true, modifies some behaviors that help with QA tasks. const bool mQAMode; @@ -197,6 +311,9 @@ private: LLCore::HttpRequest * mHttpRequest; LLCore::HttpOptions * mHttpOptions; LLCore::HttpHeaders * mHttpHeaders; + + typedef std::set wait_http_res_queue_t; + wait_http_res_queue_t mHttpWaitResource; // Mfnq public: // A probabilistically-correct indicator that the current diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index 1c89766b26..5f1d7829ed 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -235,13 +235,14 @@ void LLTextureBar::draw() { "NET", LLColor4::green }, // LOAD_FROM_NETWORK { "SIM", LLColor4::green }, // LOAD_FROM_SIMULATOR { "REQ", LLColor4::yellow },// SEND_HTTP_REQ + { "HTW", LLColor4::green }, // WAIT_HTTP_RES { "HTP", LLColor4::green }, // WAIT_HTTP_REQ { "DEC", LLColor4::yellow },// DECODE_IMAGE { "DEC", LLColor4::green }, // DECODE_IMAGE_UPDATE { "WRT", LLColor4::purple },// WRITE_TO_CACHE { "WRT", LLColor4::orange },// WAIT_ON_WRITE { "END", LLColor4::red }, // DONE -#define LAST_STATE 12 +#define LAST_STATE 13 { "CRE", LLColor4::magenta }, // LAST_STATE+1 { "FUL", LLColor4::green }, // LAST_STATE+2 { "BAD", LLColor4::red }, // LAST_STATE+3 -- cgit v1.2.3 From cab68bb04ed3c3734b48c703a43d41f231911647 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 7 Jun 2012 18:10:32 -0400 Subject: First efforts at getting the texture cache working. This is now avoiding doing HTTP fetches for read data. Not certain it's completely correct but the difference is already significant. --- indra/newview/lltexturefetch.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 23232cb590..92b847345d 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1584,7 +1584,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == DONE) { - if (mDecodedDiscard >= 0 && mDesiredDiscard < mDecodedDiscard) + if (mDecodedDiscard > 0 && mDesiredDiscard < mDecodedDiscard) { // More data was requested, return to INIT mState = INIT; @@ -1883,7 +1883,7 @@ S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, llassert_always(mDecodeHandle == 0); mFormattedImage = NULL; // discard any previous data we had } - else if (data_size < mRequestedSize && mRequestedDiscard == 0) + else if (data_size < mRequestedSize /*&& mRequestedDiscard == 0*/) { // *FIXME: I think we can treat this as complete regardless // of requested discard level. Revisit this... -- cgit v1.2.3 From 841a447e55a48dbf48d9508a0b5a42b7c77693b7 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 8 Jun 2012 16:12:52 +0000 Subject: Limit libcurl's DNS resolution to IPV4 addresses for now. Callers who want to try IPV6 can still override at will using CURLOPT_IPRESOLVE. --- indra/llmessage/llcurl.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index 3bcaffc275..c444f82b51 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -308,6 +308,8 @@ LLCurl::Easy* LLCurl::Easy::getEasy() // multi handles cache if they are added to one. CURLcode result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); check_curl_code(result); + result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + check_curl_code(result); ++gCurlEasyCount; return easy; @@ -494,7 +496,8 @@ void LLCurl::Easy::prepRequest(const std::string& url, //setopt(CURLOPT_VERBOSE, 1); // useful for debugging setopt(CURLOPT_NOSIGNAL, 1); - + setopt(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + // Set the CURL options for either Socks or HTTP proxy LLProxy::getInstance()->applyProxySettings(this); -- cgit v1.2.3 From 28a04400b4160dd34166483ddcf0c12637bcc363 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 8 Jun 2012 20:21:54 -0400 Subject: Implemented HTTP retry for requests. Went in rather easily which surprised me. Added a retry queue similar to ready queue to the policy object which is sorted by retry time. Currently do five retries (after the initial try) delayed by .25, .5, 1, 2 and 5 seconds. Removed the retry logic from the lltexturefetch module. Upped the waiting time in the unit test for the retries. People won't like this but tough, need tests. --- indra/llcorehttp/_httplibcurl.cpp | 58 +++++++++-- indra/llcorehttp/_httplibcurl.h | 4 +- indra/llcorehttp/_httpoprequest.cpp | 51 ++++++++-- indra/llcorehttp/_httpoprequest.h | 5 + indra/llcorehttp/_httppolicy.cpp | 98 ++++++++++++++----- indra/llcorehttp/_httppolicy.h | 19 +++- indra/llcorehttp/_httpreadyqueue.h | 2 - indra/llcorehttp/_httpretryqueue.h | 94 ++++++++++++++++++ indra/llcorehttp/httpcommon.h | 12 +++ indra/llcorehttp/tests/test_httprequest.hpp | 2 +- indra/newview/lltexturefetch.cpp | 145 ++++++++++++++++++++-------- indra/newview/lltexturefetch.h | 6 +- 12 files changed, 402 insertions(+), 94 deletions(-) create mode 100644 indra/llcorehttp/_httpretryqueue.h diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 5272c391e8..05b2c2be69 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -29,6 +29,7 @@ #include "httpheaders.h" #include "bufferarray.h" #include "_httpoprequest.h" +#include "_httppolicy.h" namespace LLCore @@ -85,6 +86,8 @@ void HttpLibcurl::term() HttpService::ELoopSpeed HttpLibcurl::processTransport() { + HttpService::ELoopSpeed ret(HttpService::REQUEST_SLEEP); + // Give libcurl some cycles to do I/O & callbacks for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) { @@ -110,7 +113,8 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() CURL * handle(msg->easy_handle); CURLcode result(msg->data.result); - completeRequest(mMultiHandles[policy_class], handle, result); + HttpService::ELoopSpeed speed(completeRequest(mMultiHandles[policy_class], handle, result)); + ret = (std::min)(ret, speed); handle = NULL; // No longer valid on return } else if (CURLMSG_NONE == msg->msg) @@ -127,7 +131,11 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() } } - return mActiveOps.empty() ? HttpService::REQUEST_SLEEP : HttpService::NORMAL; + if (! mActiveOps.empty()) + { + ret = (std::min)(ret, HttpService::NORMAL); + } + return ret; } @@ -153,8 +161,12 @@ void HttpLibcurl::addOp(HttpOpRequest * op) } -void HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status) +HttpService::ELoopSpeed HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status) { + static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); + static const HttpStatus cant_res_proxy(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_PROXY); + static const HttpStatus cant_res_host(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST); + HttpOpRequest * op(NULL); curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); // *FIXME: check the pointer @@ -190,10 +202,7 @@ void HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode int http_status(200); curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_status); - op->mStatus = LLCore::HttpStatus(http_status, - (http_status >= 200 && http_status <= 299 - ? HE_SUCCESS - : HE_REPLY_ERROR)); + op->mStatus = LLCore::HttpStatus(http_status); } // Detach from multi and recycle handle @@ -201,9 +210,42 @@ void HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode curl_easy_cleanup(handle); op->mCurlHandle = NULL; - // Deliver to reply queue and release + // Retry or finalize + if (! op->mStatus) + { + // If this failed, we might want to retry. Have to inspect + // the status a little more deeply for those reasons worth retrying... + if (op->mPolicyRetries < op->mPolicyRetryLimit && + ((op->mStatus.isHttpStatus() && op->mStatus.mType >= 499 && op->mStatus.mType <= 599) || + cant_connect == op->mStatus || + cant_res_proxy == op->mStatus || + cant_res_host == op->mStatus)) + { + // Okay, worth a retry. We include 499 in this test as + // it's the old 'who knows?' error from many grid services... + HttpPolicy & policy(mService->getPolicy()); + + policy.retryOp(op); + return HttpService::NORMAL; // Having pushed to retry, keep things running + } + } + + // This op is done, finalize it delivering it to the reply queue... + if (! op->mStatus) + { + LL_WARNS("CoreHttp") << "URL op failed after " << op->mPolicyRetries + << " retries. Reason: " << op->mStatus.toString() + << LL_ENDL; + } + else if (op->mPolicyRetries) + { + LL_WARNS("CoreHttp") << "URL op succeeded after " << op->mPolicyRetries << " retries." + << LL_ENDL; + } + op->stageFromActive(mService); op->release(); + return HttpService::REQUEST_SLEEP; } diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index ec325c1946..fe628b9ab0 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -83,7 +83,9 @@ public: protected: /// Invoked when libcurl has indicated a request has been processed /// to completion and we need to move the request to a new state. - void completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status); + HttpService::ELoopSpeed completeRequest(CURLM * multi_handle, + CURL * handle, + CURLcode status); protected: typedef std::set active_set_t; diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 4bdc4a5257..895629c514 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -99,8 +99,15 @@ HttpOpRequest::HttpOpRequest() mReplyBody(NULL), mReplyOffset(0), mReplyLength(0), - mReplyHeaders(NULL) -{} + mReplyHeaders(NULL), + mPolicyRetries(0), + mPolicyRetryAt(HttpTime(0)), + mPolicyRetryLimit(5) // *FIXME: Get from policy definitions +{ + // *NOTE: As members are added, retry initialization/cleanup + // may need to be extended in @prepareRequest(). +} + HttpOpRequest::~HttpOpRequest() @@ -130,7 +137,6 @@ HttpOpRequest::~HttpOpRequest() } mCurlService = NULL; - if (mCurlHeaders) { @@ -313,6 +319,30 @@ HttpStatus HttpOpRequest::setupPost(HttpRequest::policy_t policy_id, HttpStatus HttpOpRequest::prepareRequest(HttpService * service) { + // Scrub transport and result data for retried op case + mCurlActive = false; + mCurlHandle = NULL; + mCurlService = NULL; + if (mCurlHeaders) + { + curl_slist_free_all(mCurlHeaders); + mCurlHeaders = NULL; + } + mCurlBodyPos = 0; + + if (mReplyBody) + { + mReplyBody->release(); + mReplyBody = NULL; + } + mReplyOffset = 0; + mReplyLength = 0; + if (mReplyHeaders) + { + mReplyHeaders->release(); + mReplyHeaders = NULL; + } + // *FIXME: better error handling later HttpStatus status; @@ -321,6 +351,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) mCurlHandle = curl_easy_init(); // curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, 30); curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, 30); curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1); @@ -403,12 +434,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) break; } - if (mReqHeaders) - { - mCurlHeaders = append_headers_to_slist(mReqHeaders, mCurlHeaders); - } - mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); - + // There's a CURLOPT for this now... if ((mReqOffset || mReqLength) && HOR_GET == mReqMethod) { static const char * const fmt1("Range: bytes=%lu-%lu"); @@ -428,6 +454,13 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) range_line[sizeof(range_line) - 1] = '\0'; mCurlHeaders = curl_slist_append(mCurlHeaders, range_line); } + + mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); + if (mReqHeaders) + { + // Caller's headers last to override + mCurlHeaders = append_headers_to_slist(mReqHeaders, mCurlHeaders); + } curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders); if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS)) diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 0cad4e8459..6dcf30ca0c 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -128,6 +128,11 @@ public: off_t mReplyOffset; size_t mReplyLength; HttpHeaders * mReplyHeaders; + + // Policy data + int mPolicyRetries; + HttpTime mPolicyRetryAt; + const int mPolicyRetryLimit; }; // end class HttpOpRequest diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 51f5e487dc..1f4cd34a4b 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -24,39 +24,46 @@ * $/LicenseInfo$ */ +#include "linden_common.h" + #include "_httppolicy.h" #include "_httpoprequest.h" #include "_httpservice.h" #include "_httplibcurl.h" +#include "lltimer.h" + namespace LLCore { HttpPolicy::HttpPolicy(HttpService * service) : mService(service) -{ - for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) - { - mReadyInClass[policy_class] = 0; - } -} +{} HttpPolicy::~HttpPolicy() { - for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) + for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mState); ++policy_class) { - HttpReadyQueue & readyq(mReadyQueue[policy_class]); + HttpRetryQueue & retryq(mState[policy_class].mRetryQueue); + while (! retryq.empty()) + { + HttpOpRequest * op(retryq.top()); + op->cancel(); + op->release(); + retryq.pop(); + } + + HttpReadyQueue & readyq(mState[policy_class].mReadyQueue); while (! readyq.empty()) { HttpOpRequest * op(readyq.top()); op->cancel(); op->release(); - mReadyInClass[policy_class]--; readyq.pop(); } } @@ -68,27 +75,69 @@ void HttpPolicy::addOp(HttpOpRequest * op) { const int policy_class(op->mReqPolicy); - mReadyQueue[policy_class].push(op); - ++mReadyInClass[policy_class]; + op->mPolicyRetries = 0; + mState[policy_class].mReadyQueue.push(op); +} + + +void HttpPolicy::retryOp(HttpOpRequest * op) +{ + static const HttpTime retry_deltas[] = + { + 250000, // 1st retry in 0.25 S, etc... + 500000, + 1000000, + 2000000, + 5000000 // ... to every 5.0 S. + }; + static const int delta_max(int(LL_ARRAY_SIZE(retry_deltas)) - 1); + + const HttpTime now(totalTime()); + const int policy_class(op->mReqPolicy); + + const HttpTime delta(retry_deltas[llclamp(op->mPolicyRetries, 0, delta_max)]); + op->mPolicyRetryAt = now + delta; + ++op->mPolicyRetries; + LL_WARNS("CoreHttp") << "URL op retry #" << op->mPolicyRetries + << " being scheduled for " << delta << " uSecs from now." + << LL_ENDL; + mState[policy_class].mRetryQueue.push(op); } HttpService::ELoopSpeed HttpPolicy::processReadyQueue() { + const HttpTime now(totalTime()); HttpService::ELoopSpeed result(HttpService::REQUEST_SLEEP); HttpLibcurl & transport(mService->getTransport()); - for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) + for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mState); ++policy_class) { - HttpReadyQueue & readyq(mReadyQueue[policy_class]); int active(transport.getActiveCountInClass(policy_class)); int needed(8 - active); - if (needed > 0 && mReadyInClass[policy_class] > 0) + HttpRetryQueue & retryq(mState[policy_class].mRetryQueue); + HttpReadyQueue & readyq(mState[policy_class].mReadyQueue); + + if (needed > 0) { - // Scan ready queue for requests that match policy - - while (! readyq.empty() && needed > 0 && mReadyInClass[policy_class] > 0) + // First see if we have any retries... + while (needed > 0 && ! retryq.empty()) + { + HttpOpRequest * op(retryq.top()); + if (op->mPolicyRetryAt > now) + break; + + retryq.pop(); + + op->stageFromReady(mService); + op->release(); + + --needed; + } + + // Now go on to the new requests... + while (needed > 0 && ! readyq.empty()) { HttpOpRequest * op(readyq.top()); readyq.pop(); @@ -96,17 +145,16 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() op->stageFromReady(mService); op->release(); - --mReadyInClass[policy_class]; --needed; } } - - if (! readyq.empty()) + + if (! readyq.empty() || ! retryq.empty()) { // If anything is ready, continue looping... result = (std::min)(result, HttpService::NORMAL); } - } + } // end foreach policy_class return result; } @@ -114,9 +162,9 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t priority) { - for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) + for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mState); ++policy_class) { - HttpReadyQueue::container_type & c(mReadyQueue[policy_class].get_container()); + HttpReadyQueue::container_type & c(mState[policy_class].mReadyQueue.get_container()); // Scan ready queue for requests that match policy for (HttpReadyQueue::container_type::iterator iter(c.begin()); c.end() != iter;) @@ -126,9 +174,9 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior if (static_cast(*cur) == handle) { HttpOpRequest * op(*cur); - c.erase(cur); // All iterators are now invalidated + c.erase(cur); // All iterators are now invalidated op->mReqPriority = priority; - mReadyQueue[policy_class].push(op); // Re-insert using adapter class + mState[policy_class].mReadyQueue.push(op); // Re-insert using adapter class return true; } } diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 425079ec63..6f18264f3d 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -31,6 +31,7 @@ #include "httprequest.h" #include "_httpservice.h" #include "_httpreadyqueue.h" +#include "_httpretryqueue.h" #include "_httppolicyglobal.h" @@ -67,6 +68,14 @@ public: /// additional references will be added.) void addOp(HttpOpRequest *); + /// Similar to addOp, used when a caller wants to retry a + /// request that has failed. It's placed on a special retry + /// queue but ordered by retry time not priority. Otherwise, + /// handling is the same and retried operations are considered + /// before new ones but that doesn't guarantee completion + /// order. + void retryOp(HttpOpRequest *); + // Shadows HttpService's method bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); @@ -77,10 +86,14 @@ public: return mGlobalOptions; } - protected: - int mReadyInClass[HttpRequest::POLICY_CLASS_LIMIT]; - HttpReadyQueue mReadyQueue[HttpRequest::POLICY_CLASS_LIMIT]; + struct State + { + HttpReadyQueue mReadyQueue; + HttpRetryQueue mRetryQueue; + }; + + State mState[HttpRequest::POLICY_CLASS_LIMIT]; HttpService * mService; // Naked pointer, not refcounted, not owner HttpPolicyGlobal mGlobalOptions; diff --git a/indra/llcorehttp/_httpreadyqueue.h b/indra/llcorehttp/_httpreadyqueue.h index 2cd96aefe3..87828834dc 100644 --- a/indra/llcorehttp/_httpreadyqueue.h +++ b/indra/llcorehttp/_httpreadyqueue.h @@ -36,8 +36,6 @@ namespace LLCore { -class HttpOpRequest; - /// HttpReadyQueue provides a simple priority queue for HttpOpRequest objects. /// /// This uses the priority_queue adaptor class to provide the queue diff --git a/indra/llcorehttp/_httpretryqueue.h b/indra/llcorehttp/_httpretryqueue.h new file mode 100644 index 0000000000..745adec09d --- /dev/null +++ b/indra/llcorehttp/_httpretryqueue.h @@ -0,0 +1,94 @@ +/** + * @file _httpretryqueue.h + * @brief Internal declaration for the operation retry queue + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_RETRY_QUEUE_H_ +#define _LLCORE_HTTP_RETRY_QUEUE_H_ + + +#include + +#include "_httpoprequest.h" + + +namespace LLCore +{ + +/// HttpRetryQueue provides a simple priority queue for HttpOpRequest objects. +/// +/// This uses the priority_queue adaptor class to provide the queue +/// as well as the ordering scheme while allowing us access to the +/// raw container if we follow a few simple rules. One of the more +/// important of those rules is that any iterator becomes invalid +/// on element erasure. So pay attention. +/// +/// Threading: not thread-safe. Expected to be used entirely by +/// a single thread, typically a worker thread of some sort. + +struct HttpOpRetryCompare +{ + bool operator()(const HttpOpRequest * lhs, const HttpOpRequest * rhs) + { + return lhs->mPolicyRetryAt < rhs->mPolicyRetryAt; + } +}; + + +typedef std::priority_queue, + LLCore::HttpOpRetryCompare> HttpRetryQueueBase; + +class HttpRetryQueue : public HttpRetryQueueBase +{ +public: + HttpRetryQueue() + : HttpRetryQueueBase() + {} + + ~HttpRetryQueue() + {} + +protected: + HttpRetryQueue(const HttpRetryQueue &); // Not defined + void operator=(const HttpRetryQueue &); // Not defined + +public: + const container_type & get_container() const + { + return c; + } + + container_type & get_container() + { + return c; + } + +}; // end class HttpRetryQueue + + +} // end namespace LLCore + + +#endif // _LLCORE_HTTP_RETRY_QUEUE_H_ diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index fd2661b700..42b75edb41 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -114,6 +114,9 @@ namespace LLCore typedef void * HttpHandle; #define LLCORE_HTTP_HANDLE_INVALID (NULL) +/// For internal scheduling and metrics, we use a microsecond +/// timebase compatible with the environment. +typedef U64 HttpTime; /// Error codes defined by the library itself as distinct from /// libcurl (or any other transport provider). @@ -180,6 +183,15 @@ struct HttpStatus mStatus(status) {} + HttpStatus(int http_status) + : mType(http_status), + mStatus(http_status >= 200 && http_status <= 299 + ? HE_SUCCESS + : HE_REPLY_ERROR) + { + llassert(http_status >= 100 && http_status <= 999); + } + HttpStatus(const HttpStatus & rhs) : mType(rhs.mType), mStatus(rhs.mStatus) diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 0e9d7d8979..2d91b95347 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -381,7 +381,7 @@ void HttpRequestTestObjectType::test<5>() // Run the notification pump. int count(0); - int limit(20); + int limit(180); // With retries, can take more than 10 seconds to give up while (count++ < limit && mHandlerCalls < 1) { req->update(1000); diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 92b847345d..4a46ea0e97 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -67,9 +67,53 @@ // This is an attempt to document what's going on in here after-the-fact. // It's a sincere attempt to be accurate but there will be mistakes. // +// // Purpose // -// (What is this solving?) +// What is this module trying to do? It accepts requests to load textures +// at a given priority and discard level and notifies the caller when done +// (successfully or not). Additional constraints are: +// +// * Support a local texture cache. Don't hit network when possible +// to avoid it. +// * Use UDP or HTTP as directed or as fallback. HTTP is tried when +// not disabled and a URL is available. UDP when a URL isn't +// available or HTTP attempts fail. +// * Asynchronous (using threads). Main thread is not to be blocked or +// burdened. +// * High concurrency. Many requests need to be in-flight and at various +// stages of completion. +// * Tolerate frequent re-prioritizations of requests. Priority is +// a reflection of a camera's viewpoint and as that viewpoint changes, +// objects and textures become more and less relevant and that is +// expressed at this level by priority changes and request cancelations. +// +// The caller interfaces that fall out of the above and shape the +// implementation are: +// * createRequest - Load j2c image via UDP or HTTP at given discard level and priority +// * deleteRequest - Request removal of prior request +// * getRequestFinished - Test if request is finished returning data to caller +// * updateRequestPriority - Change priority of existing request +// * getFetchState - Retrieve progress on existing request +// +// Everything else in here is mostly plumbing, metrics and debug. +// +// +// The Work Queue +// +// The two central classes are LLTextureFetch and LLTextureFetchWorker. +// LLTextureFetch combines threading with a priority queue of work +// requests. The priority queue is sorted by a U32 priority derived +// from the F32 priority in the APIs. The *only* work request that +// receives service time by this thread is the highest priority +// request. All others wait until it is complete or a dynamic priority +// change has re-ordered work. +// +// LLTextureFetchWorker implements the work request and is 1:1 with +// texture fetch requests. Embedded in each is a state machine that +// walks it through the cache, HTTP, UDP, image decode and retry +// steps of texture acquisition. +// // // Threads // @@ -83,6 +127,7 @@ // 5. Tid Image decoder's worker thread // 6. Thl HTTP library's worker thread // +// // Mutexes/Condition Variables // // 1. Mt Mutex defined for LLThread's condition variable (base class of @@ -98,6 +143,7 @@ // LLTextureFetchWorker). One per request. // 7. Mw LLTextureFetchWorker's mutex. One per request. // +// // Lock Ordering Rules // // Not an exhaustive list but shows the order of lock acquisition @@ -105,6 +151,8 @@ // acquiring 'B'. // // 1. Mw < Mfnq +// (there are many more...) +// // // Method and Member Definitions // @@ -124,7 +172,10 @@ // comment can mean the member is unlocked or that I didn't bother // to do the archaeology. In the case of LLTextureFetchWorker, // most data members added by the leaf class are actually covered -// by the Mw lock. +// by the Mw lock. You may also see "// T" which means that +// the member's usage is restricted to one thread (except for +// perhaps construction and destruction) and so explicit locking +// isn't used. // // In code, a trailing comment like "// [-+]M" indicates a // lock acquision or release point. @@ -132,27 +183,54 @@ // // Worker Lifecycle // -// (Can't unilaterally delete, cleanup is two-phase, etc.) +// The threading and responder model makes it very likely that +// other components are holding on to a pointer to a worker request. +// So, uncoordinated deletions of requests is a guarantee of memory +// corruption in a short time. So destroying a request involves +// invocations's of LLQueuedThread/LLWorkerThread's abort/stop +// logic that removes workers and puts them ona delete queue for +// 2-phase destruction. That second phase is deferrable by calls +// to deleteOK() which only allow final destruction (via dtor) +// once deleteOK has determined that the request is in a safe +// state. +// // // Worker State Machine // // (ASCII art needed) // +// // Priority Scheme // // [PRIORITY_LOW, PRIORITY_NORMAL) - for WAIT_HTTP_RESOURCE state // [PRIORITY_NORMAL, PRIORITY_HIGH) - waiting for external event -// [PRIORITY_HIGH, PRIORITY_URGENT) - rapidly transitioning through states, +// [PRIORITY_HIGH, PRIORITY_URGENT) - External event delivered, +// rapidly transitioning through states, // no waiting allowed // +// By itself, the above work queue model would fail the concurrency +// and liveness requirements of the interface. A high priority +// request could find itself on the head and stalled for external +// reasons (see VWR-28996). So a few additional constraints are +// required to keep things running: +// * Anything that can make forward progress must be kept at a +// higher priority than anything that can't. +// * On completion of external events, the associated request +// needs to be elevated beyond the normal range to handle +// any data delivery and release any external resource. +// +// This effort is made to keep higher-priority entities moving +// forward in their state machines at every possible step of +// processing. It's not entirely proven that this produces the +// experiencial benefits promised. // ////////////////////////////////////////////////////////////////////////////// // Tuning/Parameterization Constants -static const S32 HTTP_REQUESTS_IN_QUEUE_HIGH_WATER = 40; -static const S32 HTTP_REQUESTS_IN_QUEUE_LOW_WATER = 20; +static const S32 HTTP_REQUESTS_IN_QUEUE_HIGH_WATER = 40; // Maximum requests to have active in HTTP +static const S32 HTTP_REQUESTS_IN_QUEUE_LOW_WATER = 20; // Active level at which to refill ////////////////////////////////////////////////////////////////////////////// @@ -425,7 +503,6 @@ private: BOOL mInLocalCache; bool mCanUseHTTP ; bool mCanUseNET ; //can get from asset server. - S32 mHTTPFailCount; S32 mRetryAttempt; S32 mActiveCount; LLCore::HttpStatus mGetStatus; @@ -745,7 +822,6 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mHaveAllData(FALSE), mInLocalCache(FALSE), mCanUseHTTP(true), - mHTTPFailCount(0), mRetryAttempt(0), mActiveCount(0), mWorkMutex(NULL), @@ -936,6 +1012,9 @@ void LLTextureFetchWorker::startWork(S32 param) // Threads: Ttf bool LLTextureFetchWorker::doWork(S32 param) { + static const LLCore::HttpStatus http_not_found(HTTP_NOT_FOUND); + static const LLCore::HttpStatus http_service_unavail(HTTP_SERVICE_UNAVAILABLE); + // Release waiters while we aren't holding the Mw lock. mFetcher->releaseHttpWaiters(); @@ -1286,7 +1365,6 @@ bool LLTextureFetchWorker::doWork(S32 param) { llwarns << "HTTP GET request failed for " << mID << llendl; resetFormattedData(); - ++mHTTPFailCount; return true; // failed } @@ -1313,10 +1391,8 @@ bool LLTextureFetchWorker::doWork(S32 param) S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; if (mRequestedSize < 0) { - S32 max_attempts; - if (mGetStatus == LLCore::HttpStatus(HTTP_NOT_FOUND, LLCore::HE_REPLY_ERROR)) + if (http_not_found == mGetStatus) { - mHTTPFailCount = max_attempts = 1; // Don't retry llwarns << "Texture missing from server (404): " << mUrl << llendl; // roll back to try UDP @@ -1328,47 +1404,32 @@ bool LLTextureFetchWorker::doWork(S32 param) return false; } } - else if (mGetStatus == LLCore::HttpStatus(HTTP_SERVICE_UNAVAILABLE, LLCore::HE_REPLY_ERROR)) + else if (http_service_unavail == mGetStatus) { - // *TODO: Should probably introduce a timer here to delay future HTTP requsts - // for a short time (~1s) to ease server load? Ideally the server would queue - // requests instead of returning 503... we already limit the number pending. - ++mHTTPFailCount; - max_attempts = mHTTPFailCount+1; // Keep retrying LL_INFOS_ONCE("Texture") << "Texture server busy (503): " << mUrl << LL_ENDL; } else { - const S32 HTTP_MAX_RETRY_COUNT = 3; - max_attempts = HTTP_MAX_RETRY_COUNT + 1; - ++mHTTPFailCount; llinfos << "HTTP GET failed for: " << mUrl << " Status: " << mGetStatus.toHex() << " Reason: '" << mGetReason << "'" - << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl; + // *FIXME: Add retry info for reporting purposes... + // << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts + << llendl; } - if (mHTTPFailCount >= max_attempts) - { - if (cur_size > 0) - { - // Use available data - mLoadedDiscard = mFormattedImage->getDiscardLevel(); - mState = DECODE_IMAGE; - return false; - } - else - { - resetFormattedData(); - mState = DONE; - return true; // failed - } - } - else + if (cur_size > 0) { - mState = SEND_HTTP_REQ; - return false; // retry + // Use available data + mLoadedDiscard = mFormattedImage->getDiscardLevel(); + mState = DECODE_IMAGE; + return false; } + + // Fail harder + resetFormattedData(); + mState = DONE; + return true; // failed } if (! mHttpBufferArray || ! mHttpBufferArray->size()) @@ -1649,7 +1710,7 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe } else { - static const LLCore::HttpStatus par_status(LLCore::HttpStatus(HTTP_PARTIAL_CONTENT, LLCore::HE_SUCCESS)); + static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); partial = (par_status == status); } diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 53b0f7885f..4ee13d171e 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -308,9 +308,9 @@ private: // Interfaces and objects into the core http library used // to make our HTTP requests. These replace the various // LLCurl interfaces used in the past. - LLCore::HttpRequest * mHttpRequest; - LLCore::HttpOptions * mHttpOptions; - LLCore::HttpHeaders * mHttpHeaders; + LLCore::HttpRequest * mHttpRequest; // Ttf + LLCore::HttpOptions * mHttpOptions; // Ttf + LLCore::HttpHeaders * mHttpHeaders; // Ttf typedef std::set wait_http_res_queue_t; wait_http_res_queue_t mHttpWaitResource; // Mfnq -- cgit v1.2.3 From f4a59854c5aab0fb1f666d8df45984a0f4cfd465 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 11 Jun 2012 12:24:54 -0400 Subject: Unit test fixups. Mac/Linux detect memory leak due to llwarns, try/catch cleanup. Our logging holds on to a changing bit of memory between operations and the memory leak detection I'm using senses this and complains. So, for now, disable the final memory check on Mac & Linux, leave it active on Windows. Solve this for real some other day. Add try/catch blocks to do cleanup in unit tests that go wrong so that we don't get a cascade of assertion failures when subsequent tests find singletons still alive. --- indra/llcorehttp/_httplibcurl.cpp | 2 +- indra/llcorehttp/tests/llcorehttp_test.cpp | 9 - indra/llcorehttp/tests/test_httprequest.hpp | 496 ++++++++++++++++------------ 3 files changed, 279 insertions(+), 228 deletions(-) diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 05b2c2be69..332b6f3856 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -242,7 +242,7 @@ HttpService::ELoopSpeed HttpLibcurl::completeRequest(CURLM * multi_handle, CURL LL_WARNS("CoreHttp") << "URL op succeeded after " << op->mPolicyRetries << " retries." << LL_ENDL; } - + op->stageFromActive(mService); op->release(); return HttpService::REQUEST_SLEEP; diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index 0ee767fdca..2d48bca443 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -130,12 +130,3 @@ void ssl_locking_callback(int mode, int type, const char * /* file */, int /* li } -#if defined(WIN32) - -int getopt(int argc, char * const argv[], const char *optstring) -{ - return -1; -} - -#endif - diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 2d91b95347..68da9e2dc7 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -110,24 +110,35 @@ void HttpRequestTestObjectType::test<1>() set_test_name("HttpRequest construction"); + HttpRequest * req = NULL; + // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); - // Get singletons created - HttpRequest::createService(); + try + { + // Get singletons created + HttpRequest::createService(); - // create a new ref counted object with an implicit reference - HttpRequest * req = new HttpRequest(); - ensure(mMemTotal < GetMemTotal()); + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure(mMemTotal < GetMemTotal()); - // release the request object - delete req; - req = NULL; + // release the request object + delete req; + req = NULL; - HttpRequest::destroyService(); + HttpRequest::destroyService(); - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); + } + catch (...) + { + delete req; + HttpRequest::destroyService(); + throw; + } } template <> template <> @@ -137,35 +148,46 @@ void HttpRequestTestObjectType::test<2>() set_test_name("HttpRequest and Null Op queued"); + HttpRequest * req = NULL; + // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); - // Get singletons created - HttpRequest::createService(); + try + { + // Get singletons created + HttpRequest::createService(); - // create a new ref counted object with an implicit reference - HttpRequest * req = new HttpRequest(); - ensure(mMemTotal < GetMemTotal()); + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure(mMemTotal < GetMemTotal()); - // Issue a NoOp - HttpHandle handle = req->requestNoOp(NULL); - ensure(handle != LLCORE_HTTP_HANDLE_INVALID); + // Issue a NoOp + HttpHandle handle = req->requestNoOp(NULL); + ensure(handle != LLCORE_HTTP_HANDLE_INVALID); - // release the request object - delete req; - req = NULL; + // release the request object + delete req; + req = NULL; - // We're still holding onto the operation which is - // sitting, unserviced, on the request queue so... - ensure(mMemTotal < GetMemTotal()); + // We're still holding onto the operation which is + // sitting, unserviced, on the request queue so... + ensure(mMemTotal < GetMemTotal()); - // Request queue should have two references: global singleton & service object - ensure("Two references to request queue", 2 == HttpRequestQueue::instanceOf()->getRefCount()); + // Request queue should have two references: global singleton & service object + ensure("Two references to request queue", 2 == HttpRequestQueue::instanceOf()->getRefCount()); - // Okay, tear it down - HttpRequest::destroyService(); - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure(mMemTotal == GetMemTotal()); + // Okay, tear it down + HttpRequest::destroyService(); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure(mMemTotal == GetMemTotal()); + } + catch (...) + { + delete req; + HttpRequest::destroyService(); + throw; + } } @@ -173,7 +195,7 @@ template <> template <> void HttpRequestTestObjectType::test<3>() { ScopedCurlInit ready; - + set_test_name("HttpRequest NoOp + Stop execution"); // Handler can be stack-allocated *if* there are no dangling @@ -184,69 +206,79 @@ void HttpRequestTestObjectType::test<3>() // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { - // Get singletons created - HttpRequest::createService(); + // Get singletons created + HttpRequest::createService(); - // Start threading early so that thread memory is invariant - // over the test. - HttpRequest::startThread(); - - // create a new ref counted object with an implicit reference - HttpRequest * req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); - - // Issue a NoOp - HttpHandle handle = req->requestNoOp(&handler); - ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); - - // Run the notification pump. - int count(0); - int limit(20); - while (count++ < limit && mHandlerCalls < 1) - { - req->update(1000); - usleep(100000); - } - ensure("Request executed in reasonable time", count < limit); - ensure("One handler invocation for request", mHandlerCalls == 1); + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a NoOp + HttpHandle handle = req->requestNoOp(&handler); + ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(20); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); - // Okay, request a shutdown of the servicing thread - handle = req->requestStopThread(&handler); - ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + // Okay, request a shutdown of the servicing thread + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); - // Run the notification pump again - count = 0; - limit = 100; - while (count++ < limit && mHandlerCalls < 2) - { - req->update(1000); - usleep(100000); - } - ensure("Second request executed in reasonable time", count < limit); - ensure("Second handler invocation", mHandlerCalls == 2); + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); - // See that we actually shutdown the thread - count = 0; - limit = 10; - while (count++ < limit && ! HttpService::isStopped()) - { - usleep(100000); - } - ensure("Thread actually stopped running", HttpService::isStopped()); + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); - // release the request object - delete req; - req = NULL; + // release the request object + delete req; + req = NULL; - // Shut down service - HttpRequest::destroyService(); + // Shut down service + HttpRequest::destroyService(); - // We have left over state so can't really say something - // definitive about memory usage at the end of this. - ensure("Two handler calls on the way out", 2 == mHandlerCalls); - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + } + catch (...) + { + delete req; + HttpRequest::destroyService(); + throw; + } } template <> template <> @@ -264,80 +296,92 @@ void HttpRequestTestObjectType::test<4>() // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; + + HttpRequest * req1 = NULL; + HttpRequest * req2 = NULL; + + try + { - // Get singletons created - HttpRequest::createService(); + // Get singletons created + HttpRequest::createService(); - // Start threading early so that thread memory is invariant - // over the test. - HttpRequest::startThread(); - - // create a new ref counted object with an implicit reference - HttpRequest * req1 = new HttpRequest(); - HttpRequest * req2 = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); - - // Issue some NoOps - HttpHandle handle = req1->requestNoOp(&handler1); - ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); - handler1.mExpectHandle = handle; - - handle = req2->requestNoOp(&handler2); - ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); - handler2.mExpectHandle = handle; - - // Run the notification pump. - int count(0); - int limit(20); - while (count++ < limit && mHandlerCalls < 2) - { - req1->update(1000); - req2->update(1000); - usleep(100000); - } - ensure("Request executed in reasonable time", count < limit); - ensure("One handler invocation for request", mHandlerCalls == 2); + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req1 = new HttpRequest(); + req2 = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue some NoOps + HttpHandle handle = req1->requestNoOp(&handler1); + ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + handler1.mExpectHandle = handle; + + handle = req2->requestNoOp(&handler2); + ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + handler2.mExpectHandle = handle; + + // Run the notification pump. + int count(0); + int limit(20); + while (count++ < limit && mHandlerCalls < 2) + { + req1->update(1000); + req2->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 2); - // Okay, request a shutdown of the servicing thread - handle = req2->requestStopThread(&handler2); - ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); - handler2.mExpectHandle = handle; + // Okay, request a shutdown of the servicing thread + handle = req2->requestStopThread(&handler2); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + handler2.mExpectHandle = handle; - // Run the notification pump again - count = 0; - limit = 100; - while (count++ < limit && mHandlerCalls < 3) - { - req1->update(1000); - req2->update(1000); - usleep(100000); - } - ensure("Second request executed in reasonable time", count < limit); - ensure("Second handler invocation", mHandlerCalls == 3); + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 3) + { + req1->update(1000); + req2->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 3); - // See that we actually shutdown the thread - count = 0; - limit = 10; - while (count++ < limit && ! HttpService::isStopped()) - { - usleep(100000); - } - ensure("Thread actually stopped running", HttpService::isStopped()); + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); - // release the request object - delete req1; - req1 = NULL; - delete req2; - req2 = NULL; - - // Shut down service - HttpRequest::destroyService(); + // release the request object + delete req1; + req1 = NULL; + delete req2; + req2 = NULL; + + // Shut down service + HttpRequest::destroyService(); - // We have left over state so can't really say something - // definitive about memory usage at the end of this. - ensure("Two handler calls on the way out", 3 == mHandlerCalls); - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + ensure("Two handler calls on the way out", 3 == mHandlerCalls); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + } + catch (...) + { + delete req1; + delete req2; + HttpRequest::destroyService(); + throw; + } } template <> template <> @@ -356,77 +400,93 @@ void HttpRequestTestObjectType::test<5>() mMemTotal = GetMemTotal(); mHandlerCalls = 0; - // Get singletons created - HttpRequest::createService(); - - // Start threading early so that thread memory is invariant - // over the test. - HttpRequest::startThread(); - - // create a new ref counted object with an implicit reference - HttpRequest * req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); - - // Issue a GET that can't connect - mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); - HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, - 0U, - "http://127.0.0.1:2/nothing/here", - 0, - 0, - NULL, - NULL, - &handler); - ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); - - // Run the notification pump. - int count(0); - int limit(180); // With retries, can take more than 10 seconds to give up - while (count++ < limit && mHandlerCalls < 1) + HttpRequest * req = NULL; + + try { - req->update(1000); - usleep(100000); - } - ensure("Request executed in reasonable time", count < limit); - ensure("One handler invocation for request", mHandlerCalls == 1); + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that can't connect + mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + "http://127.0.0.1:2/nothing/here", + 0, + 0, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(180); // With retries, can take more than 10 seconds to give up + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); - // Okay, request a shutdown of the servicing thread - mStatus = HttpStatus(); - handle = req->requestStopThread(&handler); - ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); - // Run the notification pump again - count = 0; - limit = 100; - while (count++ < limit && mHandlerCalls < 2) - { - req->update(1000); - usleep(100000); - } - ensure("Second request executed in reasonable time", count < limit); - ensure("Second handler invocation", mHandlerCalls == 2); + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); - // See that we actually shutdown the thread - count = 0; - limit = 10; - while (count++ < limit && ! HttpService::isStopped()) - { - usleep(100000); - } - ensure("Thread actually stopped running", HttpService::isStopped()); + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); - // release the request object - delete req; - req = NULL; + ensure("Two handler calls on the way out", 2 == mHandlerCalls); - // Shut down service - HttpRequest::destroyService(); +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... - // We have left over state so can't really say something - // definitive about memory usage at the end of this. - ensure("Two handler calls on the way out", 2 == mHandlerCalls); - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + delete req; + HttpRequest::destroyService(); + throw; + } } } // end namespace tut -- cgit v1.2.3 From 89187229dd630845177ecd7a16e2b9cb01dc47ce Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 11 Jun 2012 15:28:06 -0400 Subject: Refactoring of the request completion thread and removal of 206/content-range hack in xport. Retry/response handling is decided in policy so moved that there. Removed special case 206-without-content-range response in transport. Have this sitation recognizable in the API and let callers deal with it as needed. --- indra/llcorehttp/_httplibcurl.cpp | 51 +++++++------------------------------ indra/llcorehttp/_httplibcurl.h | 4 +-- indra/llcorehttp/_httpoprequest.cpp | 19 +++----------- indra/llcorehttp/_httppolicy.cpp | 42 ++++++++++++++++++++++++++++++ indra/llcorehttp/_httppolicy.h | 10 ++++++++ indra/newview/lltexturefetch.cpp | 9 +++++-- 6 files changed, 72 insertions(+), 63 deletions(-) diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 332b6f3856..e134a28401 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -113,8 +113,11 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() CURL * handle(msg->easy_handle); CURLcode result(msg->data.result); - HttpService::ELoopSpeed speed(completeRequest(mMultiHandles[policy_class], handle, result)); - ret = (std::min)(ret, speed); + if (completeRequest(mMultiHandles[policy_class], handle, result)) + { + // Request is still active, don't get too sleepy + ret = (std::min)(ret, HttpService::NORMAL); + } handle = NULL; // No longer valid on return } else if (CURLMSG_NONE == msg->msg) @@ -161,12 +164,8 @@ void HttpLibcurl::addOp(HttpOpRequest * op) } -HttpService::ELoopSpeed HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status) +bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status) { - static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); - static const HttpStatus cant_res_proxy(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_PROXY); - static const HttpStatus cant_res_host(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST); - HttpOpRequest * op(NULL); curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); // *FIXME: check the pointer @@ -209,43 +208,11 @@ HttpService::ELoopSpeed HttpLibcurl::completeRequest(CURLM * multi_handle, CURL curl_multi_remove_handle(multi_handle, handle); curl_easy_cleanup(handle); op->mCurlHandle = NULL; - - // Retry or finalize - if (! op->mStatus) - { - // If this failed, we might want to retry. Have to inspect - // the status a little more deeply for those reasons worth retrying... - if (op->mPolicyRetries < op->mPolicyRetryLimit && - ((op->mStatus.isHttpStatus() && op->mStatus.mType >= 499 && op->mStatus.mType <= 599) || - cant_connect == op->mStatus || - cant_res_proxy == op->mStatus || - cant_res_host == op->mStatus)) - { - // Okay, worth a retry. We include 499 in this test as - // it's the old 'who knows?' error from many grid services... - HttpPolicy & policy(mService->getPolicy()); - - policy.retryOp(op); - return HttpService::NORMAL; // Having pushed to retry, keep things running - } - } - // This op is done, finalize it delivering it to the reply queue... - if (! op->mStatus) - { - LL_WARNS("CoreHttp") << "URL op failed after " << op->mPolicyRetries - << " retries. Reason: " << op->mStatus.toString() - << LL_ENDL; - } - else if (op->mPolicyRetries) - { - LL_WARNS("CoreHttp") << "URL op succeeded after " << op->mPolicyRetries << " retries." - << LL_ENDL; - } + HttpPolicy & policy(mService->getPolicy()); + bool still_active(policy.stageAfterCompletion(op)); - op->stageFromActive(mService); - op->release(); - return HttpService::REQUEST_SLEEP; + return still_active; } diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index fe628b9ab0..16b68bde43 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -83,9 +83,7 @@ public: protected: /// Invoked when libcurl has indicated a request has been processed /// to completion and we need to move the request to a new state. - HttpService::ELoopSpeed completeRequest(CURLM * multi_handle, - CURL * handle, - CURLcode status); + bool completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status); protected: typedef std::set active_set_t; diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 895629c514..ea0b99303e 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -144,6 +144,8 @@ HttpOpRequest::~HttpOpRequest() mCurlHeaders = NULL; } + mReplyOffset = 0; + mReplyLength = 0; if (mReplyBody) { mReplyBody->release(); @@ -211,26 +213,11 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) response->setStatus(mStatus); response->setBody(mReplyBody); response->setHeaders(mReplyHeaders); - unsigned int offset(0), length(0); if (mReplyOffset || mReplyLength) { // Got an explicit offset/length in response - offset = mReplyOffset; - length = mReplyLength; - } - else if (mReplyBody && partial_content == mStatus) - { - // Legacy grid services did not provide a 'Content-Range' - // header in responses to full- or partly-satisfyiable - // 'Range' requests. For these, we have to hope that - // the data starts where requested and the length is simply - // whatever we received. A bit of sanity could be provided - // by overlapping ranged requests and verifying that the - // overlap matches. - offset = mReqOffset; - length = mReplyBody->size(); + response->setRange(mReplyOffset, mReplyLength); } - response->setRange(offset, length); mLibraryHandler->onCompleted(static_cast(this), response); diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 1f4cd34a4b..72bb6f14e4 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -185,5 +185,47 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior return false; } +bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) +{ + static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); + static const HttpStatus cant_res_proxy(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_PROXY); + static const HttpStatus cant_res_host(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST); + + // Retry or finalize + if (! op->mStatus) + { + // If this failed, we might want to retry. Have to inspect + // the status a little more deeply for those reasons worth retrying... + if (op->mPolicyRetries < op->mPolicyRetryLimit && + ((op->mStatus.isHttpStatus() && op->mStatus.mType >= 499 && op->mStatus.mType <= 599) || + cant_connect == op->mStatus || + cant_res_proxy == op->mStatus || + cant_res_host == op->mStatus)) + { + // Okay, worth a retry. We include 499 in this test as + // it's the old 'who knows?' error from many grid services... + retryOp(op); + return true; // still active/ready + } + } + + // This op is done, finalize it delivering it to the reply queue... + if (! op->mStatus) + { + LL_WARNS("CoreHttp") << "URL op failed after " << op->mPolicyRetries + << " retries. Reason: " << op->mStatus.toString() + << LL_ENDL; + } + else if (op->mPolicyRetries) + { + LL_WARNS("CoreHttp") << "URL op succeeded after " << op->mPolicyRetries << " retries." + << LL_ENDL; + } + + op->stageFromActive(mService); + op->release(); + return false; // not active +} + } // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 6f18264f3d..14f6a9a676 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -79,6 +79,16 @@ public: // Shadows HttpService's method bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); + /// When transport is finished with an op and takes it off the + /// active queue, it is delivered here for dispatch. Policy + /// may send it back to the ready/retry queues if it needs another + /// go or we may finalize it and send it on to the reply queue. + /// + /// @return Returns true of the request is still active + /// or ready after staging, false if has been + /// sent on to the reply queue. + bool stageAfterCompletion(HttpOpRequest * op); + // Get pointer to global policy options. Caller is expected // to do context checks like no setting once running. HttpPolicyGlobal & getGlobalOptions() diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 4a46ea0e97..571f9ab3b5 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1692,8 +1692,8 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe << " status: " << status.toHex() << " '" << status.toString() << "'" << llendl; - unsigned int offset(0), length(0); - response->getRange(&offset, &length); +// unsigned int offset(0), length(0); +// response->getRange(&offset, &length); // llwarns << "HTTP COMPLETE: " << mID << " handle: " << handle // << " status: " << status.toULong() << " '" << status.toString() << "'" // << " req offset: " << mRequestedOffset << " req length: " << mRequestedSize @@ -1710,6 +1710,11 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe } else { + // A warning about partial (HTTP 206) data. Some grid services + // do *not* return a 'Content-Range' header in the response to + // Range requests with a 206 status. We're forced to assume + // we get what we asked for in these cases until we can fix + // the services. static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); partial = (par_status == status); -- cgit v1.2.3 From 267ab5b417eaef64a170d69ad83334df9d566ed9 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 11 Jun 2012 17:47:04 -0400 Subject: Convert BufferArray interfaces to void * (not char *). HttpRequest::update() honor time limit. Generally, opaque data operations are expected to be over 'void *' and have now converted interfaces to do that. Update() method honors millisecond limit to dwell time. Might want to homologate the millis/uSecs mix later.... --- indra/llcorehttp/bufferarray.cpp | 37 ++++++++++++++++------------- indra/llcorehttp/bufferarray.h | 8 +++---- indra/llcorehttp/httprequest.cpp | 13 ++++------ indra/llcorehttp/httprequest.h | 2 +- indra/llcorehttp/tests/test_bufferarray.hpp | 6 ++--- 5 files changed, 34 insertions(+), 32 deletions(-) diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index 36a8006bce..6d5309a043 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -108,10 +108,11 @@ BufferArray::~BufferArray() } -size_t BufferArray::append(const char * src, size_t len) +size_t BufferArray::append(const void * src, size_t len) { const size_t ret(len); - + const char * c_src(static_cast(src)); + // First, try to copy into the last block if (len && ! mBlocks.empty()) { @@ -121,11 +122,11 @@ size_t BufferArray::append(const char * src, size_t len) // Some will fit... const size_t copy_len((std::min)(len, (last.mAlloced - last.mUsed))); - memcpy(&last.mData[last.mUsed], src, copy_len); + memcpy(&last.mData[last.mUsed], c_src, copy_len); last.mUsed += copy_len; llassert_always(last.mUsed <= last.mAlloced); mLen += copy_len; - src += copy_len; + c_src += copy_len; len -= copy_len; } } @@ -140,19 +141,19 @@ size_t BufferArray::append(const char * src, size_t len) mBlocks.reserve(mBlocks.size() + 5); } Block * block = Block::alloc(BLOCK_ALLOC_SIZE); - memcpy(block->mData, src, copy_len); + memcpy(block->mData, c_src, copy_len); block->mUsed = copy_len; llassert_always(block->mUsed <= block->mAlloced); mBlocks.push_back(block); mLen += copy_len; - src += copy_len; + c_src += copy_len; len -= copy_len; } return ret; } -char * BufferArray::appendBufferAlloc(size_t len) +void * BufferArray::appendBufferAlloc(size_t len) { // If someone asks for zero-length, we give them a valid pointer. if (mBlocks.size() >= mBlocks.capacity()) @@ -167,8 +168,10 @@ char * BufferArray::appendBufferAlloc(size_t len) } -size_t BufferArray::read(size_t pos, char * dst, size_t len) +size_t BufferArray::read(size_t pos, void * dst, size_t len) { + char * c_dst(static_cast(dst)); + if (pos >= mLen) return 0; size_t len_limit(mLen - pos); @@ -188,10 +191,10 @@ size_t BufferArray::read(size_t pos, char * dst, size_t len) size_t block_limit(block.mUsed - offset); size_t block_len(std::min(block_limit, len)); - memcpy(dst, &block.mData[offset], block_len); + memcpy(c_dst, &block.mData[offset], block_len); result += block_len; len -= block_len; - dst += block_len; + c_dst += block_len; offset = 0; ++block_start; } @@ -201,8 +204,10 @@ size_t BufferArray::read(size_t pos, char * dst, size_t len) } -size_t BufferArray::write(size_t pos, const char * src, size_t len) +size_t BufferArray::write(size_t pos, const void * src, size_t len) { + const char * c_src(static_cast(src)); + if (pos > mLen || 0 == len) return 0; @@ -220,9 +225,9 @@ size_t BufferArray::write(size_t pos, const char * src, size_t len) size_t block_limit(block.mUsed - offset); size_t block_len(std::min(block_limit, len)); - memcpy(&block.mData[offset], src, block_len); + memcpy(&block.mData[offset], c_src, block_len); result += block_len; - src += block_len; + c_src += block_len; len -= block_len; offset = 0; ++block_start; @@ -240,12 +245,12 @@ size_t BufferArray::write(size_t pos, const char * src, size_t len) // Some will fit... const size_t copy_len((std::min)(len, (last.mAlloced - last.mUsed))); - memcpy(&last.mData[last.mUsed], src, copy_len); + memcpy(&last.mData[last.mUsed], c_src, copy_len); last.mUsed += copy_len; result += copy_len; llassert_always(last.mUsed <= last.mAlloced); mLen += copy_len; - src += copy_len; + c_src += copy_len; len -= copy_len; } } @@ -254,7 +259,7 @@ size_t BufferArray::write(size_t pos, const char * src, size_t len) { // Some or all of the remaining write data will // be an append. - result += append(src, len); + result += append(c_src, len); } return result; diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h index 9ccd85d4f8..72c3e1c669 100644 --- a/indra/llcorehttp/bufferarray.h +++ b/indra/llcorehttp/bufferarray.h @@ -81,7 +81,7 @@ public: /// position is one beyond the final byte of the buffer. /// /// @return Count of bytes copied to BufferArray - size_t append(const char * src, size_t len); + size_t append(const void * src, size_t len); /// Similar to @see append(), this call guarantees a /// contiguous block of memory of requested size placed @@ -91,7 +91,7 @@ public: /// /// @return Pointer to contiguous region at end /// of BufferArray of 'len' size. - char * appendBufferAlloc(size_t len); + void * appendBufferAlloc(size_t len); /// Current count of bytes in BufferArray instance. size_t size() const @@ -102,13 +102,13 @@ public: /// Copies data from the given position in the instance /// to the caller's buffer. Will return a short count of /// bytes copied if the 'len' extends beyond the data. - size_t read(size_t pos, char * dst, size_t len); + size_t read(size_t pos, void * dst, size_t len); /// Copies data from the caller's buffer to the instance /// at the current position. May overwrite existing data, /// append data when current position is equal to the /// size of the instance or do a mix of both. - size_t write(size_t pos, const char * src, size_t len); + size_t write(size_t pos, const void * src, size_t len); protected: int findBlock(size_t pos, size_t * ret_offset); diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index baa0fe1a84..2f36168f8b 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -35,6 +35,8 @@ #include "_httpopsetpriority.h" #include "_httpopcancel.h" +#include "lltimer.h" + namespace { @@ -279,14 +281,9 @@ HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priori HttpStatus HttpRequest::update(long millis) { - HttpStatus status; - - // *FIXME: need timer stuff - // long now(getNow()); - // long limit(now + millis); - + const HttpTime limit(totalTime() + (1000 * HttpTime(millis))); HttpOperation * op(NULL); - while ((op = mReplyQueue->fetchOp())) + while (limit >= totalTime() && (op = mReplyQueue->fetchOp())) { // Process operation op->visitNotifier(this); @@ -295,7 +292,7 @@ HttpStatus HttpRequest::update(long millis) op->release(); } - return status; + return HttpStatus(); } diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 3592d5c6a3..01dbfba6dd 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -259,7 +259,7 @@ public: /// @param millis Maximum number of wallclock milliseconds to /// spend in the call. As hinted at above, this /// is partly a function of application code so it's - /// a soft limit. (And not currently implemented.) + /// a soft limit. /// /// @return Standard status code. HttpStatus update(long millis); diff --git a/indra/llcorehttp/tests/test_bufferarray.hpp b/indra/llcorehttp/tests/test_bufferarray.hpp index 2ad9391d1c..3f947db967 100644 --- a/indra/llcorehttp/tests/test_bufferarray.hpp +++ b/indra/llcorehttp/tests/test_bufferarray.hpp @@ -348,7 +348,7 @@ void BufferArrayTestObjectType::test<7>() ensure("Append length correct", str2_len == len); // append some more - char * out_buf(ba->appendBufferAlloc(str1_len)); + void * out_buf(ba->appendBufferAlloc(str1_len)); memcpy(out_buf, str1, str1_len); // And some final writes @@ -399,11 +399,11 @@ void BufferArrayTestObjectType::test<8>() len = ba->write(str1_len, str1, str1_len); // zero-length allocate (we allow this with a valid pointer returned) - char * out_buf(ba->appendBufferAlloc(0)); + void * out_buf(ba->appendBufferAlloc(0)); ensure("Buffer from zero-length appendBufferAlloc non-NULL", NULL != out_buf); // Do it again - char * out_buf2(ba->appendBufferAlloc(0)); + void * out_buf2(ba->appendBufferAlloc(0)); ensure("Buffer from zero-length appendBufferAlloc non-NULL.2", NULL != out_buf2); ensure("Two zero-length appendBufferAlloc buffers distinct", out_buf != out_buf2); -- cgit v1.2.3 From 75242eab8f8a892c792681fca080d86cfbb3e061 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 11 Jun 2012 19:06:52 -0400 Subject: Bring in the testrunner/http server scaffold for better integration testing. This brings in a copy of llmessage's llsdmessage testing server. We run a mocked HTTP service to handle requests and the integration tests run against it by picking up the LL_TEST_PORT environment variable when running. Add some checks and output to produce useful info when run in the wrong environment and when bad status is received. Later will add a dead port as well so we can test that rather than use 'localhost:2'. --- indra/llcorehttp/CMakeLists.txt | 2 + indra/llcorehttp/tests/llcorehttp_test.cpp | 36 ++++ indra/llcorehttp/tests/llcorehttp_test.h | 5 + indra/llcorehttp/tests/test_httprequest.hpp | 119 ++++++++++- indra/llcorehttp/tests/test_llcorehttp_peer.py | 146 ++++++++++++++ indra/llcorehttp/tests/testrunner.py | 262 +++++++++++++++++++++++++ 6 files changed, 568 insertions(+), 2 deletions(-) create mode 100644 indra/llcorehttp/tests/test_llcorehttp_peer.py create mode 100644 indra/llcorehttp/tests/testrunner.py diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 3fda524ddf..a0827286e3 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -125,6 +125,8 @@ if (LL_TESTS) LL_ADD_INTEGRATION_TEST(llcorehttp "${llcorehttp_TEST_SOURCE_FILES}" "${test_libs}" + ${PYTHON_EXECUTABLE} + "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llcorehttp_peer.py" ) endif (LL_TESTS) diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index 2d48bca443..f59361ab53 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -27,6 +27,7 @@ #include "llcorehttp_test.h" #include +#include // These are not the right way in viewer for some reason: // #include @@ -130,3 +131,38 @@ void ssl_locking_callback(int mode, int type, const char * /* file */, int /* li } +std::string get_base_url() +{ + const char * env(getenv("LL_TEST_PORT")); + + if (! env) + { + std::cerr << "LL_TEST_PORT environment variable missing." << std::endl; + std::cerr << "Test expects to run in test_llcorehttp_peer.py script." << std::endl; + tut::ensure("LL_TEST_PORT set in environment", NULL != env); + } + + int port(atoi(env)); + std::ostringstream out; + out << "http://localhost:" << port << "/"; + return out.str(); +} + + +void stop_thread(LLCore::HttpRequest * req) +{ + if (req) + { + req->requestStopThread(NULL); + + int count = 0; + int limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + req->update(1000); + usleep(100000); + } + } +} + + diff --git a/indra/llcorehttp/tests/llcorehttp_test.h b/indra/llcorehttp/tests/llcorehttp_test.h index 1550881a00..a9567435ce 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.h +++ b/indra/llcorehttp/tests/llcorehttp_test.h @@ -32,6 +32,9 @@ #include #include +#include + +#include "httprequest.h" // Initialization and cleanup for libcurl. Mainly provides // a mutex callback for SSL and a thread ID hash for libcurl. @@ -40,6 +43,8 @@ // operations. extern void init_curl(); extern void term_curl(); +extern std::string get_base_url(); +extern void stop_thread(LLCore::HttpRequest * req); class ScopedCurlInit { diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 68da9e2dc7..81f8fe4a85 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -84,8 +84,10 @@ public: if (response && mState) { const HttpStatus actual_status(response->getStatus()); - - ensure("Expected HttpStatus received in response", actual_status == mState->mStatus); + std::ostringstream test; + test << "Expected HttpStatus received in response. Wanted: " + << mState->mStatus.toHex() << " Received: " << actual_status.toHex(); + ensure(test.str().c_str(), actual_status == mState->mStatus); } if (mState) { @@ -184,6 +186,7 @@ void HttpRequestTestObjectType::test<2>() } catch (...) { + stop_thread(req); delete req; HttpRequest::destroyService(); throw; @@ -275,6 +278,7 @@ void HttpRequestTestObjectType::test<3>() } catch (...) { + stop_thread(req); delete req; HttpRequest::destroyService(); throw; @@ -377,6 +381,7 @@ void HttpRequestTestObjectType::test<4>() } catch (...) { + stop_thread(req1); delete req1; delete req2; HttpRequest::destroyService(); @@ -483,6 +488,116 @@ void HttpRequestTestObjectType::test<5>() } catch (...) { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + +template <> template <> +void HttpRequestTestObjectType::test<6>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET to real service"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + 0, + 0, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); delete req; HttpRequest::destroyService(); throw; diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py new file mode 100644 index 0000000000..3e200a5c19 --- /dev/null +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python +"""\ +@file test_llsdmessage_peer.py +@author Nat Goodspeed +@date 2008-10-09 +@brief This script asynchronously runs the executable (with args) specified on + the command line, returning its result code. While that executable is + running, we provide dummy local services for use by C++ tests. + +$LicenseInfo:firstyear=2008&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 + +Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +$/LicenseInfo$ +""" + +import os +import sys +from threading import Thread +from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler + +mydir = os.path.dirname(__file__) # expected to be .../indra/llmessage/tests/ +sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python")) +from indra.util.fastest_elementtree import parse as xml_parse +from indra.base import llsd +from testrunner import freeport, run, debug, VERBOSE + +class TestHTTPRequestHandler(BaseHTTPRequestHandler): + """This subclass of BaseHTTPRequestHandler is to receive and echo + LLSD-flavored messages sent by the C++ LLHTTPClient. + """ + def read(self): + # The following logic is adapted from the library module + # SimpleXMLRPCServer.py. + # Get arguments by reading body of request. + # We read this in chunks to avoid straining + # socket.read(); around the 10 or 15Mb mark, some platforms + # begin to have problems (bug #792570). + try: + size_remaining = int(self.headers["content-length"]) + except (KeyError, ValueError): + return "" + max_chunk_size = 10*1024*1024 + L = [] + while size_remaining: + chunk_size = min(size_remaining, max_chunk_size) + chunk = self.rfile.read(chunk_size) + L.append(chunk) + size_remaining -= len(chunk) + return ''.join(L) + # end of swiped read() logic + + def read_xml(self): + # This approach reads the entire POST data into memory first + return llsd.parse(self.read()) +## # This approach attempts to stream in the LLSD XML from self.rfile, +## # assuming that the underlying XML parser reads its input file +## # incrementally. Unfortunately I haven't been able to make it work. +## tree = xml_parse(self.rfile) +## debug("Finished raw parse") +## debug("parsed XML tree %s", tree) +## debug("parsed root node %s", tree.getroot()) +## debug("root node tag %s", tree.getroot().tag) +## return llsd.to_python(tree.getroot()) + + def do_GET(self): + # Of course, don't attempt to read data. + self.answer(dict(reply="success", status=200, + reason="Your GET operation worked")) + + def do_POST(self): + # Read the provided POST data. + self.answer(self.read()) + + def answer(self, data): + debug("%s.answer(%s): self.path = %r", self.__class__.__name__, data, self.path) + if "fail" not in self.path: + response = llsd.format_xml(data.get("reply", llsd.LLSD("success"))) + debug("success: %s", response) + self.send_response(200) + self.send_header("Content-type", "application/llsd+xml") + self.send_header("Content-Length", str(len(response))) + self.end_headers() + self.wfile.write(response) + else: # fail requested + status = data.get("status", 500) + # self.responses maps an int status to a (short, long) pair of + # strings. We want the longer string. That's why we pass a string + # pair to get(): the [1] will select the second string, whether it + # came from self.responses or from our default pair. + reason = data.get("reason", + self.responses.get(status, + ("fail requested", + "Your request specified failure status %s " + "without providing a reason" % status))[1]) + debug("fail requested: %s: %r", status, reason) + self.send_error(status, reason) + + if not VERBOSE: + # When VERBOSE is set, skip both these overrides because they exist to + # suppress output. + + def log_request(self, code, size=None): + # For present purposes, we don't want the request splattered onto + # stderr, as it would upset devs watching the test run + pass + + def log_error(self, format, *args): + # Suppress error output as well + pass + +class Server(HTTPServer): + # This pernicious flag is on by default in HTTPServer. But proper + # operation of freeport() absolutely depends on it being off. + allow_reuse_address = False + +if __name__ == "__main__": + # Instantiate a Server(TestHTTPRequestHandler) on the first free port + # in the specified port range. Doing this inline is better than in a + # daemon thread: if it blows up here, we'll get a traceback. If it blew up + # in some other thread, the traceback would get eaten and we'd run the + # subject test program anyway. + httpd, port = freeport(xrange(8000, 8020), + lambda port: Server(('127.0.0.1', port), TestHTTPRequestHandler)) + # Pass the selected port number to the subject test program via the + # environment. We don't want to impose requirements on the test program's + # command-line parsing -- and anyway, for C++ integration tests, that's + # performed in TUT code rather than our own. + os.environ["LL_TEST_PORT"] = str(port) + debug("$LL_TEST_PORT = %s", port) + sys.exit(run(server=Thread(name="httpd", target=httpd.serve_forever), *sys.argv[1:])) diff --git a/indra/llcorehttp/tests/testrunner.py b/indra/llcorehttp/tests/testrunner.py new file mode 100644 index 0000000000..f2c841532a --- /dev/null +++ b/indra/llcorehttp/tests/testrunner.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python +"""\ +@file testrunner.py +@author Nat Goodspeed +@date 2009-03-20 +@brief Utilities for writing wrapper scripts for ADD_COMM_BUILD_TEST unit tests + +$LicenseInfo:firstyear=2009&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 + +Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +$/LicenseInfo$ +""" + +from __future__ import with_statement + +import os +import sys +import re +import errno +import socket + +VERBOSE = os.environ.get("INTEGRATION_TEST_VERBOSE", "1") # default to verbose +# Support usage such as INTEGRATION_TEST_VERBOSE=off -- distressing to user if +# that construct actually turns on verbosity... +VERBOSE = not re.match(r"(0|off|false|quiet)$", VERBOSE, re.IGNORECASE) + +if VERBOSE: + def debug(fmt, *args): + print fmt % args + sys.stdout.flush() +else: + debug = lambda *args: None + +def freeport(portlist, expr): + """ + Find a free server port to use. Specifically, evaluate 'expr' (a + callable(port)) until it stops raising EADDRINUSE exception. + + Pass: + + portlist: an iterable (e.g. xrange()) of ports to try. If you exhaust the + range, freeport() lets the socket.error exception propagate. If you want + unbounded, you could pass itertools.count(baseport), though of course in + practice the ceiling is 2^16-1 anyway. But it seems prudent to constrain + the range much more sharply: if we're iterating an absurd number of times, + probably something else is wrong. + + expr: a callable accepting a port number, specifically one of the items + from portlist. If calling that callable raises socket.error with + EADDRINUSE, freeport() retrieves the next item from portlist and retries. + + Returns: (expr(port), port) + + port: the value from portlist for which expr(port) succeeded + + Raises: + + Any exception raised by expr(port) other than EADDRINUSE. + + socket.error if, for every item from portlist, expr(port) raises + socket.error. The exception you see is the one from the last item in + portlist. + + StopIteration if portlist is completely empty. + + Example: + + class Server(HTTPServer): + # If you use BaseHTTPServer.HTTPServer, turning off this flag is + # essential for proper operation of freeport()! + allow_reuse_address = False + # ... + server, port = freeport(xrange(8000, 8010), + lambda port: Server(("localhost", port), + MyRequestHandler)) + # pass 'port' to client code + # call server.serve_forever() + """ + try: + # If portlist is completely empty, let StopIteration propagate: that's an + # error because we can't return meaningful values. We have no 'port', + # therefore no 'expr(port)'. + portiter = iter(portlist) + port = portiter.next() + + while True: + try: + # If this value of port works, return as promised. + value = expr(port) + + except socket.error, err: + # Anything other than 'Address already in use', propagate + if err.args[0] != errno.EADDRINUSE: + raise + + # Here we want the next port from portiter. But on StopIteration, + # we want to raise the original exception rather than + # StopIteration. So save the original exc_info(). + type, value, tb = sys.exc_info() + try: + try: + port = portiter.next() + except StopIteration: + raise type, value, tb + finally: + # Clean up local traceback, see docs for sys.exc_info() + del tb + + else: + debug("freeport() returning %s on port %s", value, port) + return value, port + + # Recap of the control flow above: + # If expr(port) doesn't raise, return as promised. + # If expr(port) raises anything but EADDRINUSE, propagate that + # exception. + # If portiter.next() raises StopIteration -- that is, if the port + # value we just passed to expr(port) was the last available -- reraise + # the EADDRINUSE exception. + # If we've actually arrived at this point, portiter.next() delivered a + # new port value. Loop back to pass that to expr(port). + + except Exception, err: + debug("*** freeport() raising %s: %s", err.__class__.__name__, err) + raise + +def run(*args, **kwds): + """All positional arguments collectively form a command line, executed as + a synchronous child process. + In addition, pass server=new_thread_instance as an explicit keyword (to + differentiate it from an additional command-line argument). + new_thread_instance should be an instantiated but not yet started Thread + subclass instance, e.g.: + run("python", "-c", 'print "Hello, world!"', server=TestHTTPServer(name="httpd")) + """ + # If there's no server= keyword arg, don't start a server thread: simply + # run a child process. + try: + thread = kwds.pop("server") + except KeyError: + pass + else: + # Start server thread. Note that this and all other comm server + # threads should be daemon threads: we'll let them run "forever," + # confident that the whole process will terminate when the main thread + # terminates, which will be when the child process terminates. + thread.setDaemon(True) + thread.start() + # choice of os.spawnv(): + # - [v vs. l] pass a list of args vs. individual arguments, + # - [no p] don't use the PATH because we specifically want to invoke the + # executable passed as our first arg, + # - [no e] child should inherit this process's environment. + debug("Running %s...", " ".join(args)) + rc = os.spawnv(os.P_WAIT, args[0], args) + debug("%s returned %s", args[0], rc) + return rc + +# **************************************************************************** +# test code -- manual at this point, see SWAT-564 +# **************************************************************************** +def test_freeport(): + # ------------------------------- Helpers -------------------------------- + from contextlib import contextmanager + # helper Context Manager for expecting an exception + # with exc(SomeError): + # raise SomeError() + # raises AssertionError otherwise. + @contextmanager + def exc(exception_class, *args): + try: + yield + except exception_class, err: + for i, expected_arg in enumerate(args): + assert expected_arg == err.args[i], \ + "Raised %s, but args[%s] is %r instead of %r" % \ + (err.__class__.__name__, i, err.args[i], expected_arg) + print "Caught expected exception %s(%s)" % \ + (err.__class__.__name__, ', '.join(repr(arg) for arg in err.args)) + else: + assert False, "Failed to raise " + exception_class.__class__.__name__ + + # helper to raise specified exception + def raiser(exception): + raise exception + + # the usual + def assert_equals(a, b): + assert a == b, "%r != %r" % (a, b) + + # ------------------------ Sanity check the above ------------------------ + class SomeError(Exception): pass + # Without extra args, accept any err.args value + with exc(SomeError): + raiser(SomeError("abc")) + # With extra args, accept only the specified value + with exc(SomeError, "abc"): + raiser(SomeError("abc")) + with exc(AssertionError): + with exc(SomeError, "abc"): + raiser(SomeError("def")) + with exc(AssertionError): + with exc(socket.error, errno.EADDRINUSE): + raiser(socket.error(errno.ECONNREFUSED, 'Connection refused')) + + # ----------- freeport() without engaging socket functionality ----------- + # If portlist is empty, freeport() raises StopIteration. + with exc(StopIteration): + freeport([], None) + + assert_equals(freeport([17], str), ("17", 17)) + + # This is the magic exception that should prompt us to retry + inuse = socket.error(errno.EADDRINUSE, 'Address already in use') + # Get the iterator to our ports list so we can check later if we've used all + ports = iter(xrange(5)) + with exc(socket.error, errno.EADDRINUSE): + freeport(ports, lambda port: raiser(inuse)) + # did we entirely exhaust 'ports'? + with exc(StopIteration): + ports.next() + + ports = iter(xrange(2)) + # Any exception but EADDRINUSE should quit immediately + with exc(SomeError): + freeport(ports, lambda port: raiser(SomeError())) + assert_equals(ports.next(), 1) + + # ----------- freeport() with platform-dependent socket stuff ------------ + # This is what we should've had unit tests to begin with (see CHOP-661). + def newbind(port): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind(('127.0.0.1', port)) + return sock + + bound0, port0 = freeport(xrange(7777, 7780), newbind) + assert_equals(port0, 7777) + bound1, port1 = freeport(xrange(7777, 7780), newbind) + assert_equals(port1, 7778) + bound2, port2 = freeport(xrange(7777, 7780), newbind) + assert_equals(port2, 7779) + with exc(socket.error, errno.EADDRINUSE): + bound3, port3 = freeport(xrange(7777, 7780), newbind) + +if __name__ == "__main__": + test_freeport() -- cgit v1.2.3 From 24e16e1632974057013b86300bb60954ea6f5684 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 12 Jun 2012 12:41:09 -0400 Subject: Convert _refcounted.h over to using LLAtomic32<>. Beware of bad documentation. operator--(int) does not return what the header claimed it did. --- indra/llcommon/llapr.h | 2 +- indra/llcorehttp/_refcounted.h | 52 ++++++++++++++-------------------------- indra/llcorehttp/bufferarray.cpp | 6 ++--- 3 files changed, 22 insertions(+), 38 deletions(-) diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h index af33ce666f..034546c3f3 100644 --- a/indra/llcommon/llapr.h +++ b/indra/llcommon/llapr.h @@ -168,7 +168,7 @@ public: void operator -=(Type x) { apr_atomic_sub32(&mData, apr_uint32_t(x)); } void operator +=(Type x) { apr_atomic_add32(&mData, apr_uint32_t(x)); } Type operator ++(int) { return apr_atomic_inc32(&mData); } // Type++ - Type operator --(int) { return apr_atomic_dec32(&mData); } // Type-- + Type operator --(int) { return apr_atomic_dec32(&mData); } // approximately --Type (0 if final is 0, non-zero otherwise) private: apr_uint32_t mData; diff --git a/indra/llcorehttp/_refcounted.h b/indra/llcorehttp/_refcounted.h index 72cef6b342..a96c65fb6b 100644 --- a/indra/llcorehttp/_refcounted.h +++ b/indra/llcorehttp/_refcounted.h @@ -28,9 +28,11 @@ #define LLCOREINT__REFCOUNTED_H_ +#include "linden_common.h" + #include -#include "linden_common.h" +#include "llapr.h" namespace LLCoreInt @@ -44,7 +46,7 @@ private: void operator=(const RefCounted &); // Not defined public: - explicit RefCounted(bool const implicit) + explicit RefCounted(bool const implicit) : mRefCount(implicit) {} @@ -52,42 +54,34 @@ public: void addRef() const; void release() const; bool isLastRef() const; - int getRefCount() const; + S32 getRefCount() const; void noRef() const; - static const int NOT_REF_COUNTED = -1; + static const S32 NOT_REF_COUNTED = -1; protected: virtual ~RefCounted(); virtual void destroySelf(); private: - mutable int mRefCount; - mutable boost::mutex mRefLock; + mutable LLAtomicS32 mRefCount; }; // end class RefCounted inline void RefCounted::addRef() const { - boost::mutex::scoped_lock lock(mRefLock); - llassert_always(mRefCount >= 0); - ++mRefCount; + S32 count(mRefCount++); + llassert_always(count >= 0); } inline void RefCounted::release() const { - int count(0); - { - // CRITICAL SECTION - boost::mutex::scoped_lock lock(mRefLock); - llassert_always(mRefCount != NOT_REF_COUNTED); - llassert_always(mRefCount > 0); - count = --mRefCount; - // CRITICAL SECTION - } - + S32 count(mRefCount); + llassert_always(count != NOT_REF_COUNTED); + llassert_always(count > 0); + count = mRefCount--; // clean ourselves up if that was the last reference if (0 == count) @@ -99,32 +93,22 @@ inline void RefCounted::release() const inline bool RefCounted::isLastRef() const { - int count(0); - { - // CRITICAL SECTION - boost::mutex::scoped_lock lock(mRefLock); - - llassert_always(mRefCount != NOT_REF_COUNTED); - llassert_always(mRefCount >= 1); - count = mRefCount; - // CRITICAL SECTION - } - + const S32 count(mRefCount); + llassert_always(count != NOT_REF_COUNTED); + llassert_always(count >= 1); return (1 == count); } -inline int RefCounted::getRefCount() const +inline S32 RefCounted::getRefCount() const { - boost::mutex::scoped_lock lock(mRefLock); - const int result(mRefCount); + const S32 result(mRefCount); return result; } inline void RefCounted::noRef() const { - boost::mutex::scoped_lock lock(mRefLock); llassert_always(mRefCount <= 1); mRefCount = NOT_REF_COUNTED; } diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index 6d5309a043..ae92057df0 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -175,7 +175,7 @@ size_t BufferArray::read(size_t pos, void * dst, size_t len) if (pos >= mLen) return 0; size_t len_limit(mLen - pos); - len = std::min(len, len_limit); + len = (std::min)(len, len_limit); if (0 == len) return 0; @@ -189,7 +189,7 @@ size_t BufferArray::read(size_t pos, void * dst, size_t len) { Block & block(*mBlocks[block_start]); size_t block_limit(block.mUsed - offset); - size_t block_len(std::min(block_limit, len)); + size_t block_len((std::min)(block_limit, len)); memcpy(c_dst, &block.mData[offset], block_len); result += block_len; @@ -223,7 +223,7 @@ size_t BufferArray::write(size_t pos, const void * src, size_t len) { Block & block(*mBlocks[block_start]); size_t block_limit(block.mUsed - offset); - size_t block_len(std::min(block_limit, len)); + size_t block_len((std::min)(block_limit, len)); memcpy(&block.mData[offset], c_src, block_len); result += block_len; -- cgit v1.2.3 From 7adeb3923728ca84a309a6af141c148ce38066fc Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 12 Jun 2012 17:42:33 -0400 Subject: HTTP Proxy, PUT & POST, unit tests and refactoring. Implemented/modified PUT & POST to not used chunked encoding for the request. Made the unit test much happier and probably a better thing for the pipeline. Have a cheesy static & dynamic proxy capability using both local options and a way to wire into LLProxy in llmessages. Not a clean thing but it will get the proxy path working with both socks5 & http proxies. Refactoring to get rid of unneeded library handler and unified an HttpStatus return for all requests. Big batch of code removed as a result of that and more is possible as well as some syscall avoidance with a bit more work. Boosted the unit tests for simple PUT & POST test which revealed the test harness does *not* like chunked encoding so we'll avoid it for now (and don't really need it in any of our schemes). --- indra/llcorehttp/CMakeLists.txt | 5 + indra/llcorehttp/_httpopcancel.cpp | 11 -- indra/llcorehttp/_httpopcancel.h | 3 - indra/llcorehttp/_httpoperation.cpp | 18 +- indra/llcorehttp/_httpoperation.h | 13 +- indra/llcorehttp/_httpoprequest.cpp | 63 +++++-- indra/llcorehttp/_httpoprequest.h | 7 + indra/llcorehttp/_httpopsetget.cpp | 105 +++++++++++ indra/llcorehttp/_httpopsetget.h | 78 ++++++++ indra/llcorehttp/_httpopsetpriority.cpp | 14 -- indra/llcorehttp/_httpopsetpriority.h | 4 +- indra/llcorehttp/_httppolicy.cpp | 6 + indra/llcorehttp/_httppolicy.h | 4 +- indra/llcorehttp/_httppolicyglobal.cpp | 38 ++-- indra/llcorehttp/_httppolicyglobal.h | 7 +- indra/llcorehttp/_httpservice.cpp | 11 +- indra/llcorehttp/_httpservice.h | 10 +- indra/llcorehttp/httprequest.cpp | 151 ++++++++-------- indra/llcorehttp/httprequest.h | 44 ++++- indra/llcorehttp/tests/llcorehttp_test.cpp | 6 + indra/llcorehttp/tests/test_httpoperation.hpp | 5 +- indra/llcorehttp/tests/test_httprequest.hpp | 239 +++++++++++++++++++++++++ indra/llcorehttp/tests/test_llcorehttp_peer.py | 10 +- indra/newview/llappviewer.cpp | 13 ++ 24 files changed, 702 insertions(+), 163 deletions(-) create mode 100644 indra/llcorehttp/_httpopsetget.cpp create mode 100644 indra/llcorehttp/_httpopsetget.h diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index a0827286e3..4273b32fe3 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -10,12 +10,14 @@ include(OpenSSL) include(ZLIB) include(LLCoreHttp) include(LLAddBuildTest) +include(LLMessage) include(LLCommon) include(Tut) include_directories (${CMAKE_CURRENT_SOURCE_DIR}) include_directories( + ${LLMESSAGE_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} ${LLCOREHTTP_INCLUDE_DIRS} ) @@ -31,6 +33,7 @@ set(llcorehttp_SOURCE_FILES _httpopcancel.cpp _httpoperation.cpp _httpoprequest.cpp + _httpopsetget.cpp _httpopsetpriority.cpp _httppolicy.cpp _httppolicyglobal.cpp @@ -54,6 +57,7 @@ set(llcorehttp_HEADER_FILES _httpopcancel.h _httpoperation.h _httpoprequest.h + _httpopsetget.h _httpopsetpriority.h _httppolicy.h _httppolicyglobal.h @@ -113,6 +117,7 @@ if (LL_TESTS) set(test_libs ${LLCOREHTTP_LIBRARIES} ${WINDOWS_LIBRARIES} + ${LLMESSAGE_LIBRARIES} ${LLCOMMON_LIBRARIES} ${GOOGLEMOCK_LIBRARIES} ${CURL_LIBRARIES} diff --git a/indra/llcorehttp/_httpopcancel.cpp b/indra/llcorehttp/_httpopcancel.cpp index 69dbff4bb4..ad624d2e57 100644 --- a/indra/llcorehttp/_httpopcancel.cpp +++ b/indra/llcorehttp/_httpopcancel.cpp @@ -66,17 +66,6 @@ void HttpOpCancel::stageFromRequest(HttpService * service) } -void HttpOpCancel::visitNotifier(HttpRequest * request) -{ - if (mLibraryHandler) - { - HttpResponse * response = new HttpResponse(); - mLibraryHandler->onCompleted(static_cast(this), response); - response->release(); - } -} - - } // end namespace LLCore diff --git a/indra/llcorehttp/_httpopcancel.h b/indra/llcorehttp/_httpopcancel.h index fab6f1f362..6d1e0f8774 100644 --- a/indra/llcorehttp/_httpopcancel.h +++ b/indra/llcorehttp/_httpopcancel.h @@ -59,13 +59,10 @@ private: public: virtual void stageFromRequest(HttpService *); - - virtual void visitNotifier(HttpRequest * request); public: // Request data HttpHandle mHandle; - }; // end class HttpOpCancel diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index d966efd12b..b5c58013d4 100644 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -47,7 +47,6 @@ namespace LLCore HttpOperation::HttpOperation() : LLCoreInt::RefCounted(true), mReplyQueue(NULL), - mLibraryHandler(NULL), mUserHandler(NULL), mReqPolicy(HttpRequest::DEFAULT_POLICY_ID), mReqPriority(0U) @@ -57,13 +56,12 @@ HttpOperation::HttpOperation() HttpOperation::~HttpOperation() { - setHandlers(NULL, NULL, NULL); + setReplyPath(NULL, NULL); } -void HttpOperation::setHandlers(HttpReplyQueue * reply_queue, - HttpHandler * lib_handler, - HttpHandler * user_handler) +void HttpOperation::setReplyPath(HttpReplyQueue * reply_queue, + HttpHandler * user_handler) { if (reply_queue != mReplyQueue) { @@ -80,9 +78,6 @@ void HttpOperation::setHandlers(HttpReplyQueue * reply_queue, mReplyQueue = reply_queue; } - // Not refcounted - mLibraryHandler = lib_handler; - // Not refcounted mUserHandler = user_handler; } @@ -121,11 +116,12 @@ void HttpOperation::stageFromActive(HttpService *) void HttpOperation::visitNotifier(HttpRequest *) { - if (mLibraryHandler) + if (mUserHandler) { HttpResponse * response = new HttpResponse(); - mLibraryHandler->onCompleted(static_cast(this), response); + response->setStatus(mStatus); + mUserHandler->onCompleted(static_cast(this), response); response->release(); } @@ -142,7 +138,7 @@ HttpStatus HttpOperation::cancel() void HttpOperation::addAsReply() { - if (mReplyQueue && mLibraryHandler) + if (mReplyQueue) { addRef(); mReplyQueue->addOp(this); diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index 01e26029d2..c93aa2def9 100644 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -80,9 +80,8 @@ private: void operator=(const HttpOperation &); // Not defined public: - void setHandlers(HttpReplyQueue * reply_queue, - HttpHandler * lib_handler, - HttpHandler * user_handler); + void setReplyPath(HttpReplyQueue * reply_queue, + HttpHandler * handler); HttpHandler * getUserHandler() const { @@ -102,13 +101,15 @@ protected: protected: HttpReplyQueue * mReplyQueue; // Have refcount - HttpHandler * mLibraryHandler; // Have refcount - HttpHandler * mUserHandler; // Have refcount + HttpHandler * mUserHandler; public: + // Request Data HttpRequest::policy_t mReqPolicy; HttpRequest::priority_t mReqPriority; - + + // Reply Data + HttpStatus mStatus; }; // end class HttpOperation diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index ea0b99303e..e2550d057e 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -44,6 +44,7 @@ #include "_httplibcurl.h" #include "llhttpstatuscodes.h" +#include "llproxy.h" namespace { @@ -207,7 +208,7 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) { static const HttpStatus partial_content(HTTP_PARTIAL_CONTENT, HE_SUCCESS); - if (mLibraryHandler) + if (mUserHandler) { HttpResponse * response = new HttpResponse(); response->setStatus(mStatus); @@ -219,7 +220,7 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) response->setRange(mReplyOffset, mReplyLength); } - mLibraryHandler->onCompleted(static_cast(this), response); + mUserHandler->onCompleted(static_cast(this), response); response->release(); } @@ -304,6 +305,39 @@ HttpStatus HttpOpRequest::setupPost(HttpRequest::policy_t policy_id, } +HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers) +{ + HttpStatus status; + + mProcFlags = 0; + mReqPolicy = policy_id; + mReqPriority = priority; + mReqMethod = HOR_PUT; + mReqURL = url; + if (body) + { + body->addRef(); + mReqBody = body; + } + if (headers && ! mReqHeaders) + { + headers->addRef(); + mReqHeaders = headers; + } + if (options && ! mReqOptions) + { + mReqOptions = new HttpOptions(*options); + } + + return status; +} + + HttpStatus HttpOpRequest::prepareRequest(HttpService * service) { // Scrub transport and result data for retried op case @@ -346,8 +380,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str()); curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this); curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); - // *FIXME: Need to deal with proxy setup... - // curl_easy_setopt(handle, CURLOPT_PROXY, ""); // *FIXME: Revisit this old DNS timeout setting - may no longer be valid curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); @@ -361,18 +393,31 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1); curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0); - std::string opt_value; + const std::string * opt_value(NULL); if (policy.get(HttpRequest::GP_CA_PATH, opt_value)) { - curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value.c_str()); + curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value->c_str()); } if (policy.get(HttpRequest::GP_CA_FILE, opt_value)) { - curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value.c_str()); + curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value->c_str()); } if (policy.get(HttpRequest::GP_HTTP_PROXY, opt_value)) { - curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value.c_str()); + if (*opt_value == "LLProxy") + { + // Use the viewer-based thread-safe API which has a + // fast/safe check for proxy enable. Would like to + // encapsulate this someway... + LLProxy::getInstance()->applyProxySettings(mCurlHandle); + } + else + { + // *TODO: This is fine for now but get fuller socks/ + // authentication thing going later.... + curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value->c_str()); + curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + } } switch (mReqMethod) @@ -394,7 +439,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, static_cast(NULL)); curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDSIZE, data_size); - mCurlHeaders = curl_slist_append(mCurlHeaders, "Transfer-Encoding: chunked"); mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); } break; @@ -409,7 +453,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size); curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL); - mCurlHeaders = curl_slist_append(mCurlHeaders, "Transfer-Encoding: chunked"); mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 6dcf30ca0c..80893beb40 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -91,6 +91,13 @@ public: HttpOptions * options, HttpHeaders * headers); + HttpStatus setupPut(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers); + HttpStatus prepareRequest(HttpService * service); virtual HttpStatus cancel(); diff --git a/indra/llcorehttp/_httpopsetget.cpp b/indra/llcorehttp/_httpopsetget.cpp new file mode 100644 index 0000000000..21e058b2be --- /dev/null +++ b/indra/llcorehttp/_httpopsetget.cpp @@ -0,0 +1,105 @@ +/** + * @file _httpopsetget.cpp + * @brief Definitions for internal class HttpOpSetGet + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpopsetget.h" + +#include +#include + +#include "httpcommon.h" +#include "httphandler.h" +#include "httpresponse.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" +#include "_httppolicy.h" +#include "_httplibcurl.h" + + +namespace LLCore +{ + + +// ================================== +// HttpOpSetget +// ================================== + + +HttpOpSetGet::HttpOpSetGet() + : HttpOperation(), + mIsGlobal(false), + mDoSet(false), + mSetting(-1), // Nothing requested + mLongValue(0L) +{} + + +HttpOpSetGet::~HttpOpSetGet() +{} + + +void HttpOpSetGet::setupGet(HttpRequest::EGlobalPolicy setting) +{ + mIsGlobal = true; + mSetting = setting; +} + + +void HttpOpSetGet::setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value) +{ + mIsGlobal = true; + mDoSet = true; + mSetting = setting; + mStrValue = value; +} + + +void HttpOpSetGet::stageFromRequest(HttpService * service) +{ + HttpPolicyGlobal & pol_opt(service->getPolicy().getGlobalOptions()); + HttpRequest::EGlobalPolicy setting(static_cast(mSetting)); + + if (mDoSet) + { + mStatus = pol_opt.set(setting, mStrValue); + } + if (mStatus) + { + const std::string * value; + if ((mStatus = pol_opt.get(setting, value))) + { + mStrValue = *value; + } + } + + addAsReply(); +} + + +} // end namespace LLCore + + diff --git a/indra/llcorehttp/_httpopsetget.h b/indra/llcorehttp/_httpopsetget.h new file mode 100644 index 0000000000..e065eb4c30 --- /dev/null +++ b/indra/llcorehttp/_httpopsetget.h @@ -0,0 +1,78 @@ +/** + * @file _httpopsetget.h + * @brief Internal declarations for the HttpOpSetGet subclass + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_OPSETGET_H_ +#define _LLCORE_HTTP_OPSETGET_H_ + + +#include "linden_common.h" // Modifies curl/curl.h interfaces + +#include "httpcommon.h" + +#include + +#include "_httpoperation.h" +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// HttpOpSetGet requests dynamic changes to policy and +/// configuration settings. + +class HttpOpSetGet : public HttpOperation +{ +public: + HttpOpSetGet(); + virtual ~HttpOpSetGet(); + +private: + HttpOpSetGet(const HttpOpSetGet &); // Not defined + void operator=(const HttpOpSetGet &); // Not defined + +public: + void setupGet(HttpRequest::EGlobalPolicy setting); + void setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value); + + virtual void stageFromRequest(HttpService *); + +public: + // Request data + bool mIsGlobal; + bool mDoSet; + int mSetting; + long mLongValue; + std::string mStrValue; + +}; // end class HttpOpSetGet + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_OPSETGET_H_ + diff --git a/indra/llcorehttp/_httpopsetpriority.cpp b/indra/llcorehttp/_httpopsetpriority.cpp index b0ee577087..d48c7a0b7d 100644 --- a/indra/llcorehttp/_httpopsetpriority.cpp +++ b/indra/llcorehttp/_httpopsetpriority.cpp @@ -60,18 +60,4 @@ void HttpOpSetPriority::stageFromRequest(HttpService * service) } -void HttpOpSetPriority::visitNotifier(HttpRequest * request) -{ - if (mLibraryHandler) - { - HttpResponse * response = new HttpResponse(); - - response->setStatus(mStatus); - mLibraryHandler->onCompleted(static_cast(this), response); - - response->release(); - } -} - - } // end namespace LLCore diff --git a/indra/llcorehttp/_httpopsetpriority.h b/indra/llcorehttp/_httpopsetpriority.h index b972f50fff..f1e94b6e43 100644 --- a/indra/llcorehttp/_httpopsetpriority.h +++ b/indra/llcorehttp/_httpopsetpriority.h @@ -56,10 +56,8 @@ private: public: virtual void stageFromRequest(HttpService *); - virtual void visitNotifier(HttpRequest * request); - protected: - HttpStatus mStatus; + // Request Data HttpHandle mHandle; HttpRequest::priority_t mPriority; }; // end class HttpOpSetPriority diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 72bb6f14e4..8ee3f88658 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -71,6 +71,12 @@ HttpPolicy::~HttpPolicy() } +void HttpPolicy::setPolicies(const HttpPolicyGlobal & global) +{ + mGlobalOptions = global; +} + + void HttpPolicy::addOp(HttpOpRequest * op) { const int policy_class(op->mReqPolicy); diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 14f6a9a676..73c22bab78 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -95,7 +95,9 @@ public: { return mGlobalOptions; } - + + void setPolicies(const HttpPolicyGlobal & global); + protected: struct State { diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp index 877b85896f..d95d73cfba 100644 --- a/indra/llcorehttp/_httppolicyglobal.cpp +++ b/indra/llcorehttp/_httppolicyglobal.cpp @@ -32,7 +32,7 @@ namespace LLCore HttpPolicyGlobal::HttpPolicyGlobal() - : mValidMask(0UL), + : mSetMask(0UL), mConnectionLimit(32L) {} @@ -41,6 +41,20 @@ HttpPolicyGlobal::~HttpPolicyGlobal() {} +HttpPolicyGlobal & HttpPolicyGlobal::operator=(const HttpPolicyGlobal & other) +{ + if (this != &other) + { + mSetMask = other.mSetMask; + mConnectionLimit = other.mConnectionLimit; + mCAPath = other.mCAPath; + mCAFile = other.mCAFile; + mHttpProxy = other.mHttpProxy; + } + return *this; +} + + HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value) { switch (opt) @@ -53,7 +67,7 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value) return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); } - mValidMask |= 1UL << int(opt); + mSetMask |= 1UL << int(opt); return HttpStatus(); } @@ -78,7 +92,7 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::stri return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); } - mValidMask |= 1UL << int(opt); + mSetMask |= 1UL << int(opt); return HttpStatus(); } @@ -90,7 +104,7 @@ HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long & value) switch (opt) { case HttpRequest::GP_CONNECTION_LIMIT: - if (! (mValidMask & (1UL << int(opt)))) + if (! (mSetMask & (1UL << int(opt)))) return not_set; value = mConnectionLimit; break; @@ -103,28 +117,28 @@ HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long & value) } -HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, std::string & value) +HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, const std::string *& value) { static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); - + switch (opt) { case HttpRequest::GP_CA_PATH: - if (! (mValidMask & (1UL << int(opt)))) + if (! (mSetMask & (1UL << int(opt)))) return not_set; - value = mCAPath; + value = &mCAPath; break; case HttpRequest::GP_CA_FILE: - if (! (mValidMask & (1UL << int(opt)))) + if (! (mSetMask & (1UL << int(opt)))) return not_set; - value = mCAFile; + value = &mCAFile; break; case HttpRequest::GP_HTTP_PROXY: - if (! (mValidMask & (1UL << int(opt)))) + if (! (mSetMask & (1UL << int(opt)))) return not_set; - value = mHttpProxy; + value = &mHttpProxy; break; default: diff --git a/indra/llcorehttp/_httppolicyglobal.h b/indra/llcorehttp/_httppolicyglobal.h index 39ffbcb9bb..f4bb4d4b25 100644 --- a/indra/llcorehttp/_httppolicyglobal.h +++ b/indra/llcorehttp/_httppolicyglobal.h @@ -40,18 +40,19 @@ public: HttpPolicyGlobal(); ~HttpPolicyGlobal(); + HttpPolicyGlobal & operator=(const HttpPolicyGlobal &); + private: HttpPolicyGlobal(const HttpPolicyGlobal &); // Not defined - void operator=(const HttpPolicyGlobal &); // Not defined public: HttpStatus set(HttpRequest::EGlobalPolicy opt, long value); HttpStatus set(HttpRequest::EGlobalPolicy opt, const std::string & value); HttpStatus get(HttpRequest::EGlobalPolicy opt, long & value); - HttpStatus get(HttpRequest::EGlobalPolicy opt, std::string & value); + HttpStatus get(HttpRequest::EGlobalPolicy opt, const std::string *& value); public: - unsigned long mValidMask; + unsigned long mSetMask; long mConnectionLimit; std::string mCAPath; std::string mCAFile; diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index b038bdb720..920a3f3b6d 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -79,11 +79,8 @@ HttpService::~HttpService() mTransport = NULL; } - if (mPolicy) - { - delete mPolicy; - mPolicy = NULL; - } + delete mPolicy; + mPolicy = NULL; if (mThread) { @@ -145,6 +142,10 @@ void HttpService::startThread() { mThread->release(); } + + // Push current policy definitions + mPolicy->setPolicies(mPolicyGlobal); + mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1)); mThread->addRef(); // Need an explicit reference, implicit one is used internally sState = RUNNING; diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index 748354a8e4..3f953ec1a7 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -30,6 +30,7 @@ #include "httpcommon.h" #include "httprequest.h" +#include "_httppolicyglobal.h" namespace LLCoreInt @@ -157,6 +158,11 @@ public: { return *mTransport; } + + HttpPolicyGlobal & getGlobalOptions() + { + return mPolicyGlobal; + } protected: void threadRun(LLCoreInt::HttpThread * thread); @@ -173,11 +179,11 @@ protected: // === calling-thread-only data === LLCoreInt::HttpThread * mThread; - + HttpPolicyGlobal mPolicyGlobal; + // === working-thread-only data === HttpPolicy * mPolicy; // Simple pointer, has ownership HttpLibcurl * mTransport; // Simple pointer, has ownership - }; // end class HttpService } // end namespace LLCore diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 2f36168f8b..089eee76f3 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -34,6 +34,7 @@ #include "_httpoprequest.h" #include "_httpopsetpriority.h" #include "_httpopcancel.h" +#include "_httpopsetget.h" #include "lltimer.h" @@ -48,39 +49,6 @@ bool has_inited(false); namespace LLCore { -// ==================================== -// InternalHandler Implementation -// ==================================== - - -class HttpRequest::InternalHandler : public HttpHandler -{ -public: - InternalHandler(HttpRequest & request) - : mRequest(request) - {} - -protected: - InternalHandler(const InternalHandler &); // Not defined - void operator=(const InternalHandler &); // Not defined - -public: - void onCompleted(HttpHandle handle, HttpResponse * response) - { - HttpOperation * op(static_cast(handle)); - HttpHandler * user_handler(op->getUserHandler()); - if (user_handler) - { - user_handler->onCompleted(handle, response); - } - } - -protected: - HttpRequest & mRequest; - -}; // end class HttpRequest::InternalHandler - - // ==================================== // HttpRequest Implementation // ==================================== @@ -92,15 +60,12 @@ HttpRequest::policy_t HttpRequest::sNextPolicyID(1); HttpRequest::HttpRequest() : //HttpHandler(), mReplyQueue(NULL), - mRequestQueue(NULL), - mSelfHandler(NULL) + mRequestQueue(NULL) { mRequestQueue = HttpRequestQueue::instanceOf(); mRequestQueue->addRef(); mReplyQueue = new HttpReplyQueue(); - - mSelfHandler = new InternalHandler(*this); } @@ -117,9 +82,6 @@ HttpRequest::~HttpRequest() mReplyQueue->release(); mReplyQueue = NULL; } - - delete mSelfHandler; - mSelfHandler = NULL; } @@ -132,7 +94,7 @@ HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value) { // *FIXME: Fail if thread is running. - return HttpService::instanceOf()->getPolicy().getGlobalOptions().set(opt, value); + return HttpService::instanceOf()->getGlobalOptions().set(opt, value); } @@ -140,7 +102,7 @@ HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, const std::stri { // *FIXME: Fail if thread is running. - return HttpService::instanceOf()->getPolicy().getGlobalOptions().set(opt, value); + return HttpService::instanceOf()->getGlobalOptions().set(opt, value); } @@ -192,7 +154,7 @@ HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id, mLastReqStatus = status; return handle; } - op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + op->setReplyPath(mReplyQueue, user_handler); mRequestQueue->addOp(op); // transfers refcount mLastReqStatus = status; @@ -220,7 +182,7 @@ HttpHandle HttpRequest::requestPost(policy_t policy_id, mLastReqStatus = status; return handle; } - op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + op->setReplyPath(mReplyQueue, user_handler); mRequestQueue->addOp(op); // transfers refcount mLastReqStatus = status; @@ -230,19 +192,31 @@ HttpHandle HttpRequest::requestPost(policy_t policy_id, } -HttpHandle HttpRequest::requestCancel(HttpHandle handle, HttpHandler * user_handler) +HttpHandle HttpRequest::requestPut(policy_t policy_id, + priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * user_handler) { HttpStatus status; - HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); - - HttpOpCancel * op = new HttpOpCancel(handle); - op->setHandlers(mReplyQueue, mSelfHandler, user_handler); - mRequestQueue->addOp(op); // transfer refcount as well + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + HttpOpRequest * op = new HttpOpRequest(); + if (! (status = op->setupPut(policy_id, priority, url, body, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setReplyPath(mReplyQueue, user_handler); + mRequestQueue->addOp(op); // transfers refcount + mLastReqStatus = status; - ret_handle = static_cast(op); + handle = static_cast(op); - return ret_handle; + return handle; } @@ -252,7 +226,7 @@ HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); HttpOpNull * op = new HttpOpNull(); - op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + op->setReplyPath(mReplyQueue, user_handler); mRequestQueue->addOp(op); // transfer refcount as well mLastReqStatus = status; @@ -262,23 +236,6 @@ HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) } -HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priority, - HttpHandler * handler) -{ - HttpStatus status; - HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); - - HttpOpSetPriority * op = new HttpOpSetPriority(request, priority); - op->setHandlers(mReplyQueue, mSelfHandler, handler); - mRequestQueue->addOp(op); // transfer refcount as well - - mLastReqStatus = status; - ret_handle = static_cast(op); - - return ret_handle; -} - - HttpStatus HttpRequest::update(long millis) { const HttpTime limit(totalTime() + (1000 * HttpTime(millis))); @@ -302,6 +259,38 @@ HttpStatus HttpRequest::update(long millis) // Request Management Methods // ==================================== +HttpHandle HttpRequest::requestCancel(HttpHandle handle, HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpCancel * op = new HttpOpCancel(handle); + op->setReplyPath(mReplyQueue, user_handler); + mRequestQueue->addOp(op); // transfer refcount as well + + mLastReqStatus = status; + ret_handle = static_cast(op); + + return ret_handle; +} + + +HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priority, + HttpHandler * handler) +{ + HttpStatus status; + HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpSetPriority * op = new HttpOpSetPriority(request, priority); + op->setReplyPath(mReplyQueue, handler); + mRequestQueue->addOp(op); // transfer refcount as well + + mLastReqStatus = status; + ret_handle = static_cast(op); + + return ret_handle; +} + // ==================================== // Utility Methods @@ -350,7 +339,27 @@ HttpHandle HttpRequest::requestStopThread(HttpHandler * user_handler) HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); HttpOpStop * op = new HttpOpStop(); - op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + op->setReplyPath(mReplyQueue, user_handler); + mRequestQueue->addOp(op); // transfer refcount as well + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + +// ==================================== +// Dynamic Policy Methods +// ==================================== + +HttpHandle HttpRequest::requestSetHttpProxy(const std::string & proxy, HttpHandler * handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpSetGet * op = new HttpOpSetGet(); + op->setupSet(GP_HTTP_PROXY, proxy); + op->setReplyPath(mReplyQueue, handler); mRequestQueue->addOp(op); // transfer refcount as well mLastReqStatus = status; diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 01dbfba6dd..a953aa28d0 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -124,8 +124,8 @@ public: /// @param opt Enum of option to be set. /// @param value Desired value of option. /// @return Standard status code. - HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value); - HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value); + static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value); + static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value); /// Create a new policy class into which requests can be made. /// @@ -236,6 +236,32 @@ public: HttpHandler * handler); + /// + /// @param policy_id Default or user-defined policy class under + /// which this request is to be serviced. + /// @param priority Standard priority scheme inherited from + /// Indra code base. + /// @param url + /// @param body Byte stream to be sent as the body. No + /// further encoding or escaping will be done + /// to the content. + /// @param options (optional) + /// @param headers (optional) + /// @param handler (optional) + /// @return The handle of the request if successfully + /// queued or LLCORE_HTTP_HANDLE_INVALID if the + /// request could not be queued. In the latter + /// case, @see getStatus() will return more info. + /// + HttpHandle requestPut(policy_t policy_id, + priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * handler); + + /// Queue a NoOp request. /// The request is queued and serviced by the working thread which /// immediately processes it and returns the request to the reply @@ -325,13 +351,20 @@ public: HttpHandle requestStopThread(HttpHandler * handler); /// @} + + /// @name DynamicPolicyMethods + /// + /// @{ + + /// Request that a running transport pick up a new proxy setting. + /// An empty string will indicate no proxy is to be used. + HttpHandle requestSetHttpProxy(const std::string & proxy, HttpHandler * handler); + + /// @} protected: void generateNotification(HttpOperation * op); - class InternalHandler; - friend class InternalHandler; - private: /// @name InstanceData /// @@ -339,7 +372,6 @@ private: HttpStatus mLastReqStatus; HttpReplyQueue * mReplyQueue; HttpRequestQueue * mRequestQueue; - InternalHandler * mSelfHandler; /// @} diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index f59361ab53..2b36d3a982 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -44,6 +44,8 @@ #include "test_bufferarray.hpp" #include "test_httprequestqueue.hpp" +#include "llproxy.h" + unsigned long ssl_thread_id_callback(void); void ssl_locking_callback(int mode, int type, const char * file, int line); @@ -91,11 +93,15 @@ void init_curl() CRYPTO_set_locking_callback(ssl_locking_callback); CRYPTO_set_id_callback(ssl_thread_id_callback); } + + LLProxy::getInstance(); } void term_curl() { + LLProxy::cleanupClass(); + CRYPTO_set_locking_callback(NULL); for (int i(0); i < ssl_mutex_count; ++i) { diff --git a/indra/llcorehttp/tests/test_httpoperation.hpp b/indra/llcorehttp/tests/test_httpoperation.hpp index 6c3df1e9e3..17b1a96878 100644 --- a/indra/llcorehttp/tests/test_httpoperation.hpp +++ b/indra/llcorehttp/tests/test_httpoperation.hpp @@ -97,13 +97,12 @@ namespace tut // Get some handlers TestHandler * h1 = new TestHandler(); - TestHandler * h2 = new TestHandler(); // create a new ref counted object with an implicit reference HttpOpNull * op = new HttpOpNull(); // Add the handlers - op->setHandlers(NULL, h1, h2); + op->setReplyPath(NULL, h1); // Check ref count ensure(op->getRefCount() == 1); @@ -117,8 +116,6 @@ namespace tut // release the handlers delete h1; h1 = NULL; - delete h2; - h2 = NULL; ensure(mMemTotal == GetMemTotal()); } diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 81f8fe4a85..61698f34d8 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -27,6 +27,7 @@ #define TEST_LLCORE_HTTP_REQUEST_H_ #include "httprequest.h" +#include "bufferarray.h" #include "httphandler.h" #include "httpresponse.h" #include "_httpservice.h" @@ -604,6 +605,244 @@ void HttpRequestTestObjectType::test<6>() } } +template <> template <> +void HttpRequestTestObjectType::test<7>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest PUT to real service"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + BufferArray * body = new BufferArray; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + static const char * body_text("Now is the time for all good men..."); + body->append(body_text, strlen(body_text)); + mStatus = HttpStatus(200); + HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + body, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // Lose the request body + body->release(); + body = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + if (body) + { + body->release(); + } + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + +template <> template <> +void HttpRequestTestObjectType::test<8>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest POST to real service"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + BufferArray * body = new BufferArray; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + static const char * body_text("Now is the time for all good men..."); + body->append(body_text, strlen(body_text)); + mStatus = HttpStatus(200); + HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + body, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // Lose the request body + body->release(); + body = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + if (body) + { + body->release(); + } + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + } // end namespace tut namespace diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 3e200a5c19..8c3ad805b3 100644 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -85,7 +85,15 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): def do_POST(self): # Read the provided POST data. - self.answer(self.read()) + # self.answer(self.read()) + self.answer(dict(reply="success", status=200, + reason=self.read())) + + def do_PUT(self): + # Read the provided PUT data. + # self.answer(self.read()) + self.answer(dict(reply="success", status=200, + reason=self.read())) def answer(self, data): debug("%s.answer(%s): self.path = %r", self.__class__.__name__, data, self.path) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 7a44415fba..e2c13e77e3 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5358,6 +5358,7 @@ void CoreHttp::init() mRequest = new LLCore::HttpRequest; + // Point to our certs or SSH/https: will fail on connect status = mRequest->setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE, gDirUtilp->getCAFile()); if (! status) @@ -5366,6 +5367,18 @@ void CoreHttp::init() << status.toString() << LL_ENDL; } + + // Establish HTTP Proxy. "LLProxy" is a special string which directs + // the code to use LLProxy::applyProxySettings() to establish any + // HTTP or SOCKS proxy for http operations. + status = mRequest->setPolicyGlobalOption(LLCore::HttpRequest::GP_HTTP_PROXY, + std::string("LLProxy")); + if (! status) + { + LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } status = LLCore::HttpRequest::startThread(); if (! status) -- cgit v1.2.3 From c71e808a36adef2ced44caa03a668296a2e962ca Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 12 Jun 2012 19:46:19 -0400 Subject: Really need to figure out the 'static const' problem on Windows. For now, workaround... --- indra/llcorehttp/_refcounted.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/indra/llcorehttp/_refcounted.cpp b/indra/llcorehttp/_refcounted.cpp index 2c132e1b36..11d75fdf97 100644 --- a/indra/llcorehttp/_refcounted.cpp +++ b/indra/llcorehttp/_refcounted.cpp @@ -30,6 +30,12 @@ namespace LLCoreInt { +#if ! defined(WIN32) + +const S32 RefCounted::NOT_REF_COUNTED; + +#endif // ! defined(WIN32) + RefCounted::~RefCounted() {} -- cgit v1.2.3 From 682ae001cf3a618ff6f96469e3c1f074fbb76a4e Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 13 Jun 2012 11:46:38 -0400 Subject: Restore original priority scheme of LOW/HIGH. The NORMAL range doesn't do any sleeping at all and so we'll spin the core harder than we already are. Bring all idlers into the same range. --- indra/newview/lltexturefetch.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 571f9ab3b5..f5e7540e85 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -203,7 +203,7 @@ // Priority Scheme // // [PRIORITY_LOW, PRIORITY_NORMAL) - for WAIT_HTTP_RESOURCE state -// [PRIORITY_NORMAL, PRIORITY_HIGH) - waiting for external event +// and other wait states // [PRIORITY_HIGH, PRIORITY_URGENT) - External event delivered, // rapidly transitioning through states, // no waiting allowed @@ -1102,7 +1102,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mUrl.compare(0, 7, "file://") == 0) { - setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); // Set priority first since Responder may change it + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it // read file from local disk std::string filename = mUrl.substr(7, std::string::npos); @@ -1112,7 +1112,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } else if (mUrl.empty()) { - setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); // Set priority first since Responder may change it + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority, @@ -1241,7 +1241,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mSentRequest = QUEUED; mFetcher->addToNetworkQueue(this); recordTextureStart(false); - setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } @@ -1252,7 +1252,7 @@ bool LLTextureFetchWorker::doWork(S32 param) // Make certain this is in the network queue //mFetcher->addToNetworkQueue(this); //recordTextureStart(false); - //setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); + //setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } } @@ -1281,7 +1281,7 @@ bool LLTextureFetchWorker::doWork(S32 param) else { mFetcher->addToNetworkQueue(this); // failsafe - setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); recordTextureStart(false); } return false; @@ -1371,7 +1371,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mHttpActive = true; mFetcher->addToHTTPQueue(mID); recordTextureStart(true); - setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); mState = WAIT_HTTP_REQ; // fall through @@ -1495,7 +1495,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } else { - setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } } @@ -1504,7 +1504,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { static LLCachedControl textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled"); - setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); // Set priority first since Responder may change it + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it if (textures_decode_disabled) { // for debug use, don't decode @@ -1540,7 +1540,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mAuxImage = NULL; llassert_always(mFormattedImage.notNull()); S32 discard = mHaveAllData ? 0 : mLoadedDiscard; - U32 image_priority = LLWorkerThread::PRIORITY_NORMAL | mWorkPriority; + U32 image_priority = LLWorkerThread::PRIORITY_LOW | mWorkPriority; mDecoded = FALSE; mState = DECODE_IMAGE_UPDATE; LL_DEBUGS("Texture") << mID << ": Decoding. Bytes: " << mFormattedImage->getDataSize() << " Discard: " << discard @@ -1612,7 +1612,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } } llassert_always(datasize); - setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); // Set priority first since Responder may change it + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it U32 cache_priority = mWorkPriority; mWritten = FALSE; mState = WAIT_ON_WRITE; @@ -1654,7 +1654,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } else { - setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return true; } } -- cgit v1.2.3 From b08125a5874a89ce5210f8fb2c961ae17fb80fde Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 14 Jun 2012 16:31:48 -0400 Subject: LLMutex recursive lock, global & per-request tracing, simple GET request, LLProxy support, HttpOptions starting to work, HTTP resource waiting fixed. Non-LLThread-based threads need to do some registration or LLMutex locks taken out in these threads will not work as expected (SH-3154). We'll get a better solution later, this fixes some things for now. Tracing of operations now supported. Global and per-request (via HttpOptions) tracing levels of [0..3]. The 2 and 3 levels use libcurl's VERBOSE mode combined with CURLOPT_DEBUGFUNCTION to stream high levels of detail into the log. *Very* laggy but useful. Simple GET request supported (no Range: header). Really just a degenrate case of a ranged get but supplied an API anyway. Global option to use the LLProxy interface to setup CURL handles for either socks5 or http proxy usage. This isn't really the most encapsulated way to do this but a better solution will have to come later. The wantHeaders and tracing options are now supported in HttpOptions giving per-request controls. Big refactoring of the HTTP resource waiter in lltexturefetch. What I was doing before wasn't correct. Instead, I'm implementing the resource wait after the Semaphore model (though not using system semaphores). So instead of having a sequence like: SEND_HTTP_REQ -> WAIT_HTTP_RESOURCE -> SEND_HTTP_REQ, we now do WAIT_HTTP_RESOURCE -> WAIT_HTTP_RESOURCE2 (actual wait) -> SEND_HTTP_REQ. Works well but the prioritized filling of the corehttp library needs some performance work later. --- indra/llcommon/llthread.cpp | 7 + indra/llcommon/llthread.h | 5 + indra/llcorehttp/_httplibcurl.cpp | 24 ++- indra/llcorehttp/_httpoperation.cpp | 15 +- indra/llcorehttp/_httpoperation.h | 4 + indra/llcorehttp/_httpoprequest.cpp | 271 +++++++++++++++++++++------- indra/llcorehttp/_httpoprequest.h | 16 +- indra/llcorehttp/_httpopsetget.cpp | 4 +- indra/llcorehttp/_httppolicy.cpp | 21 ++- indra/llcorehttp/_httppolicy.h | 5 +- indra/llcorehttp/_httppolicyglobal.cpp | 52 ++++-- indra/llcorehttp/_httppolicyglobal.h | 6 +- indra/llcorehttp/_httpservice.cpp | 18 +- indra/llcorehttp/httpoptions.cpp | 18 +- indra/llcorehttp/httpoptions.h | 14 +- indra/llcorehttp/httprequest.cpp | 27 +++ indra/llcorehttp/httprequest.h | 75 +++++++- indra/llcorehttp/tests/test_httprequest.hpp | 228 ++++++++++++++++++++++- indra/newview/llappviewer.cpp | 20 +- indra/newview/lltexturefetch.cpp | 120 +++++++----- indra/newview/lltexturefetch.h | 9 + indra/newview/lltextureview.cpp | 5 +- 22 files changed, 803 insertions(+), 161 deletions(-) diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index a6ad6b125c..b27b64b26f 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -71,6 +71,13 @@ LL_COMMON_API void assert_main_thread() } } +void LLThread::registerThreadID() +{ +#if !LL_DARWIN + sThreadID = ++sIDIter; +#endif +} + // // Handed to the APR thread creation function // diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index b52e70ab2e..54af41ec59 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -88,6 +88,11 @@ public: U32 getID() const { return mID; } + // Called by threads *not* created via LLThread to register some + // internal state used by LLMutex. You must call this once early + // in the running thread to prevent collisions with the main thread. + static void registerThreadID(); + private: BOOL mPaused; diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index e134a28401..a176dd5b2a 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -159,6 +159,17 @@ void HttpLibcurl::addOp(HttpOpRequest * op) curl_multi_add_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle); op->mCurlActive = true; + if (op->mTracing > 0) + { + HttpPolicy & policy(mService->getPolicy()); + + LL_INFOS("CoreHttp") << "TRACE, ToActiveQueue, Handle: " + << static_cast(op) + << ", Actives: " << mActiveOps.size() + << ", Readies: " << policy.getReadyCount(op->mReqPolicy) + << LL_ENDL; + } + // On success, make operation active mActiveOps.insert(op); } @@ -190,10 +201,9 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode // Deactivate request op->mCurlActive = false; - // Set final status of request + // Set final status of request if it hasn't failed by other mechanisms yet if (op->mStatus) { - // Only set if it hasn't failed by other mechanisms yet op->mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, status); } if (op->mStatus) @@ -209,6 +219,16 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode curl_easy_cleanup(handle); op->mCurlHandle = NULL; + // Tracing + if (op->mTracing > 0) + { + LL_INFOS("CoreHttp") << "TRACE, RequestComplete, Handle: " + << static_cast(op) + << ", Status: " << op->mStatus.toHex() + << LL_ENDL; + } + + // Dispatch to next stage HttpPolicy & policy(mService->getPolicy()); bool still_active(policy.stageAfterCompletion(op)); diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index b5c58013d4..5a31bf90e7 100644 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -34,6 +34,8 @@ #include "_httpreplyqueue.h" #include "_httpservice.h" +#include "lltimer.h" + namespace LLCore { @@ -49,8 +51,10 @@ HttpOperation::HttpOperation() mReplyQueue(NULL), mUserHandler(NULL), mReqPolicy(HttpRequest::DEFAULT_POLICY_ID), - mReqPriority(0U) + mReqPriority(0U), + mTracing(0) { + mMetricCreated = totalTime(); } @@ -113,7 +117,7 @@ void HttpOperation::stageFromActive(HttpService *) llassert_always(false); } - + void HttpOperation::visitNotifier(HttpRequest *) { if (mUserHandler) @@ -138,6 +142,13 @@ HttpStatus HttpOperation::cancel() void HttpOperation::addAsReply() { + if (mTracing > 0) + { + LL_INFOS("CoreHttp") << "TRACE, ToReplyQueue, Handle: " + << static_cast(this) + << LL_ENDL; + } + if (mReplyQueue) { addRef(); diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index c93aa2def9..de4939a0ac 100644 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -110,6 +110,10 @@ public: // Reply Data HttpStatus mStatus; + + // Tracing, debug and metrics + HttpTime mMetricCreated; + int mTracing; }; // end class HttpOperation diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index e2550d057e..f78971d8f2 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -63,6 +63,16 @@ int parse_content_range_header(char * buffer, unsigned int * last, unsigned int * length); + +// Take data from libcurl's CURLOPT_DEBUGFUNCTION callback and +// escape and format it for a tracing line in logging. Absolutely +// anything including NULs can be in the data. If @scrub is true, +// non-printing or non-ascii characters are replaced with spaces +// otherwise a %XX form of escaping is used. +void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, + std::string & safe_line); + + #if defined(WIN32) // Not available on windows where the legacy strtok interface @@ -78,11 +88,6 @@ namespace LLCore { -// ================================== -// HttpOpRequest -// ================================== - - HttpOpRequest::HttpOpRequest() : HttpOperation(), mProcFlags(0U), @@ -237,6 +242,19 @@ HttpStatus HttpOpRequest::cancel() } +HttpStatus HttpOpRequest::setupGet(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers) +{ + setupCommon(policy_id, priority, url, NULL, options, headers); + mReqMethod = HOR_GET; + + return HttpStatus(); +} + + HttpStatus HttpOpRequest::setupGetByteRange(HttpRequest::policy_t policy_id, HttpRequest::priority_t priority, const std::string & url, @@ -245,30 +263,16 @@ HttpStatus HttpOpRequest::setupGetByteRange(HttpRequest::policy_t policy_id, HttpOptions * options, HttpHeaders * headers) { - HttpStatus status; - - mProcFlags = 0; - mReqPolicy = policy_id; - mReqPriority = priority; + setupCommon(policy_id, priority, url, NULL, options, headers); mReqMethod = HOR_GET; - mReqURL = url; mReqOffset = offset; mReqLength = len; if (offset || len) { mProcFlags |= PF_SCAN_RANGE_HEADER; } - if (headers && ! mReqHeaders) - { - headers->addRef(); - mReqHeaders = headers; - } - if (options && ! mReqOptions) - { - mReqOptions = new HttpOptions(*options); - } - return status; + return HttpStatus(); } @@ -279,29 +283,10 @@ HttpStatus HttpOpRequest::setupPost(HttpRequest::policy_t policy_id, HttpOptions * options, HttpHeaders * headers) { - HttpStatus status; - - mProcFlags = 0; - mReqPolicy = policy_id; - mReqPriority = priority; + setupCommon(policy_id, priority, url, body, options, headers); mReqMethod = HOR_POST; - mReqURL = url; - if (body) - { - body->addRef(); - mReqBody = body; - } - if (headers && ! mReqHeaders) - { - headers->addRef(); - mReqHeaders = headers; - } - if (options && ! mReqOptions) - { - mReqOptions = new HttpOptions(*options); - } - return status; + return HttpStatus(); } @@ -312,12 +297,23 @@ HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id, HttpOptions * options, HttpHeaders * headers) { - HttpStatus status; + setupCommon(policy_id, priority, url, body, options, headers); + mReqMethod = HOR_PUT; + + return HttpStatus(); +} + +void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers) +{ mProcFlags = 0; mReqPolicy = policy_id; mReqPriority = priority; - mReqMethod = HOR_PUT; mReqURL = url; if (body) { @@ -331,10 +327,14 @@ HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id, } if (options && ! mReqOptions) { - mReqOptions = new HttpOptions(*options); + options->addRef(); + mReqOptions = options; + if (options->getWantHeaders()) + { + mProcFlags |= PF_SAVE_HEADERS; + } + mTracing = (std::max)(mTracing, llclamp(options->getTrace(), 0, 3)); } - - return status; } @@ -394,30 +394,29 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0); const std::string * opt_value(NULL); - if (policy.get(HttpRequest::GP_CA_PATH, opt_value)) + long opt_long(0L); + policy.get(HttpRequest::GP_LLPROXY, &opt_long); + if (opt_long) { - curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value->c_str()); + // Use the viewer-based thread-safe API which has a + // fast/safe check for proxy enable. Would like to + // encapsulate this someway... + LLProxy::getInstance()->applyProxySettings(mCurlHandle); } - if (policy.get(HttpRequest::GP_CA_FILE, opt_value)) + else if (policy.get(HttpRequest::GP_HTTP_PROXY, &opt_value)) { - curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value->c_str()); + // *TODO: This is fine for now but get fuller socks/ + // authentication thing going later.... + curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value->c_str()); + curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); } - if (policy.get(HttpRequest::GP_HTTP_PROXY, opt_value)) + if (policy.get(HttpRequest::GP_CA_PATH, &opt_value)) { - if (*opt_value == "LLProxy") - { - // Use the viewer-based thread-safe API which has a - // fast/safe check for proxy enable. Would like to - // encapsulate this someway... - LLProxy::getInstance()->applyProxySettings(mCurlHandle); - } - else - { - // *TODO: This is fine for now but get fuller socks/ - // authentication thing going later.... - curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value->c_str()); - curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); - } + curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value->c_str()); + } + if (policy.get(HttpRequest::GP_CA_FILE, &opt_value)) + { + curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value->c_str()); } switch (mReqMethod) @@ -463,6 +462,14 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) // *FIXME: fail out here break; } + + // Tracing + if (mTracing > 1) + { + curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, mCurlHandle); + curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGFUNCTION, debugCallback); + } // There's a CURLOPT for this now... if ((mReqOffset || mReqLength) && HOR_GET == mReqMethod) @@ -621,6 +628,101 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi return hdr_size; } + +int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffer, size_t len, void * userdata) +{ + HttpOpRequest * op(NULL); + curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); + // *FIXME: check the pointer + + std::string safe_line; + std::string tag; + bool logit(false); + len = (std::min)(len, size_t(256)); // Keep things reasonable in all cases + + switch (info) + { + case CURLINFO_TEXT: + if (op->mTracing > 1) + { + tag = "TEXT"; + escape_libcurl_debug_data(buffer, len, true, safe_line); + logit = true; + } + break; + + case CURLINFO_HEADER_IN: + if (op->mTracing > 1) + { + tag = "HEADERIN"; + escape_libcurl_debug_data(buffer, len, true, safe_line); + logit = true; + } + break; + + case CURLINFO_HEADER_OUT: + if (op->mTracing > 1) + { + tag = "HEADEROUT"; + escape_libcurl_debug_data(buffer, len, true, safe_line); + logit = true; + } + break; + + case CURLINFO_DATA_IN: + if (op->mTracing > 1) + { + tag = "DATAIN"; + logit = true; + if (op->mTracing > 2) + { + escape_libcurl_debug_data(buffer, len, false, safe_line); + } + else + { + std::ostringstream out; + out << len << " Bytes"; + safe_line = out.str(); + } + } + break; + + case CURLINFO_DATA_OUT: + if (op->mTracing > 1) + { + tag = "DATAOUT"; + logit = true; + if (op->mTracing > 2) + { + escape_libcurl_debug_data(buffer, len, false, safe_line); + } + else + { + std::ostringstream out; + out << len << " Bytes"; + safe_line = out.str(); + } + } + break; + + default: + logit = false; + break; + } + + if (logit) + { + LL_INFOS("CoreHttp") << "TRACE, LibcurlDebug, Handle: " + << static_cast(op) + << ", Type: " << tag + << ", Data: " << safe_line + << LL_ENDL; + } + + return 0; +} + + } // end namespace LLCore @@ -694,6 +796,43 @@ char *strtok_r(char *str, const char *delim, char ** savestate) #endif + +void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::string & safe_line) +{ + std::string out; + len = (std::min)(len, size_t(200)); + out.reserve(3 * len); + for (int i(0); i < len; ++i) + { + unsigned char uc(static_cast(buffer[i])); + + if (uc < 32 || uc > 126) + { + if (scrub) + { + out.append(1, ' '); + } + else + { + static const char hex[] = "0123456789ABCDEF"; + char convert[4]; + + convert[0] = '%'; + convert[1] = hex[(uc >> 4) % 16]; + convert[2] = hex[uc % 16]; + convert[3] = '\0'; + out.append(convert); + } + } + else + { + out.append(1, buffer[i]); + } + } + safe_line.swap(out); +} + + } // end anonymous namespace diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 80893beb40..fc2301057c 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -76,6 +76,12 @@ public: public: // Setup Methods + HttpStatus setupGet(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers); + HttpStatus setupGetByteRange(HttpRequest::policy_t policy_id, HttpRequest::priority_t priority, const std::string & url, @@ -103,15 +109,23 @@ public: virtual HttpStatus cancel(); protected: + void setupCommon(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers); + static size_t writeCallback(void * data, size_t size, size_t nmemb, void * userdata); static size_t readCallback(void * data, size_t size, size_t nmemb, void * userdata); static size_t headerCallback(void * data, size_t size, size_t nmemb, void * userdata); + static int debugCallback(CURL *, curl_infotype info, char * buffer, size_t len, void * userdata); protected: unsigned int mProcFlags; static const unsigned int PF_SCAN_RANGE_HEADER = 0x00000001U; static const unsigned int PF_SAVE_HEADERS = 0x00000002U; - + public: // Request data EMethod mReqMethod; diff --git a/indra/llcorehttp/_httpopsetget.cpp b/indra/llcorehttp/_httpopsetget.cpp index 21e058b2be..c1357f9ae5 100644 --- a/indra/llcorehttp/_httpopsetget.cpp +++ b/indra/llcorehttp/_httpopsetget.cpp @@ -89,8 +89,8 @@ void HttpOpSetGet::stageFromRequest(HttpService * service) } if (mStatus) { - const std::string * value; - if ((mStatus = pol_opt.get(setting, value))) + const std::string * value(NULL); + if ((mStatus = pol_opt.get(setting, &value))) { mStrValue = *value; } diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 8ee3f88658..0e08d88276 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -107,6 +107,12 @@ void HttpPolicy::retryOp(HttpOpRequest * op) LL_WARNS("CoreHttp") << "URL op retry #" << op->mPolicyRetries << " being scheduled for " << delta << " uSecs from now." << LL_ENDL; + if (op->mTracing > 0) + { + LL_INFOS("CoreHttp") << "TRACE, ToRetryQueue, Handle: " + << static_cast(op) + << LL_ENDL; + } mState[policy_class].mRetryQueue.push(op); } @@ -224,8 +230,8 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) } else if (op->mPolicyRetries) { - LL_WARNS("CoreHttp") << "URL op succeeded after " << op->mPolicyRetries << " retries." - << LL_ENDL; + LL_DEBUGS("CoreHttp") << "URL op succeeded after " << op->mPolicyRetries << " retries." + << LL_ENDL; } op->stageFromActive(mService); @@ -234,4 +240,15 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) } +int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) +{ + if (policy_class < HttpRequest::POLICY_CLASS_LIMIT) + { + return (mState[policy_class].mReadyQueue.size() + + mState[policy_class].mRetryQueue.size()); + } + return 0; +} + + } // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 73c22bab78..4114f64848 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -97,7 +97,10 @@ public: } void setPolicies(const HttpPolicyGlobal & global); - + + // Get ready counts for a particular class + int getReadyCount(HttpRequest::policy_t policy_class); + protected: struct State { diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp index d95d73cfba..6b1de38fd6 100644 --- a/indra/llcorehttp/_httppolicyglobal.cpp +++ b/indra/llcorehttp/_httppolicyglobal.cpp @@ -33,7 +33,9 @@ namespace LLCore HttpPolicyGlobal::HttpPolicyGlobal() : mSetMask(0UL), - mConnectionLimit(32L) + mConnectionLimit(32L), + mTrace(0), + mUseLLProxy(0) {} @@ -50,6 +52,8 @@ HttpPolicyGlobal & HttpPolicyGlobal::operator=(const HttpPolicyGlobal & other) mCAPath = other.mCAPath; mCAFile = other.mCAFile; mHttpProxy = other.mHttpProxy; + mTrace = other.mTrace; + mUseLLProxy = other.mUseLLProxy; } return *this; } @@ -63,6 +67,14 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value) mConnectionLimit = value; break; + case HttpRequest::GP_TRACE: + mTrace = llclamp(value, 0L, 3L); + break; + + case HttpRequest::GP_LLPROXY: + mUseLLProxy = llclamp(value, 0L, 1L); + break; + default: return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); } @@ -97,54 +109,64 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::stri } -HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long & value) +HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long * value) { static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); + long * src(NULL); switch (opt) { case HttpRequest::GP_CONNECTION_LIMIT: - if (! (mSetMask & (1UL << int(opt)))) - return not_set; - value = mConnectionLimit; + src = &mConnectionLimit; + break; + + case HttpRequest::GP_TRACE: + src = &mTrace; + break; + + case HttpRequest::GP_LLPROXY: + src = &mUseLLProxy; break; default: return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); } + if (! (mSetMask & (1UL << int(opt)))) + return not_set; + + *value = *src; return HttpStatus(); } -HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, const std::string *& value) +HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, const std::string ** value) { static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); + const std::string * src(NULL); switch (opt) { case HttpRequest::GP_CA_PATH: - if (! (mSetMask & (1UL << int(opt)))) - return not_set; - value = &mCAPath; + src = &mCAPath; break; case HttpRequest::GP_CA_FILE: - if (! (mSetMask & (1UL << int(opt)))) - return not_set; - value = &mCAFile; + src = &mCAFile; break; case HttpRequest::GP_HTTP_PROXY: - if (! (mSetMask & (1UL << int(opt)))) - return not_set; - value = &mHttpProxy; + src = &mHttpProxy; break; default: return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); } + if (! (mSetMask & (1UL << int(opt)))) + return not_set; + + *value = src; return HttpStatus(); } diff --git a/indra/llcorehttp/_httppolicyglobal.h b/indra/llcorehttp/_httppolicyglobal.h index f4bb4d4b25..a50d0e4188 100644 --- a/indra/llcorehttp/_httppolicyglobal.h +++ b/indra/llcorehttp/_httppolicyglobal.h @@ -48,8 +48,8 @@ private: public: HttpStatus set(HttpRequest::EGlobalPolicy opt, long value); HttpStatus set(HttpRequest::EGlobalPolicy opt, const std::string & value); - HttpStatus get(HttpRequest::EGlobalPolicy opt, long & value); - HttpStatus get(HttpRequest::EGlobalPolicy opt, const std::string *& value); + HttpStatus get(HttpRequest::EGlobalPolicy opt, long * value); + HttpStatus get(HttpRequest::EGlobalPolicy opt, const std::string ** value); public: unsigned long mSetMask; @@ -57,6 +57,8 @@ public: std::string mCAPath; std::string mCAFile; std::string mHttpProxy; + long mTrace; + long mUseLLProxy; }; // end class HttpPolicyGlobal } // end namespace LLCore diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index 920a3f3b6d..beba8f08f4 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -36,6 +36,7 @@ #include "_thread.h" #include "lltimer.h" +#include "llthread.h" // Tuning parameters @@ -186,8 +187,10 @@ void HttpService::shutdown() void HttpService::threadRun(LLCoreInt::HttpThread * thread) { boost::this_thread::disable_interruption di; - ELoopSpeed loop(REQUEST_SLEEP); + + LLThread::registerThreadID(); + ELoopSpeed loop(REQUEST_SLEEP); while (! mExitRequested) { loop = processRequestQueue(loop); @@ -226,6 +229,19 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop) // Process operation if (! mExitRequested) { + // Setup for subsequent tracing + long tracing(0); + mPolicy->getGlobalOptions().get(HttpRequest::GP_TRACE, &tracing); + op->mTracing = (std::max)(op->mTracing, int(tracing)); + + if (op->mTracing > 0) + { + LL_INFOS("CoreHttp") << "TRACE, FromRequestQueue, Handle: " + << static_cast(op) + << LL_ENDL; + } + + // Stage op->stageFromRequest(this); } diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp index 15b505f5bf..155fbda7f1 100644 --- a/indra/llcorehttp/httpoptions.cpp +++ b/indra/llcorehttp/httpoptions.cpp @@ -33,13 +33,15 @@ namespace LLCore HttpOptions::HttpOptions() : RefCounted(true), - mWantHeaders(false) + mWantHeaders(false), + mTracing(0) {} HttpOptions::HttpOptions(const HttpOptions & rhs) : RefCounted(true), - mWantHeaders(rhs.mWantHeaders) + mWantHeaders(rhs.mWantHeaders), + mTracing(rhs.mTracing) {} @@ -47,4 +49,16 @@ HttpOptions::~HttpOptions() {} +void HttpOptions::setWantHeaders() +{ + mWantHeaders = true; +} + + +void HttpOptions::setTrace(long level) +{ + mTracing = int(level); +} + + } // end namespace LLCore diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h index 267a982dd5..0b9dfdc1de 100644 --- a/indra/llcorehttp/httpoptions.h +++ b/indra/llcorehttp/httpoptions.h @@ -67,11 +67,21 @@ protected: void operator=(const HttpOptions &); // Not defined public: + void setWantHeaders(); + bool getWantHeaders() const + { + return mWantHeaders; + } + + void setTrace(long level); + int getTrace() const + { + return mTracing; + } protected: - // *TODO: add some options bool mWantHeaders; - + long int mTracing; }; // end class HttpOptions diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 089eee76f3..2036ecfd1c 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -135,6 +135,33 @@ HttpStatus HttpRequest::getStatus() const } +HttpHandle HttpRequest::requestGet(policy_t policy_id, + priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpRequest * op = new HttpOpRequest(); + if (! (status = op->setupGet(policy_id, priority, url, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setReplyPath(mReplyQueue, user_handler); + mRequestQueue->addOp(op); // transfers refcount + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + + HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id, priority_t priority, const std::string & url, diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index a953aa28d0..4e78ed3719 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -111,10 +111,43 @@ public: /// Maximum number of connections the library will use to /// perform operations. This is somewhat soft as the underlying /// transport will cache some connections (up to 5). - GP_CONNECTION_LIMIT, ///< Takes long giving number of connections - GP_CA_PATH, ///< System path/directory where SSL certs are stored. - GP_CA_FILE, ///< System path/file containing certs. - GP_HTTP_PROXY ///< String giving host/port to use for HTTP proxy + + /// A long value setting the maximum number of connections + /// allowed over all policy classes. Note that this will be + /// a somewhat soft value. There may be an additional five + /// connections per policy class depending upon runtime + /// behavior. + GP_CONNECTION_LIMIT, + + /// String containing a system-appropriate directory name + /// where SSL certs are stored. + GP_CA_PATH, + + /// String giving a full path to a file containing SSL certs. + GP_CA_FILE, + + /// String of host/port to use as simple HTTP proxy. This is + /// going to change in the future into something more elaborate + /// that may support richer schemes. + GP_HTTP_PROXY, + + /// Long value that if non-zero enables the use of the + /// traditional LLProxy code for http/socks5 support. If + /// enabled, has priority over GP_HTTP_PROXY. + GP_LLPROXY, + + /// Long value setting the logging trace level for the + /// library. Possible values are: + /// 0 - No tracing (default) + /// 1 - Basic tracing of request start, stop and major events. + /// 2 - Connection, header and payload size information from + /// HTTP transactions. + /// 3 - Partial logging of payload itself. + /// + /// These values are also used in the trace modes for + /// individual requests in HttpOptions. Also be aware that + /// tracing tends to impact performance of the viewer. + GP_TRACE }; /// Set a parameter on a global policy option. Calls @@ -133,7 +166,7 @@ public: /// the class in other methods. If -1, an error /// occurred and @see getStatus() may provide more /// detail on the reason. - policy_t createPolicyClass(); + static policy_t createPolicyClass(); enum EClassPolicy { @@ -157,7 +190,7 @@ public: /// @param opt Enum of option to be set. /// @param value Desired value of option. /// @return Standard status code. - HttpStatus setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value); + static HttpStatus setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value); /// @} @@ -176,6 +209,36 @@ public: /// HttpStatus getStatus() const; + /// Queue a full HTTP GET request to be issued for entire entity. + /// The request is queued and serviced by the working thread and + /// notification of completion delivered to the optional HttpHandler + /// argument during @see update() calls. + /// + /// With a valid handle returned, it can be used to reference the + /// request in other requests (like cancellation) and will be an + /// argument when any HttpHandler object is invoked. + /// + /// @param policy_id Default or user-defined policy class under + /// which this request is to be serviced. + /// @param priority Standard priority scheme inherited from + /// Indra code base (U32-type scheme). + /// @param url + /// @param options (optional) + /// @param headers (optional) + /// @param handler (optional) + /// @return The handle of the request if successfully + /// queued or LLCORE_HTTP_HANDLE_INVALID if the + /// request could not be queued. In the latter + /// case, @see getStatus() will return more info. + /// + HttpHandle requestGet(policy_t policy_id, + priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * handler); + + /// Queue a full HTTP GET request to be issued with a 'Range' header. /// The request is queued and serviced by the working thread and /// notification of completion delivered to the optional HttpHandler diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 61698f34d8..5b04796c8a 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -395,7 +395,7 @@ void HttpRequestTestObjectType::test<5>() { ScopedCurlInit ready; - set_test_name("HttpRequest GET + Stop execution"); + set_test_name("HttpRequest GET to dead port + Stop execution"); // Handler can be stack-allocated *if* there are no dangling // references to it after completion of this method. @@ -496,6 +496,7 @@ void HttpRequestTestObjectType::test<5>() } } + template <> template <> void HttpRequestTestObjectType::test<6>() { @@ -517,6 +518,114 @@ void HttpRequestTestObjectType::test<6>() HttpRequest * req = NULL; + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<7>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET with Range: header to real service"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + try { // Get singletons created @@ -605,8 +714,9 @@ void HttpRequestTestObjectType::test<6>() } } + template <> template <> -void HttpRequestTestObjectType::test<7>() +void HttpRequestTestObjectType::test<8>() { ScopedCurlInit ready; @@ -725,7 +835,7 @@ void HttpRequestTestObjectType::test<7>() } template <> template <> -void HttpRequestTestObjectType::test<8>() +void HttpRequestTestObjectType::test<9>() { ScopedCurlInit ready; @@ -843,6 +953,118 @@ void HttpRequestTestObjectType::test<8>() } } +template <> template <> +void HttpRequestTestObjectType::test<10>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET with some tracing"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Enable tracing + HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + 0, + 0, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + } // end namespace tut namespace diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index e2c13e77e3..430dd89c3e 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5356,11 +5356,9 @@ void CoreHttp::init() << LL_ENDL; } - mRequest = new LLCore::HttpRequest; - // Point to our certs or SSH/https: will fail on connect - status = mRequest->setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE, - gDirUtilp->getCAFile()); + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE, + gDirUtilp->getCAFile()); if (! status) { LL_ERRS("Init") << "Failed to set CA File for HTTP services. Reason: " @@ -5371,15 +5369,22 @@ void CoreHttp::init() // Establish HTTP Proxy. "LLProxy" is a special string which directs // the code to use LLProxy::applyProxySettings() to establish any // HTTP or SOCKS proxy for http operations. - status = mRequest->setPolicyGlobalOption(LLCore::HttpRequest::GP_HTTP_PROXY, - std::string("LLProxy")); + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_LLPROXY, 1); if (! status) { LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services. Reason: " << status.toString() << LL_ENDL; } - + + // Tracing levels for library & libcurl (note that 2 & 3 are beyond spammy): + // 0 - None + // 1 - Basic start, stop simple transitions + // 2 - libcurl CURLOPT_VERBOSE mode with brief lines + // 3 - with partial data content + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 0); + + // Kick the thread status = LLCore::HttpRequest::startThread(); if (! status) { @@ -5388,6 +5393,7 @@ void CoreHttp::init() << LL_ENDL; } + mRequest = new LLCore::HttpRequest; } diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index f5e7540e85..664af02f78 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -431,6 +431,22 @@ private: void lockWorkMutex() { mWorkMutex.lock(); } void unlockWorkMutex() { mWorkMutex.unlock(); } + // Locks: Mw + void acquireHttpSemaphore() + { + llassert(! mHttpHasResource); + mHttpHasResource = true; + --mFetcher->mHttpSemaphore; + } + + // Locks: Mw + void releaseHttpSemaphore() + { + llassert(mHttpHasResource); + mHttpHasResource = false; + ++mFetcher->mHttpSemaphore; + } + private: enum e_state // mState { @@ -444,8 +460,9 @@ private: CACHE_POST, LOAD_FROM_NETWORK, LOAD_FROM_SIMULATOR, - SEND_HTTP_REQ, // Commit to sending as HTTP WAIT_HTTP_RESOURCE, // Waiting for HTTP resources + WAIT_HTTP_RESOURCE2, // Waiting for HTTP resources + SEND_HTTP_REQ, // Commit to sending as HTTP WAIT_HTTP_REQ, // Request sent, wait for completion DECODE_IMAGE, DECODE_IMAGE_UPDATE, @@ -532,7 +549,7 @@ private: bool mHttpActive; // Active request to http library unsigned int mHttpReplySize; unsigned int mHttpReplyOffset; - bool mHttpReleased; // Has been released from resource wait once + bool mHttpHasResource; // Counts against Fetcher's mHttpSemaphore }; ////////////////////////////////////////////////////////////////////////////// @@ -768,8 +785,9 @@ const char* LLTextureFetchWorker::sStateDescs[] = { "CACHE_POST", "LOAD_FROM_NETWORK", "LOAD_FROM_SIMULATOR", - "SEND_HTTP_REQ", "WAIT_HTTP_RESOURCE", + "WAIT_HTTP_RESOURCE2", + "SEND_HTTP_REQ", "WAIT_HTTP_REQ", "DECODE_IMAGE", "DECODE_IMAGE_UPDATE", @@ -836,7 +854,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mHttpActive(false), mHttpReplySize(0U), mHttpReplyOffset(0U), - mHttpReleased(true) + mHttpHasResource(false) { mCanUseNET = mUrl.empty() ; @@ -860,6 +878,10 @@ LLTextureFetchWorker::~LLTextureFetchWorker() llassert_always(!haveWork()); lockWorkMutex(); // +Mw (should be useless) + if (mHttpHasResource) + { + releaseHttpSemaphore(); + } if (mHttpActive) { // Issue a cancel on a live request... @@ -1126,7 +1148,7 @@ bool LLTextureFetchWorker::doWork(S32 param) llwarns << "Unknown URL Type: " << mUrl << llendl; } setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mState = SEND_HTTP_REQ; + mState = WAIT_HTTP_RESOURCE; } else { @@ -1223,7 +1245,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } if (mCanUseHTTP && !mUrl.empty()) { - mState = SEND_HTTP_REQ; + mState = WAIT_HTTP_RESOURCE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); if(mWriteToCacheState != NOT_WRITE) { @@ -1287,31 +1309,38 @@ bool LLTextureFetchWorker::doWork(S32 param) return false; } - if (mState == SEND_HTTP_REQ) + if (mState == WAIT_HTTP_RESOURCE) { - if (! mCanUseHTTP) - { - return true; // abort - } - // NOTE: // control the number of the http requests issued for: // 1, not openning too many file descriptors at the same time; // 2, control the traffic of http so udp gets bandwidth. // - if (! mHttpReleased) + // If it looks like we're busy, keep this request here. + // Otherwise, advance into the HTTP states. + if (mFetcher->mHttpSemaphore <= 0 || mFetcher->getHttpWaitersCount()) { - // If this request hasn't been released before and it looks like - // we're busy, put this request into resource wait and allow something - // else to come to the front. - if (mFetcher->getNumHTTPRequests() >= HTTP_REQUESTS_IN_QUEUE_HIGH_WATER || - mFetcher->getHttpWaitersCount()) - { - mState = WAIT_HTTP_RESOURCE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - mFetcher->addHttpWaiter(this->mID); - return false; - } + mState = WAIT_HTTP_RESOURCE2; + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + mFetcher->addHttpWaiter(this->mID); + return false; + } + mState = SEND_HTTP_REQ; + acquireHttpSemaphore(); + } + + if (mState == WAIT_HTTP_RESOURCE2) + { + // Just idle it if we make it to the head... + return false; + } + + if (mState == SEND_HTTP_REQ) + { + if (! mCanUseHTTP) + { + releaseHttpSemaphore(); + return true; // abort } mFetcher->removeFromNetworkQueue(this, false); @@ -1327,10 +1356,12 @@ bool LLTextureFetchWorker::doWork(S32 param) // We already have all the data, just decode it mLoadedDiscard = mFormattedImage->getDiscardLevel(); mState = DECODE_IMAGE; + releaseHttpSemaphore(); return false; } else { + releaseHttpSemaphore(); return true; // abort. } } @@ -1365,6 +1396,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { llwarns << "HTTP GET request failed for " << mID << llendl; resetFormattedData(); + releaseHttpSemaphore(); return true; // failed } @@ -1377,13 +1409,6 @@ bool LLTextureFetchWorker::doWork(S32 param) // fall through } - if (mState == WAIT_HTTP_RESOURCE) - { - // Nothing to do until releaseHttpWaiters() puts us back - // into the flow... - return false; - } - if (mState == WAIT_HTTP_REQ) { if (mLoaded) @@ -1401,6 +1426,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mState = INIT; mCanUseHTTP = false; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + releaseHttpSemaphore(); return false; } } @@ -1423,12 +1449,14 @@ bool LLTextureFetchWorker::doWork(S32 param) // Use available data mLoadedDiscard = mFormattedImage->getDiscardLevel(); mState = DECODE_IMAGE; + releaseHttpSemaphore(); return false; } // Fail harder resetFormattedData(); mState = DONE; + releaseHttpSemaphore(); return true; // failed } @@ -1443,6 +1471,7 @@ bool LLTextureFetchWorker::doWork(S32 param) // abort. mState = DONE; + releaseHttpSemaphore(); return true; } @@ -1491,6 +1520,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mWriteToCacheState = SHOULD_WRITE ; } setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + releaseHttpSemaphore(); return false; } else @@ -2137,7 +2167,8 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mQAMode(qa_mode), mHttpRequest(NULL), mHttpOptions(NULL), - mHttpHeaders(NULL) + mHttpHeaders(NULL), + mHttpSemaphore(HTTP_REQUESTS_IN_QUEUE_HIGH_WATER) { mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold")); @@ -3155,12 +3186,11 @@ void LLTextureFetch::removeHttpWaiter(const LLUUID & tid) // Locks: -Mw (must not hold any worker when called) void LLTextureFetch::releaseHttpWaiters() { - if (HTTP_REQUESTS_IN_QUEUE_LOW_WATER < getNumHTTPRequests()) + if (mHttpSemaphore < HTTP_REQUESTS_IN_QUEUE_LOW_WATER) return; // Quickly make a copy of all the LLUIDs. Get off the // mutex as early as possible. - typedef std::vector uuid_vec_t; uuid_vec_t tids; @@ -3171,13 +3201,12 @@ void LLTextureFetch::releaseHttpWaiters() return; const size_t limit(mHttpWaitResource.size()); - tids.resize(limit); - wait_http_res_queue_t::iterator iter(mHttpWaitResource.begin()); - for (int i(0); - i < limit && mHttpWaitResource.end() != iter; - ++i, ++iter) + tids.reserve(limit); + for (wait_http_res_queue_t::iterator iter(mHttpWaitResource.begin()); + mHttpWaitResource.end() != iter; + ++iter) { - tids[i] = *iter; + tids.push_back(*iter); } } // -Mfnq @@ -3196,28 +3225,29 @@ void LLTextureFetch::releaseHttpWaiters() tids2.insert(worker); } } + tids.clear(); // Release workers up to the high water mark. Since we aren't // holding any locks at this point, we can be in competition // with other callers. Do defensive things like getting // refreshed counts of requests and checking if someone else // has moved any worker state around.... - tids.clear(); for (worker_set_t::iterator iter2(tids2.begin()); - tids2.end() != iter2 && 0 < (HTTP_REQUESTS_IN_QUEUE_HIGH_WATER - getNumHTTPRequests()); + tids2.end() != iter2 && mHttpSemaphore > 0; ++iter2) { LLTextureFetchWorker * worker(* iter2); worker->lockWorkMutex(); // +Mw - if (LLTextureFetchWorker::WAIT_HTTP_RESOURCE != worker->mState) + if (LLTextureFetchWorker::WAIT_HTTP_RESOURCE2 != worker->mState) { worker->unlockWorkMutex(); // -Mw continue; } - worker->mHttpReleased = true; + worker->mState = LLTextureFetchWorker::SEND_HTTP_REQ; worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); + worker->acquireHttpSemaphore(); worker->unlockWorkMutex(); // -Mw removeHttpWaiter(worker->mID); @@ -3456,7 +3486,7 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) } // In QA mode, Metrics submode, log the result for ease of testing - if (fetcher->isQAMode() || true) + if (fetcher->isQAMode()) { LL_INFOS("Textures") << merged_llsd << LL_ENDL; } diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 4ee13d171e..50e3181623 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -312,6 +312,15 @@ private: LLCore::HttpOptions * mHttpOptions; // Ttf LLCore::HttpHeaders * mHttpHeaders; // Ttf + // We use a resource semaphore to keep HTTP requests in + // WAIT_HTTP_RESOURCE2 if there aren't sufficient slots in the + // transport. This keeps them near where they can be cheaply + // reprioritized rather than dumping them all across a thread + // where it's more expensive to get at them. Requests in either + // SEND_HTTP_REQ or WAIT_HTTP_REQ charge against the semaphore + // and tracking state transitions is critical to liveness. + int mHttpSemaphore; // Ttf + typedef std::set wait_http_res_queue_t; wait_http_res_queue_t mHttpWaitResource; // Mfnq diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index 5f1d7829ed..bb1535d23d 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -234,15 +234,16 @@ void LLTextureBar::draw() { "DSK", LLColor4::blue }, // CACHE_POST { "NET", LLColor4::green }, // LOAD_FROM_NETWORK { "SIM", LLColor4::green }, // LOAD_FROM_SIMULATOR + { "HTW", LLColor4::green }, // WAIT_HTTP_RESOURCE + { "HTW", LLColor4::green }, // WAIT_HTTP_RESOURCE2 { "REQ", LLColor4::yellow },// SEND_HTTP_REQ - { "HTW", LLColor4::green }, // WAIT_HTTP_RES { "HTP", LLColor4::green }, // WAIT_HTTP_REQ { "DEC", LLColor4::yellow },// DECODE_IMAGE { "DEC", LLColor4::green }, // DECODE_IMAGE_UPDATE { "WRT", LLColor4::purple },// WRITE_TO_CACHE { "WRT", LLColor4::orange },// WAIT_ON_WRITE { "END", LLColor4::red }, // DONE -#define LAST_STATE 13 +#define LAST_STATE 14 { "CRE", LLColor4::magenta }, // LAST_STATE+1 { "FUL", LLColor4::green }, // LAST_STATE+2 { "BAD", LLColor4::red }, // LAST_STATE+3 -- cgit v1.2.3 From 888e2587e57bf296c0b9ee227a4baa3efb3f4892 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 15 Jun 2012 13:05:24 -0400 Subject: Ported example (freestanding) program to drive API & generate performance numbers. This is a command-line utility to pull content down from a service through the llcorehttp library to produce timings and resource footprints. --- indra/llcorehttp/CMakeLists.txt | 28 ++ indra/llcorehttp/examples/http_texture_load.cpp | 494 ++++++++++++++++++++++++ 2 files changed, 522 insertions(+) create mode 100644 indra/llcorehttp/examples/http_texture_load.cpp diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 4273b32fe3..9d8bae973e 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -134,5 +134,33 @@ if (LL_TESTS) "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llcorehttp_peer.py" ) + # + # Example Programs + # + SET(llcorehttp_EXAMPLE_SOURCE_FILES + examples/http_texture_load.cpp + ) + + set(example_libs + ${LLCOREHTTP_LIBRARIES} + ${WINDOWS_LIBRARIES} + ${LLMESSAGE_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${GOOGLEMOCK_LIBRARIES} + ${CURL_LIBRARIES} + ${CARES_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} + ${BOOST_THREAD_LIBRARY} + ) + + add_executable(http_texture_load + ${llcorehttp_EXAMPLE_SOURCE_FILES} + ) + set_target_properties(http_texture_load + PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}") + + target_link_libraries(http_texture_load ${example_libs}) + endif (LL_TESTS) diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp new file mode 100644 index 0000000000..83139912c9 --- /dev/null +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -0,0 +1,494 @@ +/** + * @file http_texture_load.cpp + * @brief Texture download example for core-http library + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include +#include +#include +#include +#include +#if !defined(WIN32) +#include +#endif + +#include "httpcommon.h" +#include "httprequest.h" +#include "httphandler.h" +#include "httpresponse.h" +#include "bufferarray.h" +#include "_mutex.h" + +#include +#include + + +void init_curl(); +void term_curl(); +unsigned long ssl_thread_id_callback(void); +void ssl_locking_callback(int mode, int type, const char * file, int line); +void usage(std::ostream & out); + +// Default command line settings +static int concurrency_limit(40); +static char url_format[1024] = "http://example.com/some/path?texture_id=%s.texture"; + +#if defined(WIN32) + +#define strncpy(_a, _b, _c) strncpy_s(_a, _b, _c) +#define strtok_r(_a, _b, _c) strtok_s(_a, _b, _c) + +int getopt(int argc, char * const argv[], const char *optstring); +char *optarg(NULL); +int optind(1); + +#endif + +class WorkingSet : public LLCore::HttpHandler +{ +public: + WorkingSet(); + + bool reload(LLCore::HttpRequest *); + + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + + void loadTextureUuids(FILE * in); + +public: + struct Spec + { + std::string mUuid; + int mOffset; + int mLength; + }; + typedef std::set handle_set_t; + typedef std::vector texture_list_t; + +public: + bool mVerbose; + bool mRandomRange; + int mMaxConcurrency; + handle_set_t mHandles; + int mRemaining; + int mLimit; + int mAt; + std::string mUrl; + texture_list_t mTextures; + int mErrorsApi; + int mErrorsHttp; + int mSuccesses; + long mByteCount; +}; + + +// +// +// +int main(int argc, char** argv) +{ + bool do_random(false); + bool do_verbose(false); + + int option(-1); + while (-1 != (option = getopt(argc, argv, "u:c:h?Rv"))) + { + switch (option) + { + case 'u': + strncpy(url_format, optarg, sizeof(url_format)); + url_format[sizeof(url_format) - 1] = '\0'; + break; + + case 'c': + { + unsigned long value; + char * end; + + value = strtoul(optarg, &end, 10); + if (value < 1 || value > 100 || *end != '\0') + { + usage(std::cerr); + return 1; + } + concurrency_limit = value; + } + break; + + case 'R': + do_random = true; + break; + + case 'v': + do_verbose = true; + break; + + case 'h': + case '?': + usage(std::cout); + return 0; + } + } + + if ((optind + 1) != argc) + { + usage(std::cerr); + return 1; + } + + FILE * uuids(fopen(argv[optind], "r")); + if (! uuids) + { + const char * errstr(strerror(errno)); + + std::cerr << "Couldn't open UUID file '" << argv[optind] << "'. Reason: " + << errstr << std::endl; + return 1; + } + + // Initialization + init_curl(); + LLCore::HttpRequest::createService(); + LLCore::HttpRequest::startThread(); + + // Get service point + LLCore::HttpRequest * hr = new LLCore::HttpRequest(); + + // Get a handler/working set + WorkingSet ws; + + // Fill the working set with work + ws.mUrl = url_format; + ws.loadTextureUuids(uuids); + ws.mRandomRange = do_random; + ws.mVerbose = do_verbose; + ws.mMaxConcurrency = concurrency_limit; + + if (! ws.mTextures.size()) + { + std::cerr << "No UUIDs found in file '" << argv[optind] << "'." << std::endl; + return 1; + } + + // Run it + while (! ws.reload(hr)) + { + hr->update(1000); +#if defined(WIN32) + Sleep(5); +#else + usleep(5000); +#endif + } + + // Report + std::cout << "HTTP errors: " << ws.mErrorsHttp << " API errors: " << ws.mErrorsApi + << " Successes: " << ws.mSuccesses << " Byte count: " << ws.mByteCount + << std::endl; + + // Clean up + delete hr; + term_curl(); + + return 0; +} + + +void usage(std::ostream & out) +{ + out << "\n" + "usage:\thttp_texture_load [options] uuid_file\n" + "\n" + "This is a standalone program to drive the New Platform HTTP Library.\n" + "The program is supplied with a file of texture UUIDs, one per line\n" + "These are fetched sequentially using a pool of concurrent connection\n" + "until all are fetched. The default URL format is only useful from\n" + "within Linden Lab but this can be overriden with a printf-style\n" + "URL formatting string on the command line.\n" + "\n" + "Options:\n" + "\n" + " -u printf-style format string for URL generation\n" + " Default: " << url_format << "\n" + " -R Issue GETs with random Range: headers\n" + " -c Maximum request concurrency. Range: [1..100]\n" + " Default: " << concurrency_limit << "\n" + " -v Verbose mode. Issue some chatter while running\n" + " -h print this help\n" + "\n" + << std::endl; +} + + +WorkingSet::WorkingSet() + : LLCore::HttpHandler(), + mVerbose(false), + mRandomRange(false), + mRemaining(200), + mLimit(200), + mAt(0), + mErrorsApi(0), + mErrorsHttp(0), + mSuccesses(0), + mByteCount(0L) +{ + mTextures.reserve(30000); +} + + +bool WorkingSet::reload(LLCore::HttpRequest * hr) +{ + int to_do((std::min)(mRemaining, mMaxConcurrency - int(mHandles.size()))); + + for (int i(0); i < to_do; ++i) + { + char buffer[1024]; +#if defined(WIN32) + _snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, mUrl.c_str(), mTextures[mAt].mUuid.c_str()); +#else + snprintf(buffer, sizeof(buffer), mUrl.c_str(), mUuids[mAt].c_str()); +#endif + int offset(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mTextures[mAt].mOffset); + int length(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mTextures[mAt].mLength); + + LLCore::HttpHandle handle; + if (offset || length) + { + handle = hr->requestGetByteRange(0, 0, buffer, offset, length, NULL, NULL, this); + } + else + { + handle = hr->requestGet(0, 0, buffer, NULL, NULL, this); + } + if (! handle) + { + // Fatal. Couldn't queue up something. + std::cerr << "Failed to queue work to HTTP Service. Reason: " + << hr->getStatus().toString() << std::endl; + exit(1); + } + else + { + mHandles.insert(handle); + } + mAt++; + mRemaining--; + + if (mVerbose) + { + static int count(0); + ++count; + if (0 == (count %5)) + std::cout << "Queued " << count << std::endl; + } + } + + // Are we done? + return (! mRemaining) && mHandles.empty(); +} + + +void WorkingSet::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ + handle_set_t::iterator it(mHandles.find(handle)); + if (mHandles.end() == it) + { + // Wha? + std::cerr << "Failed to find handle in request list. Fatal." << std::endl; + exit(1); + } + else + { + LLCore::HttpStatus status(response->getStatus()); + if (status) + { + // More success + LLCore::BufferArray * data(response->getBody()); + mByteCount += data->size(); + ++mSuccesses; + } + else + { + // Something in this library or libcurl + if (status.isHttpStatus()) + { + ++mErrorsHttp; + } + else + { + ++mErrorsApi; + } + } + mHandles.erase(it); + } + + if (mVerbose) + { + static int count(0); + ++count; + if (0 == (count %5)) + std::cout << "Handled " << count << std::endl; + } +} + + +void WorkingSet::loadTextureUuids(FILE * in) +{ + char buffer[1024]; + + while (fgets(buffer, sizeof(buffer), in)) + { + WorkingSet::Spec texture; + char * state(NULL); + char * token = strtok_r(buffer, " \t\n,", &state); + if (token && 36 == strlen(token)) + { + // Close enough for this function + texture.mUuid = token; + texture.mOffset = 0; + texture.mLength = 0; + token = strtok_r(buffer, " \t\n,", &state); + if (token) + { + int offset(atoi(token)); + token = strtok_r(buffer, " \t\n,", &state); + if (token) + { + int length(atoi(token)); + texture.mOffset = offset; + texture.mLength = length; + } + } + mTextures.push_back(texture); + } + } + mRemaining = mLimit = mTextures.size(); +} + + +int ssl_mutex_count(0); +LLCoreInt::HttpMutex ** ssl_mutex_list = NULL; + +void init_curl() +{ + curl_global_init(CURL_GLOBAL_ALL); + + ssl_mutex_count = CRYPTO_num_locks(); + if (ssl_mutex_count > 0) + { + ssl_mutex_list = new LLCoreInt::HttpMutex * [ssl_mutex_count]; + + for (int i(0); i < ssl_mutex_count; ++i) + { + ssl_mutex_list[i] = new LLCoreInt::HttpMutex; + } + + CRYPTO_set_locking_callback(ssl_locking_callback); + CRYPTO_set_id_callback(ssl_thread_id_callback); + } +} + + +void term_curl() +{ + CRYPTO_set_locking_callback(NULL); + for (int i(0); i < ssl_mutex_count; ++i) + { + delete ssl_mutex_list[i]; + } + delete [] ssl_mutex_list; +} + + +unsigned long ssl_thread_id_callback(void) +{ +#if defined(WIN32) + return (unsigned long) GetCurrentThread(); +#else + return (unsigned long) pthread_self(); +#endif +} + + +void ssl_locking_callback(int mode, int type, const char * /* file */, int /* line */) +{ + if (type >= 0 && type < ssl_mutex_count) + { + if (mode & CRYPTO_LOCK) + { + ssl_mutex_list[type]->lock(); + } + else + { + ssl_mutex_list[type]->unlock(); + } + } +} + + +#if defined(WIN32) + +// Very much a subset of posix functionality. Don't push +// it too hard... +int getopt(int argc, char * const argv[], const char *optstring) +{ + static int pos(0); + while (optind < argc) + { + if (pos == 0) + { + if (argv[optind][0] != '-') + return -1; + pos = 1; + } + if (! argv[optind][pos]) + { + ++optind; + pos = 0; + continue; + } + const char * thing(strchr(optstring, argv[optind][pos])); + if (! thing) + { + ++optind; + return -1; + } + if (thing[1] == ':') + { + optarg = argv[++optind]; + ++optind; + pos = 0; + } + else + { + optarg = NULL; + ++pos; + } + return *thing; + } + return -1; +} + +#endif + -- cgit v1.2.3 From 57575339bb7dd4f67c5e4dc1c1ccc9eda6a2f8f5 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 15 Jun 2012 13:42:13 -0400 Subject: Fix for linux/mac builds. --- indra/llcorehttp/examples/http_texture_load.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp index 83139912c9..6f79833cf6 100644 --- a/indra/llcorehttp/examples/http_texture_load.cpp +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -267,7 +267,7 @@ bool WorkingSet::reload(LLCore::HttpRequest * hr) #if defined(WIN32) _snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, mUrl.c_str(), mTextures[mAt].mUuid.c_str()); #else - snprintf(buffer, sizeof(buffer), mUrl.c_str(), mUuids[mAt].c_str()); + snprintf(buffer, sizeof(buffer), mUrl.c_str(), mTextures[mAt].mUuid.c_str()); #endif int offset(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mTextures[mAt].mOffset); int length(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mTextures[mAt].mLength); -- cgit v1.2.3 From 6193ee6a331e3dfd562400a32a961bad0b8bed12 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Sat, 16 Jun 2012 15:50:48 -0400 Subject: First round of basic tuning work (shorter sleeps, larger BufferArray blocks). Beefed up the metrics gathering in http_texture_load to get memory sizes and cpu consumption on windows (still need to implement that on Mac & linux). Ran runs with various idle loops with sleeps from 20 ms down to pure spinning, varied Block allocation size from 1504 to 2^20 bytes. 2ms/2ms/65540 appears to be a good spot under the test conditions (Win7, danu grid, client in Boston). --- indra/llcorehttp/_httpservice.cpp | 2 +- indra/llcorehttp/bufferarray.h | 2 +- indra/llcorehttp/examples/http_texture_load.cpp | 249 +++++++++++++++++++++++- 3 files changed, 244 insertions(+), 9 deletions(-) diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index beba8f08f4..87a78820f5 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -40,7 +40,7 @@ // Tuning parameters -static const int LOOP_SLEEP_NORMAL_MS = 10; // Normal per-loop sleep in milliseconds +static const int LOOP_SLEEP_NORMAL_MS = 2; // Normal per-loop sleep in milliseconds namespace LLCore diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h index 72c3e1c669..d3862b45e1 100644 --- a/indra/llcorehttp/bufferarray.h +++ b/indra/llcorehttp/bufferarray.h @@ -74,7 +74,7 @@ private: public: // Internal magic number, may be used by unit tests. - static const size_t BLOCK_ALLOC_SIZE = 1504; + static const size_t BLOCK_ALLOC_SIZE = 65540; /// Appends the indicated data to the BufferArray /// modifying current position and total size. New diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp index 6f79833cf6..1277dd3353 100644 --- a/indra/llcorehttp/examples/http_texture_load.cpp +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -33,6 +33,8 @@ #include #endif +#include "linden_common.h" + #include "httpcommon.h" #include "httprequest.h" #include "httphandler.h" @@ -43,6 +45,8 @@ #include #include +#include "lltimer.h" + void init_curl(); void term_curl(); @@ -65,6 +69,9 @@ int optind(1); #endif + +// Mostly just a container for the texture IDs and fetch +// parameters.... class WorkingSet : public LLCore::HttpHandler { public: @@ -98,11 +105,46 @@ public: texture_list_t mTextures; int mErrorsApi; int mErrorsHttp; + int mErrorsHttp404; + int mErrorsHttp416; + int mErrorsHttp500; + int mErrorsHttp503; int mSuccesses; long mByteCount; }; +// Gather process information while we run. Process +// size, cpu consumed, wallclock time. + +class Metrics +{ +public: + class MetricsImpl; + +public: + Metrics(); + ~Metrics(); + + void init(); + void sample(); + void term(); + +protected: + MetricsImpl * mImpl; + +public: + U64 mMaxVSZ; + U64 mMinVSZ; + U64 mStartWallTime; + U64 mEndWallTime; + U64 mStartUTime; + U64 mEndUTime; + U64 mStartSTime; + U64 mEndSTime; +}; + + // // // @@ -191,22 +233,38 @@ int main(int argc, char** argv) return 1; } + // Setup metrics + Metrics metrics; + metrics.init(); + // Run it + int passes(0); while (! ws.reload(hr)) { - hr->update(1000); -#if defined(WIN32) - Sleep(5); -#else - usleep(5000); -#endif + hr->update(5000); + ms_sleep(2); + if (0 == (++passes % 200)) + { + metrics.sample(); + } } + metrics.sample(); + metrics.term(); // Report std::cout << "HTTP errors: " << ws.mErrorsHttp << " API errors: " << ws.mErrorsApi << " Successes: " << ws.mSuccesses << " Byte count: " << ws.mByteCount << std::endl; - + std::cout << "HTTP 404 errors: " << ws.mErrorsHttp404 << " HTTP 416 errors: " << ws.mErrorsHttp416 + << " HTTP 500 errors: " << ws.mErrorsHttp500 << " HTTP 503 errors: " << ws.mErrorsHttp503 + << std::endl; + std::cout << "User CPU: " << (metrics.mEndUTime - metrics.mStartUTime) + << " uS System CPU: " << (metrics.mEndSTime - metrics.mStartSTime) + << " uS Wall Time: " << (metrics.mEndWallTime - metrics.mStartWallTime) + << " uS Maximum VSZ: " << metrics.mMaxVSZ + << " Bytes Minimum VSZ: " << metrics.mMinVSZ << " Bytes" + << std::endl; + // Clean up delete hr; term_curl(); @@ -250,6 +308,10 @@ WorkingSet::WorkingSet() mAt(0), mErrorsApi(0), mErrorsHttp(0), + mErrorsHttp404(0), + mErrorsHttp416(0), + mErrorsHttp500(0), + mErrorsHttp503(0), mSuccesses(0), mByteCount(0L) { @@ -333,7 +395,28 @@ void WorkingSet::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * r // Something in this library or libcurl if (status.isHttpStatus()) { + static const LLCore::HttpStatus hs404(404); + static const LLCore::HttpStatus hs416(416); + static const LLCore::HttpStatus hs500(500); + static const LLCore::HttpStatus hs503(503); + ++mErrorsHttp; + if (hs404 == status) + { + ++mErrorsHttp404; + } + else if (hs416 == status) + { + ++mErrorsHttp416; + } + else if (hs500 == status) + { + ++mErrorsHttp500; + } + else if (hs503 == status) + { + ++mErrorsHttp503; + } } else { @@ -492,3 +575,155 @@ int getopt(int argc, char * const argv[], const char *optstring) #endif + + +#if defined(WIN32) + +#define PSAPI_VERSION 1 +#include "windows.h" +#include "psapi.h" + +class Metrics::MetricsImpl +{ +public: + MetricsImpl() + {} + + ~MetricsImpl() + {} + + void MetricsImpl::init(Metrics * metrics) + { + HANDLE self(GetCurrentProcess()); // Does not have to be closed + FILETIME ft_dummy, ft_system, ft_user; + GetProcessTimes(self, &ft_dummy, &ft_dummy, &ft_system, &ft_user); + ULARGE_INTEGER uli; + uli.u.LowPart = ft_system.dwLowDateTime; + uli.u.HighPart = ft_system.dwHighDateTime; + metrics->mStartSTime = uli.QuadPart / U64L(10); // Convert to uS + uli.u.LowPart = ft_user.dwLowDateTime; + uli.u.HighPart = ft_user.dwHighDateTime; + metrics->mStartUTime = uli.QuadPart / U64L(10); + metrics->mStartWallTime = totalTime(); + } + + void MetricsImpl::sample(Metrics * metrics) + { + PROCESS_MEMORY_COUNTERS_EX counters; + + GetProcessMemoryInfo(GetCurrentProcess(), + (PROCESS_MEMORY_COUNTERS *) &counters, + sizeof(counters)); + // Okay, PrivateUsage isn't truly VSZ but it will be + // a good tracker for leaks and fragmentation. Work on + // a better estimator later... + SIZE_T vsz(counters.PrivateUsage); + metrics->mMaxVSZ = (std::max)(metrics->mMaxVSZ, U64(vsz)); + metrics->mMinVSZ = (std::min)(metrics->mMinVSZ, U64(vsz)); + } + + void MetricsImpl::term(Metrics * metrics) + { + HANDLE self(GetCurrentProcess()); // Does not have to be closed + FILETIME ft_dummy, ft_system, ft_user; + GetProcessTimes(self, &ft_dummy, &ft_dummy, &ft_system, &ft_user); + ULARGE_INTEGER uli; + uli.u.LowPart = ft_system.dwLowDateTime; + uli.u.HighPart = ft_system.dwHighDateTime; + metrics->mEndSTime = uli.QuadPart / U64L(10); + uli.u.LowPart = ft_user.dwLowDateTime; + uli.u.HighPart = ft_user.dwHighDateTime; + metrics->mEndUTime = uli.QuadPart / U64L(10); + metrics->mEndWallTime = totalTime(); + } + +protected: +}; + +#elif defined(DARWIN) + + +class Metrics::MetricsImpl +{ +public: + MetricsImpl() + {} + + ~MetricsImpl() + {} + + void MetricsImpl::init(Metrics *) + {} + + void MetricsImpl::sample(Metrics *) + {} + + void MetricsImpl::term(Metrics *) + {} +}; + +#else + +class Metrics::MetricsImpl +{ +public: + MetricsImpl() + {} + + + ~MetricsImpl() + {} + + void MetricsImpl::init(Metrics *) + {} + + + void MetricsImpl::sample(Metrics *) + {} + + + void MetricsImpl::term(Metrics *) + {} +}; + +#endif // defined(WIN32) + +Metrics::Metrics() + : mMaxVSZ(U64(0)), + mMinVSZ(U64L(0xffffffffffffffff)), + mStartWallTime(U64(0)), + mEndWallTime(U64(0)), + mStartUTime(U64(0)), + mEndUTime(U64(0)), + mStartSTime(U64(0)), + mEndSTime(U64(0)) +{ + mImpl = new MetricsImpl(); +} + + +Metrics::~Metrics() +{ + delete mImpl; + mImpl = NULL; +} + + +void Metrics::init() +{ + mImpl->init(this); +} + + +void Metrics::sample() +{ + mImpl->sample(this); +} + + +void Metrics::term() +{ + mImpl->term(this); +} + + -- cgit v1.2.3 From b27bb47f3ac203c474adbb65245d23c4e97baca9 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Sat, 16 Jun 2012 21:51:02 +0000 Subject: Implement metrics collection for Linux. Next: Mac OS X. --- indra/llcorehttp/examples/http_texture_load.cpp | 161 ++++++++++++++++++++++-- 1 file changed, 148 insertions(+), 13 deletions(-) diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp index 1277dd3353..2fa3cefbb1 100644 --- a/indra/llcorehttp/examples/http_texture_load.cpp +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -592,7 +592,7 @@ public: ~MetricsImpl() {} - void MetricsImpl::init(Metrics * metrics) + void init(Metrics * metrics) { HANDLE self(GetCurrentProcess()); // Does not have to be closed FILETIME ft_dummy, ft_system, ft_user; @@ -607,7 +607,7 @@ public: metrics->mStartWallTime = totalTime(); } - void MetricsImpl::sample(Metrics * metrics) + void sample(Metrics * metrics) { PROCESS_MEMORY_COUNTERS_EX counters; @@ -622,7 +622,7 @@ public: metrics->mMinVSZ = (std::min)(metrics->mMinVSZ, U64(vsz)); } - void MetricsImpl::term(Metrics * metrics) + void term(Metrics * metrics) { HANDLE self(GetCurrentProcess()); // Does not have to be closed FILETIME ft_dummy, ft_system, ft_user; @@ -652,13 +652,13 @@ public: ~MetricsImpl() {} - void MetricsImpl::init(Metrics *) + void init(Metrics *) {} - void MetricsImpl::sample(Metrics *) + void sample(Metrics *) {} - void MetricsImpl::term(Metrics *) + void term(Metrics *) {} }; @@ -668,24 +668,159 @@ class Metrics::MetricsImpl { public: MetricsImpl() + : mProcFS(NULL), + mUsecsPerTick(U64L(0)) {} ~MetricsImpl() - {} + { + if (mProcFS) + { + fclose(mProcFS); + mProcFS = NULL; + } + } - void MetricsImpl::init(Metrics *) - {} + void init(Metrics * metrics) + { + if (! mProcFS) + { + mProcFS = fopen("/proc/self/stat", "r"); + if (! mProcFS) + { + const int errnum(errno); + LL_ERRS("Main") << "Error opening proc fs: " << strerror(errnum) << LL_ENDL; + } + } + + long ticks_per_sec(sysconf(_SC_CLK_TCK)); + mUsecsPerTick = U64L(1000000) / ticks_per_sec; + U64 usecs_per_sec(mUsecsPerTick * ticks_per_sec); + if (900000 > usecs_per_sec || 1100000 < usecs_per_sec) + { + LL_ERRS("Main") << "Resolution problems using uSecs for ticks" << LL_ENDL; + } + U64 utime, stime; + if (scanProcFS(&utime, &stime, NULL)) + { + metrics->mStartSTime = stime; + metrics->mStartUTime = utime; + } + metrics->mStartWallTime = totalTime(); - void MetricsImpl::sample(Metrics *) - {} + sample(metrics); + } - void MetricsImpl::term(Metrics *) - {} + void sample(Metrics * metrics) + { + U64 vsz; + if (scanProcFS(NULL, NULL, &vsz)) + { + metrics->mMaxVSZ = (std::max)(metrics->mMaxVSZ, vsz); + metrics->mMinVSZ = (std::min)(metrics->mMinVSZ, vsz); + } + } + + + void term(Metrics * metrics) + { + U64 utime, stime; + if (scanProcFS(&utime, &stime, NULL)) + { + metrics->mEndSTime = stime; + metrics->mEndUTime = utime; + } + metrics->mEndWallTime = totalTime(); + + sample(metrics); + + if (mProcFS) + { + fclose(mProcFS); + mProcFS = NULL; + } + } + +protected: + bool scanProcFS(U64 * utime, U64 * stime, U64 * vsz) + { + if (mProcFS) + { + int i_dummy; + unsigned int ui_dummy; + unsigned long ul_dummy, user_ticks, sys_ticks, vsize; + long l_dummy, rss; + unsigned long long ull_dummy; + char c_dummy; + + char buffer[256]; + + static const char * format("%d %*s %c %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %llu %lu %ld"); + + fseek(mProcFS, 0L, SEEK_SET); + size_t len = fread(buffer, 1, sizeof(buffer) - 1, mProcFS); + if (! len) + { + return false; + } + buffer[len] = '\0'; + if (23 == sscanf(buffer, format, + &i_dummy, // pid + // &s_dummy, // command name + &c_dummy, // state + &i_dummy, // ppid + &i_dummy, // pgrp + &i_dummy, // session + &i_dummy, // terminal + &i_dummy, // terminal group id + &ui_dummy, // flags + &ul_dummy, // minor faults + &ul_dummy, // minor faults in children + &ul_dummy, // major faults + &ul_dummy, // major faults in children + &user_ticks, + &sys_ticks, + &l_dummy, // cutime + &l_dummy, // cstime + &l_dummy, // process priority + &l_dummy, // nice value + &l_dummy, // thread count + &l_dummy, // time to SIGALRM + &ull_dummy, // start time + &vsize, + &rss)) + { + // Looks like we understand the line + if (utime) + { + *utime = user_ticks * mUsecsPerTick; + } + + if (stime) + { + *stime = sys_ticks * mUsecsPerTick; + } + + if (vsz) + { + *vsz = vsize; + } + return true; + } + } + return false; + } + +protected: + FILE * mProcFS; + U64 mUsecsPerTick; + }; + #endif // defined(WIN32) Metrics::Metrics() -- cgit v1.2.3 From 252c297bcc0d9ed8e732d86c9c5886cd63c54f8d Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Sat, 16 Jun 2012 19:03:31 -0400 Subject: Add metrics gathering utils for Mac OS X. All platforms have useful numbers now. --- indra/llcorehttp/examples/http_texture_load.cpp | 78 ++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 9 deletions(-) diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp index 2fa3cefbb1..7efdf53959 100644 --- a/indra/llcorehttp/examples/http_texture_load.cpp +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -577,7 +577,7 @@ int getopt(int argc, char * const argv[], const char *optstring) -#if defined(WIN32) +#if LL_WINDOWS #define PSAPI_VERSION 1 #include "windows.h" @@ -640,8 +640,10 @@ public: protected: }; -#elif defined(DARWIN) +#elif LL_DARWIN +#include +#include class Metrics::MetricsImpl { @@ -652,14 +654,72 @@ public: ~MetricsImpl() {} - void init(Metrics *) - {} + void init(Metrics * metrics) + { + U64 utime, stime; - void sample(Metrics *) - {} + if (getTimes(&utime, &stime)) + { + metrics->mStartSTime = stime; + metrics->mStartUTime = utime; + } + metrics->mStartWallTime = totalTime(); + sample(metrics); + } - void term(Metrics *) - {} + void sample(Metrics * metrics) + { + U64 vsz; + + if (getVM(&vsz)) + { + metrics->mMaxVSZ = (std::max)(metrics->mMaxVSZ, vsz); + metrics->mMinVSZ = (std::min)(metrics->mMinVSZ, vsz); + } + } + + void term(Metrics * metrics) + { + U64 utime, stime; + + if (getTimes(&utime, &stime)) + { + metrics->mEndSTime = stime; + metrics->mEndUTime = utime; + } + metrics->mEndWallTime = totalTime(); + } + +protected: + bool getVM(U64 * vsz) + { + task_basic_info task_info_block; + mach_msg_type_number_t task_info_count(TASK_BASIC_INFO_COUNT); + + if (KERN_SUCCESS != task_info(mach_task_self(), + TASK_BASIC_INFO, + (task_info_t) &task_info_block, + &task_info_count)) + { + return false; + } + * vsz = task_info_block.virtual_size; + return true; + } + + bool getTimes(U64 * utime, U64 * stime) + { + struct rusage usage; + + if (getrusage(RUSAGE_SELF, &usage)) + { + return false; + } + * utime = U64(usage.ru_utime.tv_sec) * U64L(1000000) + usage.ru_utime.tv_usec; + * stime = U64(usage.ru_stime.tv_sec) * U64L(1000000) + usage.ru_stime.tv_usec; + return true; + } + }; #else @@ -821,7 +881,7 @@ protected: }; -#endif // defined(WIN32) +#endif // LL_WINDOWS Metrics::Metrics() : mMaxVSZ(U64(0)), -- cgit v1.2.3 From 46662a3010b2c920ae60b4ca246d56e3caee6f6f Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 18 Jun 2012 11:16:58 -0400 Subject: Move dtors for refcounted objects to protected access. --- indra/llcorehttp/_httpopcancel.h | 4 +++- indra/llcorehttp/_httpoperation.h | 8 +++++++- indra/llcorehttp/_httpoprequest.h | 4 +++- indra/llcorehttp/_httpopsetget.h | 4 +++- indra/llcorehttp/_httpopsetpriority.h | 2 ++ indra/llcorehttp/_httpreplyqueue.h | 4 +++- indra/llcorehttp/_httprequestqueue.h | 4 +++- indra/llcorehttp/bufferarray.h | 4 +++- indra/llcorehttp/httpheaders.h | 3 ++- indra/llcorehttp/httpoptions.h | 3 ++- indra/llcorehttp/httpresponse.h | 3 ++- 11 files changed, 33 insertions(+), 10 deletions(-) diff --git a/indra/llcorehttp/_httpopcancel.h b/indra/llcorehttp/_httpopcancel.h index 6d1e0f8774..4d927d1aaf 100644 --- a/indra/llcorehttp/_httpopcancel.h +++ b/indra/llcorehttp/_httpopcancel.h @@ -51,8 +51,10 @@ class HttpOpCancel : public HttpOperation { public: HttpOpCancel(HttpHandle handle); - virtual ~HttpOpCancel(); +protected: + virtual ~HttpOpCancel(); // Use release() + private: HttpOpCancel(const HttpOpCancel &); // Not defined void operator=(const HttpOpCancel &); // Not defined diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index de4939a0ac..5823c08c7b 100644 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -73,7 +73,9 @@ class HttpOperation : public LLCoreInt::RefCounted { public: HttpOperation(); - virtual ~HttpOperation(); + +protected: + virtual ~HttpOperation(); // Use release() private: HttpOperation(const HttpOperation &); // Not defined @@ -131,6 +133,8 @@ class HttpOpStop : public HttpOperation { public: HttpOpStop(); + +protected: virtual ~HttpOpStop(); private: @@ -152,6 +156,8 @@ class HttpOpNull : public HttpOperation { public: HttpOpNull(); + +protected: virtual ~HttpOpNull(); private: diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index fc2301057c..4643cc3b75 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -54,7 +54,9 @@ class HttpOpRequest : public HttpOperation { public: HttpOpRequest(); - virtual ~HttpOpRequest(); + +protected: + virtual ~HttpOpRequest(); // Use release() private: HttpOpRequest(const HttpOpRequest &); // Not defined diff --git a/indra/llcorehttp/_httpopsetget.h b/indra/llcorehttp/_httpopsetget.h index e065eb4c30..efb24855c5 100644 --- a/indra/llcorehttp/_httpopsetget.h +++ b/indra/llcorehttp/_httpopsetget.h @@ -49,7 +49,9 @@ class HttpOpSetGet : public HttpOperation { public: HttpOpSetGet(); - virtual ~HttpOpSetGet(); + +protected: + virtual ~HttpOpSetGet(); // Use release() private: HttpOpSetGet(const HttpOpSetGet &); // Not defined diff --git a/indra/llcorehttp/_httpopsetpriority.h b/indra/llcorehttp/_httpopsetpriority.h index f1e94b6e43..724293ef78 100644 --- a/indra/llcorehttp/_httpopsetpriority.h +++ b/indra/llcorehttp/_httpopsetpriority.h @@ -47,6 +47,8 @@ class HttpOpSetPriority : public HttpOperation { public: HttpOpSetPriority(HttpHandle handle, HttpRequest::priority_t priority); + +protected: virtual ~HttpOpSetPriority(); private: diff --git a/indra/llcorehttp/_httpreplyqueue.h b/indra/llcorehttp/_httpreplyqueue.h index 28cb1d68b7..4220a09a3b 100644 --- a/indra/llcorehttp/_httpreplyqueue.h +++ b/indra/llcorehttp/_httpreplyqueue.h @@ -63,7 +63,9 @@ class HttpReplyQueue : public LLCoreInt::RefCounted public: /// Caller acquires a Refcount on construction HttpReplyQueue(); - virtual ~HttpReplyQueue(); + +protected: + virtual ~HttpReplyQueue(); // Use release() private: HttpReplyQueue(const HttpReplyQueue &); // Not defined diff --git a/indra/llcorehttp/_httprequestqueue.h b/indra/llcorehttp/_httprequestqueue.h index f96bd7520c..26d7d9dca6 100644 --- a/indra/llcorehttp/_httprequestqueue.h +++ b/indra/llcorehttp/_httprequestqueue.h @@ -51,7 +51,9 @@ class HttpRequestQueue : public LLCoreInt::RefCounted protected: /// Caller acquires a Refcount on construction HttpRequestQueue(); - virtual ~HttpRequestQueue(); + +protected: + virtual ~HttpRequestQueue(); // Use release() private: HttpRequestQueue(const HttpRequestQueue &); // Not defined diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h index d3862b45e1..d0c51d3c73 100644 --- a/indra/llcorehttp/bufferarray.h +++ b/indra/llcorehttp/bufferarray.h @@ -66,7 +66,9 @@ class BufferArray : public LLCoreInt::RefCounted { public: BufferArray(); - virtual ~BufferArray(); + +protected: + virtual ~BufferArray(); // Use release() private: BufferArray(const BufferArray &); // Not defined diff --git a/indra/llcorehttp/httpheaders.h b/indra/llcorehttp/httpheaders.h index 0b6d82561b..3449daa3a1 100644 --- a/indra/llcorehttp/httpheaders.h +++ b/indra/llcorehttp/httpheaders.h @@ -68,9 +68,10 @@ public: /// to the instance. A call to @see release() will destroy /// the instance. HttpHeaders(); - ~HttpHeaders(); protected: + virtual ~HttpHeaders(); // Use release() + HttpHeaders(const HttpHeaders &); // Not defined void operator=(const HttpHeaders &); // Not defined diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h index 0b9dfdc1de..78d0aadb2e 100644 --- a/indra/llcorehttp/httpoptions.h +++ b/indra/llcorehttp/httpoptions.h @@ -61,9 +61,10 @@ class HttpOptions : public LLCoreInt::RefCounted public: HttpOptions(); HttpOptions(const HttpOptions &); - virtual ~HttpOptions(); protected: + virtual ~HttpOptions(); // Use release() + void operator=(const HttpOptions &); // Not defined public: diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h index 5cf3a919f4..5bcd7c4eb8 100644 --- a/indra/llcorehttp/httpresponse.h +++ b/indra/llcorehttp/httpresponse.h @@ -58,9 +58,10 @@ class HttpResponse : public LLCoreInt::RefCounted { public: HttpResponse(); - virtual ~HttpResponse(); protected: + virtual ~HttpResponse(); // Use release() + HttpResponse(const HttpResponse &); // Not defined void operator=(const HttpResponse &); // Not defined -- cgit v1.2.3 From 1cf8e785bad3562fac23feeb2343cfaec1b971bc Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 18 Jun 2012 18:38:24 -0400 Subject: Tidy Texture Console, add cache & resource wait stats, issue stats line to log on exit. With much trial-and-error, cleaned up the banner on the texture console and made everything mostly fit. Added global cache read, cache write and resource wait count events to the console display to show if cache is working. On clean exit, emit a log line to report stats to log file (intended for automated tests, maybe): LLTextureFetch::endThread: CacheReads: 2618, CacheWrites: 117, ResWaits: 0, TotalHTTPReq: 117 --- indra/newview/lltexturefetch.cpp | 57 +++++++++++++++++++++++++++++++++++----- indra/newview/lltexturefetch.h | 17 ++++++++++++ indra/newview/lltextureview.cpp | 34 +++++++++++++++++++----- 3 files changed, 95 insertions(+), 13 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 664af02f78..6e14c5fc63 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -550,6 +550,11 @@ private: unsigned int mHttpReplySize; unsigned int mHttpReplyOffset; bool mHttpHasResource; // Counts against Fetcher's mHttpSemaphore + + // State history + U32 mCacheReadCount; + U32 mCacheWriteCount; + U32 mResourceWaitCount; }; ////////////////////////////////////////////////////////////////////////////// @@ -854,7 +859,10 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mHttpActive(false), mHttpReplySize(0U), mHttpReplyOffset(0U), - mHttpHasResource(false) + mHttpHasResource(false), + mCacheReadCount(0U), + mCacheWriteCount(0U), + mResourceWaitCount(0U) { mCanUseNET = mUrl.empty() ; @@ -905,6 +913,7 @@ LLTextureFetchWorker::~LLTextureFetchWorker() unlockWorkMutex(); // -Mw mFetcher->removeFromHTTPQueue(mID); mFetcher->removeHttpWaiter(mID); + mFetcher->updateStateStats(mCacheReadCount, mCacheWriteCount, mResourceWaitCount); } // Locks: Mw @@ -1127,6 +1136,7 @@ bool LLTextureFetchWorker::doWork(S32 param) setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it // read file from local disk + ++mCacheReadCount; std::string filename = mUrl.substr(7, std::string::npos); CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority, @@ -1136,6 +1146,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + ++mCacheReadCount; CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority, offset, size, responder); @@ -1323,6 +1334,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mState = WAIT_HTTP_RESOURCE2; setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); mFetcher->addHttpWaiter(this->mID); + ++mResourceWaitCount; return false; } mState = SEND_HTTP_REQ; @@ -1646,6 +1658,7 @@ bool LLTextureFetchWorker::doWork(S32 param) U32 cache_priority = mWorkPriority; mWritten = FALSE; mState = WAIT_ON_WRITE; + ++mCacheWriteCount; CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID); mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority, mFormattedImage->getData(), datasize, @@ -2168,7 +2181,10 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mHttpRequest(NULL), mHttpOptions(NULL), mHttpHeaders(NULL), - mHttpSemaphore(HTTP_REQUESTS_IN_QUEUE_HIGH_WATER) + mHttpSemaphore(HTTP_REQUESTS_IN_QUEUE_HIGH_WATER), + mTotalCacheReadCount(0U), + mTotalCacheWriteCount(0U), + mTotalResourceWaitCount(0U) { mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold")); @@ -2661,12 +2677,13 @@ void LLTextureFetch::startThread() } // Threads: Ttf -// -// This detaches the texture fetch thread from the LLCore -// HTTP library but doesn't stop the thread running in that -// library... void LLTextureFetch::endThread() { + LL_INFOS("Texture") << "CacheReads: " << mTotalCacheReadCount + << ", CacheWrites: " << mTotalCacheWriteCount + << ", ResWaits: " << mTotalResourceWaitCount + << ", TotalHTTPReq: " << getTotalNumHTTPRequests() + << LL_ENDL; } // Threads: Ttf @@ -3272,6 +3289,34 @@ int LLTextureFetch::getHttpWaitersCount() } +// Threads: T* +void LLTextureFetch::updateStateStats(U32 cache_read, U32 cache_write, U32 res_wait) +{ + LLMutexLock lock(&mQueueMutex); // +Mfq + + mTotalCacheReadCount += cache_read; + mTotalCacheWriteCount += cache_write; + mTotalResourceWaitCount += res_wait; +} // -Mfq + + +// Threads: T* +void LLTextureFetch::getStateStats(U32 * cache_read, U32 * cache_write, U32 * res_wait) +{ + U32 ret1(0U), ret2(0U), ret3(0U); + + { + LLMutexLock lock(&mQueueMutex); // +Mfq + ret1 = mTotalCacheReadCount; + ret2 = mTotalCacheWriteCount; + ret3 = mTotalResourceWaitCount; + } // -Mfq + + *cache_read = ret1; + *cache_write = ret2; + *res_wait = ret3; +} + ////////////////////////////////////////////////////////////////////////////// // cross-thread command methods diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 50e3181623..e17c71113a 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -186,6 +186,17 @@ public: // Threads: T* int getHttpWaitersCount(); // ---------------------------------- + // Stats management + + // Add given counts to the global totals for the states/requests + // Threads: T* + void updateStateStats(U32 cache_read, U32 cache_write, U32 res_wait); + + // Return the global counts + // Threads: T* + void getStateStats(U32 * cache_read, U32 * cache_write, U32 * res_wait); + + // ---------------------------------- protected: // Threads: T* (but Ttf in practice) @@ -323,6 +334,12 @@ private: typedef std::set wait_http_res_queue_t; wait_http_res_queue_t mHttpWaitResource; // Mfnq + + // Cumulative stats on the states/requests issued by + // textures running through here. + U32 mTotalCacheReadCount; // Mfq + U32 mTotalCacheWriteCount; // Mfq + U32 mTotalResourceWaitCount; // Mfq public: // A probabilistically-correct indicator that the current diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index bb1535d23d..a4227ec2ff 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -76,7 +76,7 @@ static std::string title_string4(" W x H (Dis) Mem"); static S32 title_x1 = 0; static S32 title_x2 = 460; static S32 title_x3 = title_x2 + 40; -static S32 title_x4 = title_x3 + 50; +static S32 title_x4 = title_x3 + 46; static S32 texture_bar_height = 8; //////////////////////////////////////////////////////////////////////////// @@ -349,7 +349,7 @@ void LLTextureBar::draw() // draw the image size at the end { - std::string num_str = llformat("%3dx%3d (%d) %7d", mImagep->getWidth(), mImagep->getHeight(), + std::string num_str = llformat("%3dx%3d (%2d) %7d", mImagep->getWidth(), mImagep->getHeight(), mImagep->getDiscardLevel(), mImagep->hasGLTexture() ? mImagep->getTextureMemory() : 0); LLFontGL::getFontMonospace()->renderUTF8(num_str, 0, title_x4, getRect().getHeight(), color, LLFontGL::LEFT, LLFontGL::TOP); @@ -523,22 +523,42 @@ void LLGLTexMemBar::draw() LLGLSUIDefault gls_ui; LLColor4 text_color(1.f, 1.f, 1.f, 0.75f); LLColor4 color; - - std::string text = ""; + // Gray background using completely magic numbers + gGL.color4f(0.f, 0.f, 0.f, 0.25f); + const LLRect & rect(getRect()); + gl_rect_2d(-4, v_offset, rect.mRight - rect.mLeft + 2, v_offset + line_height*4); + + std::string text = ""; LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*6, text_color, LLFontGL::LEFT, LLFontGL::TOP); - 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/%d MB Bound: %d/%d MB FBO: %d MB Raw Tot: %d MB Bias: %.2f Cache: %.1f/%.1f MB", total_mem, max_total_mem, bound_mem, max_bound_mem, LLRenderTarget::sBytesAllocated/(1024*1024), - LLImageRaw::sGlobalRawMemory >> 20, discard_bias, - cache_usage, cache_max_usage, total_texture_downloaded, total_object_downloaded, total_http_requests); + LLImageRaw::sGlobalRawMemory >> 20, + discard_bias, + cache_usage, + cache_max_usage); //, cache_entries, cache_max_entries + LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*4, + text_color, LLFontGL::LEFT, LLFontGL::TOP); + + U32 cache_read(0U), cache_write(0U), res_wait(0U); + LLAppViewer::getTextureFetch()->getStateStats(&cache_read, &cache_write, &res_wait); + + text = llformat("Net Tot Tex: %.1f MB Tot Obj: %.1f MB Tot Htp: %d Cread: %u Cwrite: %u Rwait: %u", + total_texture_downloaded, + total_object_downloaded, + total_http_requests, + cache_read, + cache_write, + res_wait); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3, text_color, LLFontGL::LEFT, LLFontGL::TOP); -- cgit v1.2.3 From f0353abe7605778048d69ce3acb8f5ddd5693083 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 19 Jun 2012 15:43:29 -0400 Subject: Implement timeout and retry count options for requests. Pretty straightforward. Still don't like how I'm managing the options block. Struct? Accessors? Can't decide. But the options now speed up the unit test runs even as I add tests. --- indra/llcorehttp/_httpoprequest.cpp | 21 +++- indra/llcorehttp/_httpoprequest.h | 2 +- indra/llcorehttp/httpoptions.cpp | 23 ++-- indra/llcorehttp/httpoptions.h | 29 +++-- indra/llcorehttp/tests/test_httprequest.hpp | 155 +++++++++++++++++++++++-- indra/llcorehttp/tests/test_llcorehttp_peer.py | 4 + 6 files changed, 204 insertions(+), 30 deletions(-) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index f78971d8f2..ce41ebcce0 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -108,7 +108,7 @@ HttpOpRequest::HttpOpRequest() mReplyHeaders(NULL), mPolicyRetries(0), mPolicyRetryAt(HttpTime(0)), - mPolicyRetryLimit(5) // *FIXME: Get from policy definitions + mPolicyRetryLimit(5) { // *NOTE: As members are added, retry initialization/cleanup // may need to be extended in @prepareRequest(). @@ -333,6 +333,8 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id, { mProcFlags |= PF_SAVE_HEADERS; } + mPolicyRetryLimit = options->getRetries(); + mPolicyRetryLimit = llclamp(mPolicyRetryLimit, 0, 100); mTracing = (std::max)(mTracing, llclamp(options->getTrace(), 0, 3)); } } @@ -371,10 +373,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) HttpPolicyGlobal & policy(service->getPolicy().getGlobalOptions()); mCurlHandle = curl_easy_init(); - // curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1); curl_easy_setopt(mCurlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, 30); - curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, 30); curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1); curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str()); @@ -493,13 +492,25 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); + + // Request options + long timeout(30); + if (mReqOptions) + { + timeout = mReqOptions->getTimeout(); + timeout = llclamp(timeout, 0L, 3600L); + } + curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, timeout); + curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout); + + // Request headers if (mReqHeaders) { // Caller's headers last to override mCurlHeaders = append_headers_to_slist(mReqHeaders, mCurlHeaders); } curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders); - + if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS)) { curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback); diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 4643cc3b75..9278445763 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -155,7 +155,7 @@ public: // Policy data int mPolicyRetries; HttpTime mPolicyRetryAt; - const int mPolicyRetryLimit; + int mPolicyRetryLimit; }; // end class HttpOpRequest diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp index 155fbda7f1..c11d89e619 100644 --- a/indra/llcorehttp/httpoptions.cpp +++ b/indra/llcorehttp/httpoptions.cpp @@ -34,14 +34,9 @@ namespace LLCore HttpOptions::HttpOptions() : RefCounted(true), mWantHeaders(false), - mTracing(0) -{} - - -HttpOptions::HttpOptions(const HttpOptions & rhs) - : RefCounted(true), - mWantHeaders(rhs.mWantHeaders), - mTracing(rhs.mTracing) + mTracing(0), + mTimeout(30), + mRetries(5) {} @@ -61,4 +56,16 @@ void HttpOptions::setTrace(long level) } +void HttpOptions::setTimeout(unsigned int timeout) +{ + mTimeout = timeout; +} + + +void HttpOptions::setRetries(unsigned int retries) +{ + mRetries = retries; +} + + } // end namespace LLCore diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h index 78d0aadb2e..a0b2253c11 100644 --- a/indra/llcorehttp/httpoptions.h +++ b/indra/llcorehttp/httpoptions.h @@ -60,29 +60,44 @@ class HttpOptions : public LLCoreInt::RefCounted { public: HttpOptions(); - HttpOptions(const HttpOptions &); protected: virtual ~HttpOptions(); // Use release() + HttpOptions(const HttpOptions &); // Not defined void operator=(const HttpOptions &); // Not defined public: - void setWantHeaders(); - bool getWantHeaders() const + void setWantHeaders(); + bool getWantHeaders() const { return mWantHeaders; } - void setTrace(long level); - int getTrace() const + void setTrace(int long); + int getTrace() const { return mTracing; } + + void setTimeout(unsigned int timeout); + unsigned int getTimeout() const + { + return mTimeout; + } + + void setRetries(unsigned int retries); + unsigned int getRetries() const + { + return mRetries; + } protected: - bool mWantHeaders; - long int mTracing; + bool mWantHeaders; + int mTracing; + unsigned int mTimeout; + unsigned int mRetries; + }; // end class HttpOptions diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 5b04796c8a..42e4857037 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -30,6 +30,7 @@ #include "bufferarray.h" #include "httphandler.h" #include "httpresponse.h" +#include "httpoptions.h" #include "_httpservice.h" #include "_httprequestqueue.h" @@ -407,7 +408,8 @@ void HttpRequestTestObjectType::test<5>() mHandlerCalls = 0; HttpRequest * req = NULL; - + HttpOptions * opts = NULL; + try { // Get singletons created @@ -421,6 +423,9 @@ void HttpRequestTestObjectType::test<5>() req = new HttpRequest(); ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + opts = new HttpOptions(); + opts->setRetries(1); // Don't try for too long - default retries take about 18S + // Issue a GET that can't connect mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, @@ -428,14 +433,14 @@ void HttpRequestTestObjectType::test<5>() "http://127.0.0.1:2/nothing/here", 0, 0, - NULL, + opts, NULL, &handler); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. int count(0); - int limit(180); // With retries, can take more than 10 seconds to give up + int limit(50); // With one retry, should fail quickish while (count++ < limit && mHandlerCalls < 1) { req->update(1000); @@ -468,7 +473,11 @@ void HttpRequestTestObjectType::test<5>() usleep(100000); } ensure("Thread actually stopped running", HttpService::isStopped()); - + + // release options + opts->release(); + opts = NULL; + // release the request object delete req; req = NULL; @@ -490,6 +499,11 @@ void HttpRequestTestObjectType::test<5>() catch (...) { stop_thread(req); + if (opts) + { + opts->release(); + opts = NULL; + } delete req; HttpRequest::destroyService(); throw; @@ -503,7 +517,7 @@ void HttpRequestTestObjectType::test<6>() ScopedCurlInit ready; std::string url_base(get_base_url()); - std::cerr << "Base: " << url_base << std::endl; + // std::cerr << "Base: " << url_base << std::endl; set_test_name("HttpRequest GET to real service"); @@ -611,7 +625,7 @@ void HttpRequestTestObjectType::test<7>() ScopedCurlInit ready; std::string url_base(get_base_url()); - std::cerr << "Base: " << url_base << std::endl; + // std::cerr << "Base: " << url_base << std::endl; set_test_name("HttpRequest GET with Range: header to real service"); @@ -721,7 +735,7 @@ void HttpRequestTestObjectType::test<8>() ScopedCurlInit ready; std::string url_base(get_base_url()); - std::cerr << "Base: " << url_base << std::endl; + // std::cerr << "Base: " << url_base << std::endl; set_test_name("HttpRequest PUT to real service"); @@ -840,7 +854,7 @@ void HttpRequestTestObjectType::test<9>() ScopedCurlInit ready; std::string url_base(get_base_url()); - std::cerr << "Base: " << url_base << std::endl; + // std::cerr << "Base: " << url_base << std::endl; set_test_name("HttpRequest POST to real service"); @@ -959,7 +973,7 @@ void HttpRequestTestObjectType::test<10>() ScopedCurlInit ready; std::string url_base(get_base_url()); - std::cerr << "Base: " << url_base << std::endl; + // std::cerr << "Base: " << url_base << std::endl; set_test_name("HttpRequest GET with some tracing"); @@ -1065,6 +1079,129 @@ void HttpRequestTestObjectType::test<10>() } } + +template <> template <> +void HttpRequestTestObjectType::test<11>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest GET timeout"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + std::string url_base(get_base_url() + "/sleep/"); // path to a 30-second sleep + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * opts = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + opts = new HttpOptions(); + opts->setRetries(0); // Don't retry + opts->setTimeout(2); + + // Issue a GET that can't connect + mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + 0, + 0, + opts, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(50); // With one retry, should fail quickish + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options + opts->release(); + opts = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + if (opts) + { + opts->release(); + opts = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + } // end namespace tut namespace diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 8c3ad805b3..5f0116d384 100644 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -31,6 +31,7 @@ $/LicenseInfo$ import os import sys +import time from threading import Thread from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler @@ -97,6 +98,9 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): def answer(self, data): debug("%s.answer(%s): self.path = %r", self.__class__.__name__, data, self.path) + if self.path.find("/sleep/") != -1: + time.sleep(30) + if "fail" not in self.path: response = llsd.format_xml(data.get("reply", llsd.LLSD("success"))) debug("success: %s", response) -- cgit v1.2.3 From a50944e078b98435685f04eda0ba93e95d4c61f2 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 19 Jun 2012 17:01:02 -0400 Subject: Cleanup: move magic nubmers to new _httpinternal.h header file. --- indra/llcorehttp/CMakeLists.txt | 1 + indra/llcorehttp/_httpinternal.h | 82 ++++++++++++++++++++++++++++++++++ indra/llcorehttp/_httplibcurl.cpp | 17 ++++--- indra/llcorehttp/_httplibcurl.h | 14 ++---- indra/llcorehttp/_httpoperation.cpp | 3 +- indra/llcorehttp/_httpoprequest.cpp | 62 ++++++++++++------------- indra/llcorehttp/_httpoprequest.h | 6 +++ indra/llcorehttp/_httppolicy.cpp | 4 +- indra/llcorehttp/_httppolicy.h | 3 +- indra/llcorehttp/_httppolicyglobal.cpp | 10 +++-- indra/llcorehttp/_httpservice.cpp | 9 ++-- indra/llcorehttp/_refcounted.cpp | 4 +- indra/llcorehttp/bufferarray.cpp | 4 +- indra/llcorehttp/httpcommon.cpp | 2 +- indra/llcorehttp/httpoptions.cpp | 8 ++-- indra/llcorehttp/httprequest.cpp | 1 + indra/llcorehttp/httprequest.h | 6 +-- indra/newview/lltextureview.cpp | 2 +- 18 files changed, 161 insertions(+), 77 deletions(-) create mode 100644 indra/llcorehttp/_httpinternal.h diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 9d8bae973e..acf4c0d6a3 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -53,6 +53,7 @@ set(llcorehttp_HEADER_FILES httpoptions.h httprequest.h httpresponse.h + _httpinternal.h _httplibcurl.h _httpopcancel.h _httpoperation.h diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h new file mode 100644 index 0000000000..bc0bd6a2ab --- /dev/null +++ b/indra/llcorehttp/_httpinternal.h @@ -0,0 +1,82 @@ +/** + * @file httpinternal.h + * @brief Implementation constants and magic numbers + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_INTERNAL_H_ +#define _LLCORE_HTTP_INTERNAL_H_ + + +// If you find this included in a public interface header, +// something wrong is probably happening. + + +namespace LLCore +{ + +// Maxium number of policy classes that can be defined. +// *FIXME: Currently limited to the default class, extend. +const int POLICY_CLASS_LIMIT = 1; + +// Debug/informational tracing. Used both +// as a global option and in per-request traces. +const int TRACE_OFF = 0; +const int TRACE_LOW = 1; +const int TRACE_CURL_HEADERS = 2; +const int TRACE_CURL_BODIES = 3; + +const int TRACE_MIN = TRACE_OFF; +const int TRACE_MAX = TRACE_CURL_BODIES; + +// Request retry limits +const int DEFAULT_RETRY_COUNT = 5; +const int LIMIT_RETRY_MIN = 0; +const int LIMIT_RETRY_MAX = 100; + +const int DEFAULT_HTTP_REDIRECTS = 10; + +// Timeout value used for both connect and protocol exchange. +// Retries and time-on-queue are not included and aren't +// accounted for. +const long DEFAULT_TIMEOUT = 30L; +const long LIMIT_TIMEOUT_MIN = 0L; +const long LIMIT_TIMEOUT_MAX = 3600L; + +// Limits on connection counts +const int DEFAULT_CONNECTIONS = 8; +const int LIMIT_CONNECTIONS_MIN = 1; +const int LIMIT_CONNECTIONS_MAX = 256; + +// Tuning parameters + +// Time worker thread sleeps after a pass through the +// request, ready and active queues. +const int LOOP_SLEEP_NORMAL_MS = 2; + +// Block allocation size (a tuning parameter) is found +// in bufferarray.h. + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_INTERNAL_H_ diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index a176dd5b2a..65eb642056 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -31,6 +31,8 @@ #include "_httpoprequest.h" #include "_httppolicy.h" +#include "llhttpstatuscodes.h" + namespace LLCore { @@ -39,7 +41,8 @@ namespace LLCore HttpLibcurl::HttpLibcurl(HttpService * service) : mService(service) { - for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) + // *FIXME: Use active policy class count later + for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mMultiHandles); ++policy_class) { mMultiHandles[policy_class] = 0; } @@ -61,7 +64,7 @@ HttpLibcurl::~HttpLibcurl() mActiveOps.erase(item); } - for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) + for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mMultiHandles); ++policy_class) { if (mMultiHandles[policy_class]) { @@ -89,7 +92,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() HttpService::ELoopSpeed ret(HttpService::REQUEST_SLEEP); // Give libcurl some cycles to do I/O & callbacks - for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) + for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mMultiHandles); ++policy_class) { if (! mMultiHandles[policy_class]) continue; @@ -144,7 +147,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() void HttpLibcurl::addOp(HttpOpRequest * op) { - llassert_always(op->mReqPolicy < HttpRequest::POLICY_CLASS_LIMIT); + llassert_always(op->mReqPolicy < POLICY_CLASS_LIMIT); llassert_always(mMultiHandles[op->mReqPolicy] != NULL); // Create standard handle @@ -159,7 +162,7 @@ void HttpLibcurl::addOp(HttpOpRequest * op) curl_multi_add_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle); op->mCurlActive = true; - if (op->mTracing > 0) + if (op->mTracing > TRACE_OFF) { HttpPolicy & policy(mService->getPolicy()); @@ -208,7 +211,7 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode } if (op->mStatus) { - int http_status(200); + int http_status(HTTP_OK); curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_status); op->mStatus = LLCore::HttpStatus(http_status); @@ -220,7 +223,7 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode op->mCurlHandle = NULL; // Tracing - if (op->mTracing > 0) + if (op->mTracing > TRACE_OFF) { LL_INFOS("CoreHttp") << "TRACE, RequestComplete, Handle: " << static_cast(op) diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 16b68bde43..0d0c4cad6d 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -36,6 +36,7 @@ #include "httprequest.h" #include "_httpservice.h" +#include "_httpinternal.h" namespace LLCore @@ -91,17 +92,8 @@ protected: protected: HttpService * mService; // Simple reference, not owner active_set_t mActiveOps; - CURLM * mMultiHandles[HttpRequest::POLICY_CLASS_LIMIT]; -}; // end class HttpLibcurl - - -// --------------------------------------- -// Free functions -// --------------------------------------- - - -curl_slist * append_headers_to_slist(const HttpHeaders *, curl_slist * slist); - + CURLM * mMultiHandles[POLICY_CLASS_LIMIT]; +}; // end class HttpLibcurl } // end namespace LLCore diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index 5a31bf90e7..0d9553434e 100644 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -33,6 +33,7 @@ #include "_httprequestqueue.h" #include "_httpreplyqueue.h" #include "_httpservice.h" +#include "_httpinternal.h" #include "lltimer.h" @@ -142,7 +143,7 @@ HttpStatus HttpOperation::cancel() void HttpOperation::addAsReply() { - if (mTracing > 0) + if (mTracing > TRACE_OFF) { LL_INFOS("CoreHttp") << "TRACE, ToReplyQueue, Handle: " << static_cast(this) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index ce41ebcce0..04a8e0baff 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -42,6 +42,7 @@ #include "_httppolicy.h" #include "_httppolicyglobal.h" #include "_httplibcurl.h" +#include "_httpinternal.h" #include "llhttpstatuscodes.h" #include "llproxy.h" @@ -73,13 +74,14 @@ void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::string & safe_line); -#if defined(WIN32) +#if LL_WINDOWS // Not available on windows where the legacy strtok interface // is thread-safe. char *strtok_r(char *str, const char *delim, char **saveptr); -#endif +#endif // LL_WINDOWS + } @@ -108,7 +110,7 @@ HttpOpRequest::HttpOpRequest() mReplyHeaders(NULL), mPolicyRetries(0), mPolicyRetryAt(HttpTime(0)), - mPolicyRetryLimit(5) + mPolicyRetryLimit(DEFAULT_RETRY_COUNT) { // *NOTE: As members are added, retry initialization/cleanup // may need to be extended in @prepareRequest(). @@ -334,8 +336,8 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id, mProcFlags |= PF_SAVE_HEADERS; } mPolicyRetryLimit = options->getRetries(); - mPolicyRetryLimit = llclamp(mPolicyRetryLimit, 0, 100); - mTracing = (std::max)(mTracing, llclamp(options->getTrace(), 0, 3)); + mPolicyRetryLimit = llclamp(mPolicyRetryLimit, LIMIT_RETRY_MIN, LIMIT_RETRY_MAX); + mTracing = (std::max)(mTracing, llclamp(options->getTrace(), TRACE_MIN, TRACE_MAX)); } } @@ -384,7 +386,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1); curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, 10); // *FIXME: parameterize this later + curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, DEFAULT_HTTP_REDIRECTS); // *FIXME: parameterize this later curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback); curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, mCurlHandle); curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback); @@ -452,8 +454,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size); curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL); mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); - mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); - mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); } break; @@ -463,7 +463,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } // Tracing - if (mTracing > 1) + if (mTracing >= TRACE_CURL_HEADERS) { curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1); curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, mCurlHandle); @@ -478,7 +478,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) char range_line[64]; -#if defined(WIN32) +#if LL_WINDOWS _snprintf_s(range_line, sizeof(range_line), sizeof(range_line) - 1, (mReqLength ? fmt1 : fmt2), (unsigned long) mReqOffset, (unsigned long) (mReqOffset + mReqLength - 1)); @@ -486,7 +486,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) snprintf(range_line, sizeof(range_line), (mReqLength ? fmt1 : fmt2), (unsigned long) mReqOffset, (unsigned long) (mReqOffset + mReqLength - 1)); -#endif // defined(WIN32) +#endif // LL_WINDOWS range_line[sizeof(range_line) - 1] = '\0'; mCurlHeaders = curl_slist_append(mCurlHeaders, range_line); } @@ -494,11 +494,11 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); // Request options - long timeout(30); + long timeout(DEFAULT_TIMEOUT); if (mReqOptions) { timeout = mReqOptions->getTimeout(); - timeout = llclamp(timeout, 0L, 3600L); + timeout = llclamp(timeout, LIMIT_TIMEOUT_MIN, LIMIT_TIMEOUT_MAX); } curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, timeout); curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout); @@ -599,11 +599,11 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi memcpy(hdr_buffer, hdr_data, frag_size); hdr_buffer[frag_size] = '\0'; -#if defined(WIN32) +#if LL_WINDOWS if (! _strnicmp(hdr_buffer, con_ran_line, (std::min)(frag_size, con_ran_line_len))) #else if (! strncasecmp(hdr_buffer, con_ran_line, (std::min)(frag_size, con_ran_line_len))) -#endif +#endif // LL_WINDOWS { unsigned int first(0), last(0), length(0); int status; @@ -654,7 +654,7 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe switch (info) { case CURLINFO_TEXT: - if (op->mTracing > 1) + if (op->mTracing >= TRACE_CURL_HEADERS) { tag = "TEXT"; escape_libcurl_debug_data(buffer, len, true, safe_line); @@ -663,7 +663,7 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe break; case CURLINFO_HEADER_IN: - if (op->mTracing > 1) + if (op->mTracing >= TRACE_CURL_HEADERS) { tag = "HEADERIN"; escape_libcurl_debug_data(buffer, len, true, safe_line); @@ -672,20 +672,20 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe break; case CURLINFO_HEADER_OUT: - if (op->mTracing > 1) + if (op->mTracing >= TRACE_CURL_HEADERS) { tag = "HEADEROUT"; - escape_libcurl_debug_data(buffer, len, true, safe_line); + escape_libcurl_debug_data(buffer, 2 * len, true, safe_line); // Goes out as one line logit = true; } break; case CURLINFO_DATA_IN: - if (op->mTracing > 1) + if (op->mTracing >= TRACE_CURL_HEADERS) { tag = "DATAIN"; logit = true; - if (op->mTracing > 2) + if (op->mTracing >= TRACE_CURL_BODIES) { escape_libcurl_debug_data(buffer, len, false, safe_line); } @@ -699,11 +699,11 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe break; case CURLINFO_DATA_OUT: - if (op->mTracing > 1) + if (op->mTracing >= TRACE_CURL_HEADERS) { tag = "DATAOUT"; logit = true; - if (op->mTracing > 2) + if (op->mTracing >= TRACE_CURL_BODIES) { escape_libcurl_debug_data(buffer, len, false, safe_line); } @@ -755,22 +755,22 @@ int parse_content_range_header(char * buffer, if (! strtok_r(buffer, ": \t", &tok_state)) match = false; if (match && (tok = strtok_r(NULL, " \t", &tok_state))) -#if defined(WIN32) +#if LL_WINDOWS match = 0 == _stricmp("bytes", tok); #else match = 0 == strcasecmp("bytes", tok); -#endif +#endif // LL_WINDOWS if (match && ! (tok = strtok_r(NULL, " \t", &tok_state))) match = false; if (match) { unsigned int lcl_first(0), lcl_last(0), lcl_len(0); -#if defined(WIN32) +#if LL_WINDOWS if (3 == sscanf_s(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len)) #else if (3 == sscanf(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len)) -#endif +#endif // LL_WINDOWS { if (lcl_first > lcl_last || lcl_last >= lcl_len) return -1; @@ -779,11 +779,11 @@ int parse_content_range_header(char * buffer, *length = lcl_len; return 0; } -#if defined(WIN32) +#if LL_WINDOWS if (2 == sscanf_s(tok, "%u-%u/*", &lcl_first, &lcl_last)) #else if (2 == sscanf(tok, "%u-%u/*", &lcl_first, &lcl_last)) -#endif +#endif // LL_WINDOWS { if (lcl_first > lcl_last) return -1; @@ -798,14 +798,14 @@ int parse_content_range_header(char * buffer, return 1; } -#if defined(WIN32) +#if LL_WINDOWS char *strtok_r(char *str, const char *delim, char ** savestate) { return strtok_s(str, delim, savestate); } -#endif +#endif // LL_WINDOWS void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::string & safe_line) diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 9278445763..f2b709a3a2 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -172,6 +172,12 @@ public: }; // end class HttpOpRequestCompare +// --------------------------------------- +// Free functions +// --------------------------------------- + +curl_slist * append_headers_to_slist(const HttpHeaders *, curl_slist * slist); + } // end namespace LLCore #endif // _LLCORE_HTTP_OPREQUEST_H_ diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 0e08d88276..4be9f1d45f 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -126,7 +126,7 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mState); ++policy_class) { int active(transport.getActiveCountInClass(policy_class)); - int needed(8 - active); + int needed(DEFAULT_CONNECTIONS - active); // *FIXME: move to policy class HttpRetryQueue & retryq(mState[policy_class].mRetryQueue); HttpReadyQueue & readyq(mState[policy_class].mReadyQueue); @@ -242,7 +242,7 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) { - if (policy_class < HttpRequest::POLICY_CLASS_LIMIT) + if (policy_class < POLICY_CLASS_LIMIT) // *FIXME: use actual active class count { return (mState[policy_class].mReadyQueue.size() + mState[policy_class].mRetryQueue.size()); diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 4114f64848..05de9303b5 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -33,6 +33,7 @@ #include "_httpreadyqueue.h" #include "_httpretryqueue.h" #include "_httppolicyglobal.h" +#include "_httpinternal.h" namespace LLCore @@ -108,7 +109,7 @@ protected: HttpRetryQueue mRetryQueue; }; - State mState[HttpRequest::POLICY_CLASS_LIMIT]; + State mState[POLICY_CLASS_LIMIT]; HttpService * mService; // Naked pointer, not refcounted, not owner HttpPolicyGlobal mGlobalOptions; diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp index 6b1de38fd6..ca04839eaf 100644 --- a/indra/llcorehttp/_httppolicyglobal.cpp +++ b/indra/llcorehttp/_httppolicyglobal.cpp @@ -26,6 +26,8 @@ #include "_httppolicyglobal.h" +#include "_httpinternal.h" + namespace LLCore { @@ -33,8 +35,8 @@ namespace LLCore HttpPolicyGlobal::HttpPolicyGlobal() : mSetMask(0UL), - mConnectionLimit(32L), - mTrace(0), + mConnectionLimit(DEFAULT_CONNECTIONS), + mTrace(TRACE_OFF), mUseLLProxy(0) {} @@ -64,11 +66,11 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value) switch (opt) { case HttpRequest::GP_CONNECTION_LIMIT: - mConnectionLimit = value; + mConnectionLimit = llclamp(value, long(LIMIT_CONNECTIONS_MIN), long(LIMIT_CONNECTIONS_MAX)); break; case HttpRequest::GP_TRACE: - mTrace = llclamp(value, 0L, 3L); + mTrace = llclamp(value, long(TRACE_MIN), long(TRACE_MAX)); break; case HttpRequest::GP_LLPROXY: diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index 87a78820f5..25f64acc42 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -34,15 +34,12 @@ #include "_httppolicy.h" #include "_httplibcurl.h" #include "_thread.h" +#include "_httpinternal.h" #include "lltimer.h" #include "llthread.h" -// Tuning parameters -static const int LOOP_SLEEP_NORMAL_MS = 2; // Normal per-loop sleep in milliseconds - - namespace LLCore { @@ -230,11 +227,11 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop) if (! mExitRequested) { // Setup for subsequent tracing - long tracing(0); + long tracing(TRACE_OFF); mPolicy->getGlobalOptions().get(HttpRequest::GP_TRACE, &tracing); op->mTracing = (std::max)(op->mTracing, int(tracing)); - if (op->mTracing > 0) + if (op->mTracing > TRACE_OFF) { LL_INFOS("CoreHttp") << "TRACE, FromRequestQueue, Handle: " << static_cast(op) diff --git a/indra/llcorehttp/_refcounted.cpp b/indra/llcorehttp/_refcounted.cpp index 11d75fdf97..e7d0b72741 100644 --- a/indra/llcorehttp/_refcounted.cpp +++ b/indra/llcorehttp/_refcounted.cpp @@ -30,11 +30,11 @@ namespace LLCoreInt { -#if ! defined(WIN32) +#if ! LL_WINDOWS const S32 RefCounted::NOT_REF_COUNTED; -#endif // ! defined(WIN32) +#endif // ! LL_WINDOWS RefCounted::~RefCounted() {} diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index ae92057df0..5eaa60c9ba 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -85,9 +85,9 @@ public: // ================================== -#if ! defined(WIN32) +#if ! LL_WINDOWS const size_t BufferArray::BLOCK_ALLOC_SIZE; -#endif // ! defined(WIN32) +#endif // ! LL_WINDOWS BufferArray::BufferArray() : LLCoreInt::RefCounted(true), diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp index 9f17b5c842..1b18976359 100644 --- a/indra/llcorehttp/httpcommon.cpp +++ b/indra/llcorehttp/httpcommon.cpp @@ -54,9 +54,9 @@ std::string HttpStatus::toHex() const result.fill('0'); result << std::hex << operator unsigned long(); return result.str(); - } + std::string HttpStatus::toString() const { static const char * llcore_errors[] = diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp index c11d89e619..f2771c1f29 100644 --- a/indra/llcorehttp/httpoptions.cpp +++ b/indra/llcorehttp/httpoptions.cpp @@ -26,6 +26,8 @@ #include "httpoptions.h" +#include "_httpinternal.h" + namespace LLCore { @@ -34,9 +36,9 @@ namespace LLCore HttpOptions::HttpOptions() : RefCounted(true), mWantHeaders(false), - mTracing(0), - mTimeout(30), - mRetries(5) + mTracing(TRACE_OFF), + mTimeout(DEFAULT_TIMEOUT), + mRetries(DEFAULT_RETRY_COUNT) {} diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 2036ecfd1c..e906ff8a1e 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -108,6 +108,7 @@ HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, const std::stri HttpRequest::policy_t HttpRequest::createPolicyClass() { + // *FIXME: Implement classes policy_t policy_id = 1; return policy_id; diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 4e78ed3719..24fff24b83 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -102,10 +102,6 @@ public: /// eventual service for any HTTP request. static const int DEFAULT_POLICY_ID = 0; - /// Maximum number of policies that may be defined. No policy - /// ID will equal or exceed this value. - static const int POLICY_CLASS_LIMIT = 1; - enum EGlobalPolicy { /// Maximum number of connections the library will use to @@ -163,7 +159,7 @@ public: /// Create a new policy class into which requests can be made. /// /// @return If positive, the policy_id used to reference - /// the class in other methods. If -1, an error + /// the class in other methods. If 0, an error /// occurred and @see getStatus() may provide more /// detail on the reason. static policy_t createPolicyClass(); diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index a4227ec2ff..ae2da84edd 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2012, 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 -- cgit v1.2.3 From 6b4fe9fadc2301eb13a2854457b67fbebe045b08 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 19 Jun 2012 17:12:20 -0400 Subject: When a Content-Range header is received, make available the full triplet of . --- indra/llcorehttp/_httpoprequest.cpp | 7 ++++++- indra/llcorehttp/_httpoprequest.h | 1 + indra/llcorehttp/httpresponse.cpp | 1 + indra/llcorehttp/httpresponse.h | 7 +++++-- indra/newview/lltexturefetch.cpp | 4 ++-- 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 04a8e0baff..516daadf9b 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -107,6 +107,7 @@ HttpOpRequest::HttpOpRequest() mReplyBody(NULL), mReplyOffset(0), mReplyLength(0), + mReplyFullLength(0), mReplyHeaders(NULL), mPolicyRetries(0), mPolicyRetryAt(HttpTime(0)), @@ -154,6 +155,7 @@ HttpOpRequest::~HttpOpRequest() mReplyOffset = 0; mReplyLength = 0; + mReplyFullLength = 0; if (mReplyBody) { mReplyBody->release(); @@ -224,7 +226,7 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) if (mReplyOffset || mReplyLength) { // Got an explicit offset/length in response - response->setRange(mReplyOffset, mReplyLength); + response->setRange(mReplyOffset, mReplyLength, mReplyFullLength); } mUserHandler->onCompleted(static_cast(this), response); @@ -362,6 +364,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } mReplyOffset = 0; mReplyLength = 0; + mReplyFullLength = 0; if (mReplyHeaders) { mReplyHeaders->release(); @@ -590,6 +593,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi // taking results from the last header stanza we receive. op->mReplyOffset = 0; op->mReplyLength = 0; + op->mReplyFullLength = 0; op->mStatus = HttpStatus(); } else if (op->mProcFlags & PF_SCAN_RANGE_HEADER) @@ -613,6 +617,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi // Success, record the fragment position op->mReplyOffset = first; op->mReplyLength = last - first + 1; + op->mReplyFullLength = length; } else if (-1 == status) { diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index f2b709a3a2..5d2417466c 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -150,6 +150,7 @@ public: BufferArray * mReplyBody; off_t mReplyOffset; size_t mReplyLength; + size_t mReplyFullLength; HttpHeaders * mReplyHeaders; // Policy data diff --git a/indra/llcorehttp/httpresponse.cpp b/indra/llcorehttp/httpresponse.cpp index 3dcdadb337..a552e48a1b 100644 --- a/indra/llcorehttp/httpresponse.cpp +++ b/indra/llcorehttp/httpresponse.cpp @@ -37,6 +37,7 @@ HttpResponse::HttpResponse() : LLCoreInt::RefCounted(true), mReplyOffset(0U), mReplyLength(0U), + mReplyFullLength(0U), mBufferArray(NULL), mHeaders(NULL) {} diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h index 5bcd7c4eb8..925cf81586 100644 --- a/indra/llcorehttp/httpresponse.h +++ b/indra/llcorehttp/httpresponse.h @@ -111,16 +111,18 @@ public: /// If a 'Range:' header was used, these methods are involved /// in setting and returning data about the actual response. - void getRange(unsigned int * offset, unsigned int * length) const + void getRange(unsigned int * offset, unsigned int * length, unsigned int * full) const { *offset = mReplyOffset; *length = mReplyLength; + *full = mReplyFullLength; } - void setRange(unsigned int offset, unsigned int length) + void setRange(unsigned int offset, unsigned int length, unsigned int full_length) { mReplyOffset = offset; mReplyLength = length; + mReplyFullLength = full_length; } protected: @@ -128,6 +130,7 @@ protected: HttpStatus mStatus; unsigned int mReplyOffset; unsigned int mReplyLength; + unsigned int mReplyFullLength; BufferArray * mBufferArray; HttpHeaders * mHeaders; }; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 6e14c5fc63..2eda6a9b80 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1735,8 +1735,8 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe << " status: " << status.toHex() << " '" << status.toString() << "'" << llendl; -// unsigned int offset(0), length(0); -// response->getRange(&offset, &length); +// unsigned int offset(0), length(0), full_length(0); +// response->getRange(&offset, &length, &full_length); // llwarns << "HTTP COMPLETE: " << mID << " handle: " << handle // << " status: " << status.toULong() << " '" << status.toString() << "'" // << " req offset: " << mRequestedOffset << " req length: " << mRequestedSize -- cgit v1.2.3 From a066bc1994fccae7967921980332505aac97953f Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 20 Jun 2012 18:43:28 -0400 Subject: SH-3181 More interface options for API. Also includes returned headers. Only thing interesting in this changeset is the discovery that a sleep in the fake HTTP server ties up tests. Need to thread that or fail on client disconnect or something to speed that up and make it usable for bigger test scenarios. But good enough for now... --- indra/llcorehttp/_httpoprequest.cpp | 20 +++- indra/llcorehttp/tests/test_httprequest.hpp | 156 ++++++++++++++++++++++++- indra/llcorehttp/tests/test_llcorehttp_peer.py | 1 + indra/newview/lltexturefetch.cpp | 5 +- 4 files changed, 175 insertions(+), 7 deletions(-) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 516daadf9b..86ecee5b26 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -595,6 +595,10 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi op->mReplyLength = 0; op->mReplyFullLength = 0; op->mStatus = HttpStatus(); + if (op->mReplyHeaders) + { + op->mReplyHeaders->mHeaders.clear(); + } } else if (op->mProcFlags & PF_SCAN_RANGE_HEADER) { @@ -636,9 +640,19 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi if (op->mProcFlags & PF_SAVE_HEADERS) { // Save headers in response - // *FIXME: Implement this... - ; - + if (! op->mReplyHeaders) + { + op->mReplyHeaders = new HttpHeaders; + } + size_t wanted_size(hdr_size); + if (wanted_size && '\n' == hdr_data[wanted_size - 1]) + { + if (--wanted_size && '\r' == hdr_data[wanted_size - 1]) + { + --wanted_size; + } + } + op->mReplyHeaders->mHeaders.push_back(std::string(hdr_data, wanted_size)); } return hdr_size; diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 42e4857037..cac927cfca 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -29,6 +29,7 @@ #include "httprequest.h" #include "bufferarray.h" #include "httphandler.h" +#include "httpheaders.h" #include "httpresponse.h" #include "httpoptions.h" #include "_httpservice.h" @@ -73,7 +74,8 @@ public: const std::string & name) : mState(state), mName(name), - mExpectHandle(LLCORE_HTTP_HANDLE_INVALID) + mExpectHandle(LLCORE_HTTP_HANDLE_INVALID), + mCheckHeader(false) {} virtual void onCompleted(HttpHandle handle, HttpResponse * response) @@ -95,12 +97,33 @@ public: { mState->mHandlerCalls++; } + if (mCheckHeader) + { + ensure("Response required with header check", response != NULL); + HttpHeaders * header(response->getHeaders()); // Will not hold onto this + ensure("Some quantity of headers returned", header != NULL); + bool found_special(false); + + for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin()); + header->mHeaders.end() != iter; + ++iter) + { + if (std::string::npos != (*iter).find("X-LL-Special")) + { + found_special = true; + break; + } + } + ensure("Special header X-LL-Special in response", found_special); + } + // std::cout << "TestHandler2::onCompleted() invoked" << std::endl; } HttpRequestTestData * mState; std::string mName; HttpHandle mExpectHandle; + bool mCheckHeader; }; typedef test_group HttpRequestTestGroupType; @@ -1085,6 +1108,135 @@ void HttpRequestTestObjectType::test<11>() { ScopedCurlInit ready; + std::string url_base(get_base_url()); + // std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET with returned headers"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * opts = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Enable tracing + HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + opts = new HttpOptions(); + opts->setWantHeaders(); + + // Issue a GET that succeeds + mStatus = HttpStatus(200); + handler.mCheckHeader = true; + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + 0, + 0, + opts, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // release options + opts->release(); + opts = NULL; + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mCheckHeader = false; + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + if (opts) + { + opts->release(); + opts = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + +// *NB: This test must be last. The sleeping webserver +// won't respond for a long time. +template <> template <> +void HttpRequestTestObjectType::test<12>() +{ + ScopedCurlInit ready; + set_test_name("HttpRequest GET timeout"); // Handler can be stack-allocated *if* there are no dangling @@ -1117,7 +1269,7 @@ void HttpRequestTestObjectType::test<11>() opts->setRetries(0); // Don't retry opts->setTimeout(2); - // Issue a GET that can't connect + // Issue a GET that sleeps mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT); HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, 0U, diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 5f0116d384..aedd8acd83 100644 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -107,6 +107,7 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): self.send_response(200) self.send_header("Content-type", "application/llsd+xml") self.send_header("Content-Length", str(len(response))) + self.send_header("X-LL-Special", "Mememememe"); self.end_headers() self.wfile.write(response) else: # fail requested diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 2eda6a9b80..4995d9f5ea 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1043,8 +1043,9 @@ void LLTextureFetchWorker::startWork(S32 param) // Threads: Ttf bool LLTextureFetchWorker::doWork(S32 param) { - static const LLCore::HttpStatus http_not_found(HTTP_NOT_FOUND); - static const LLCore::HttpStatus http_service_unavail(HTTP_SERVICE_UNAVAILABLE); + static const LLCore::HttpStatus http_not_found(HTTP_NOT_FOUND); // 404 + static const LLCore::HttpStatus http_service_unavail(HTTP_SERVICE_UNAVAILABLE); // 503 + static const LLCore::HttpStatus http_not_sat(HTTP_REQUESTED_RANGE_NOT_SATISFIABLE); // 416; // Release waiters while we aren't holding the Mw lock. mFetcher->releaseHttpWaiters(); -- cgit v1.2.3 From 4da93b6ad91dff1de98c3c8dd3674c0544f2958b Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 21 Jun 2012 19:45:40 -0400 Subject: SH-3177 Add streambuf/iostream adapters to BufferArray object. Initial version that should have enough of the plumbing to produce a working adapter. Memory test is showing 8 bytes held after one of the tests so I'm going to revisit that later. But basic functionality is there going by the unit tests. --- indra/llcorehttp/CMakeLists.txt | 3 + indra/llcorehttp/bufferarray.cpp | 14 ++ indra/llcorehttp/bufferarray.h | 8 + indra/llcorehttp/bufferstream.cpp | 285 +++++++++++++++++++++++++++ indra/llcorehttp/bufferstream.h | 94 +++++++++ indra/llcorehttp/tests/llcorehttp_test.cpp | 3 +- indra/llcorehttp/tests/test_bufferarray.hpp | 2 +- indra/llcorehttp/tests/test_bufferstream.hpp | 252 +++++++++++++++++++++++ 8 files changed, 659 insertions(+), 2 deletions(-) create mode 100644 indra/llcorehttp/bufferstream.cpp create mode 100644 indra/llcorehttp/bufferstream.h create mode 100644 indra/llcorehttp/tests/test_bufferstream.hpp diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index acf4c0d6a3..4c00cc04e9 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -24,6 +24,7 @@ include_directories( set(llcorehttp_SOURCE_FILES bufferarray.cpp + bufferstream.cpp httpcommon.cpp httpheaders.cpp httpoptions.cpp @@ -47,6 +48,7 @@ set(llcorehttp_HEADER_FILES CMakeLists.txt bufferarray.h + bufferstream.h httpcommon.h httphandler.h httpheaders.h @@ -105,6 +107,7 @@ if (LL_TESTS) tests/test_httprequestqueue.hpp tests/test_httpheaders.hpp tests/test_bufferarray.hpp + tests/test_bufferstream.hpp ) set_source_files_properties(${llcorehttp_TEST_HEADER_FILES} diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index 5eaa60c9ba..5eb8f84c34 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -288,6 +288,20 @@ int BufferArray::findBlock(size_t pos, size_t * ret_offset) } +bool BufferArray::getBlockStartEnd(int block, const char ** start, const char ** end) +{ + if (block < 0 || block >= mBlocks.size()) + { + return false; + } + + const Block & b(*mBlocks[block]); + *start = &b.mData[0]; + *end = &b.mData[b.mUsed]; + return true; +} + + // ================================== // BufferArray::Block Definitions // ================================== diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h index d0c51d3c73..1094a435b4 100644 --- a/indra/llcorehttp/bufferarray.h +++ b/indra/llcorehttp/bufferarray.h @@ -37,6 +37,7 @@ namespace LLCore { +class BufferArrayStreamBuf; /// A very simple scatter/gather type map for bulk data. The motivation /// for this class is the writedata callback used by libcurl. Response @@ -65,6 +66,11 @@ namespace LLCore class BufferArray : public LLCoreInt::RefCounted { public: + // BufferArrayStreamBuf has intimate knowledge of this + // implementation to implement a buffer-free adapter. + // Changes here will likely need to be reflected there. + friend class BufferArrayStreamBuf; + BufferArray(); protected: @@ -114,6 +120,8 @@ public: protected: int findBlock(size_t pos, size_t * ret_offset); + + bool getBlockStartEnd(int block, const char ** start, const char ** end); protected: class Block; diff --git a/indra/llcorehttp/bufferstream.cpp b/indra/llcorehttp/bufferstream.cpp new file mode 100644 index 0000000000..6553900eef --- /dev/null +++ b/indra/llcorehttp/bufferstream.cpp @@ -0,0 +1,285 @@ +/** + * @file bufferstream.cpp + * @brief Implements the BufferStream adapter class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "bufferstream.h" + +#include "bufferarray.h" + + +namespace LLCore +{ + +BufferArrayStreamBuf::BufferArrayStreamBuf(BufferArray * array) + : mBufferArray(array), + mReadCurPos(0), + mReadCurBlock(-1), + mReadBegin(NULL), + mReadCur(NULL), + mReadEnd(NULL), + mWriteCurPos(0) +{ + if (array) + { + array->addRef(); + mWriteCurPos = array->mLen; + } +} + + +BufferArrayStreamBuf::~BufferArrayStreamBuf() +{ + if (mBufferArray) + { + mBufferArray->release(); + mBufferArray = NULL; + } +} + + +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::underflow() +{ + if (! mBufferArray) + { + return traits_type::eof(); + } + + if (mReadCur == mReadEnd) + { + // Find the next block with actual data or leave + // mCurBlock/mCur/mEnd unchanged if we're at the end + // of any block chain. + const char * new_begin(NULL), * new_end(NULL); + int new_cur_block(mReadCurBlock + 1); + + while (mBufferArray->getBlockStartEnd(new_cur_block, &new_begin, &new_end)) + { + if (new_begin != new_end) + { + break; + } + ++new_cur_block; + } + if (new_begin == new_end) + { + return traits_type::eof(); + } + + mReadCurBlock = new_cur_block; + mReadBegin = mReadCur = new_begin; + mReadEnd = new_end; + } + + return traits_type::to_int_type(*mReadCur); +} + + +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::uflow() +{ + const int_type ret(underflow()); + + if (traits_type::eof() != ret) + { + ++mReadCur; + ++mReadCurPos; + } + return ret; +} + + +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::pbackfail(int_type ch) +{ + if (! mBufferArray) + { + return traits_type::eof(); + } + + if (mReadCur == mReadBegin) + { + // Find the previous block with actual data or leave + // mCurBlock/mBegin/mCur/mEnd unchanged if we're at the + // beginning of any block chain. + const char * new_begin(NULL), * new_end(NULL); + int new_cur_block(mReadCurBlock - 1); + + while (mBufferArray->getBlockStartEnd(new_cur_block, &new_begin, &new_end)) + { + if (new_begin != new_end) + { + break; + } + --new_cur_block; + } + if (new_begin == new_end) + { + return traits_type::eof(); + } + + mReadCurBlock = new_cur_block; + mReadBegin = new_begin; + mReadEnd = mReadCur = new_end; + } + + if (traits_type::eof() != ch && mReadCur[-1] != ch) + { + return traits_type::eof(); + } + --mReadCurPos; + return traits_type::to_int_type(*--mReadCur); +} + + +std::streamsize BufferArrayStreamBuf::showmanyc() +{ + if (! mBufferArray) + { + return -1; + } + return mBufferArray->mLen - mReadCurPos; +} + + +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::overflow(int c) +{ + if (! mBufferArray || mWriteCurPos > mBufferArray->mLen) + { + return traits_type::eof(); + } + const size_t wrote(mBufferArray->write(mWriteCurPos, &c, 1)); + mWriteCurPos += wrote; + return wrote ? c : traits_type::eof(); +} + + +std::streamsize BufferArrayStreamBuf::xsputn(const char * src, std::streamsize count) +{ + if (! mBufferArray || mWriteCurPos > mBufferArray->mLen) + { + return 0; + } + const size_t wrote(mBufferArray->write(mWriteCurPos, src, count)); + mWriteCurPos += wrote; + return wrote; +} + + +std::streampos BufferArrayStreamBuf::seekoff(std::streamoff off, + std::ios_base::seekdir way, + std::ios_base::openmode which) +{ + std::streampos ret(-1); + + if (! mBufferArray) + { + return ret; + } + + if (std::ios_base::in == which) + { + size_t pos(0); + + switch (way) + { + case std::ios_base::beg: + pos = off; + break; + + case std::ios_base::cur: + pos = mReadCurPos += off; + break; + + case std::ios_base::end: + pos = mBufferArray->mLen - off; + break; + + default: + return ret; + } + + if (pos >= mBufferArray->size()) + { + pos = (std::max)(size_t(0), mBufferArray->size() - 1); + } + size_t ba_offset(0); + int block(mBufferArray->findBlock(pos, &ba_offset)); + if (block < 0) + return ret; + const char * start(NULL), * end(NULL); + if (! mBufferArray->getBlockStartEnd(block, &start, &end)) + return ret; + mReadCurBlock = block; + mReadBegin = start; + mReadCur = start + ba_offset; + mReadEnd = end; + ret = mReadCurPos = pos; + } + else if (std::ios_base::out == which) + { + size_t pos(0); + + switch (way) + { + case std::ios_base::beg: + pos = off; + break; + + case std::ios_base::cur: + pos = mWriteCurPos += off; + break; + + case std::ios_base::end: + pos = mBufferArray->mLen - off; + break; + + default: + return ret; + } + + if (pos < 0) + return ret; + if (pos > mBufferArray->size()) + { + pos = mBufferArray->size(); + } + ret = mWriteCurPos = pos; + } + + return ret; +} + + +BufferArrayStream::BufferArrayStream(BufferArray * ba) + : std::iostream(&mStreamBuf), + mStreamBuf(ba) +{} + + +BufferArrayStream::~BufferArrayStream() +{} + + +} // end namespace LLCore + + diff --git a/indra/llcorehttp/bufferstream.h b/indra/llcorehttp/bufferstream.h new file mode 100644 index 0000000000..60bda9ff9a --- /dev/null +++ b/indra/llcorehttp/bufferstream.h @@ -0,0 +1,94 @@ +/** + * @file bufferstream.h + * @brief Public-facing declaration for the BufferStream adapter class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_BUFFER_STREAM_H_ +#define _LLCORE_BUFFER_STREAM_H_ + + +#include +#include + +#include "bufferarray.h" + + +namespace LLCore +{ + + +class BufferArrayStreamBuf : public std::streambuf +{ +public: + BufferArrayStreamBuf(BufferArray * array); + virtual ~BufferArrayStreamBuf(); + +private: + BufferArrayStreamBuf(const BufferArrayStreamBuf &); // Not defined + void operator=(const BufferArrayStreamBuf &); // Not defined + +public: + // Input interfaces from std::streambuf + int_type underflow(); + int_type uflow(); + int_type pbackfail(int_type ch); + std::streamsize showmanyc(); + + // Output interfaces from std::streambuf + int_type overflow(int c); + std::streamsize xsputn(const char * src, std::streamsize count); + + // Common/misc interfaces from std::streambuf + std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which); + +protected: + BufferArray * mBufferArray; // Ref counted + size_t mReadCurPos; + int mReadCurBlock; + const char * mReadBegin; + const char * mReadCur; + const char * mReadEnd; + size_t mWriteCurPos; + +}; // end class BufferArrayStreamBuf + + +class BufferArrayStream : public std::iostream +{ +public: + BufferArrayStream(BufferArray * ba); + ~BufferArrayStream(); + +protected: + BufferArrayStream(const BufferArrayStream &); + void operator=(const BufferArrayStream &); + +protected: + BufferArrayStreamBuf mStreamBuf; +}; // end class BufferArrayStream + + +} // end namespace LLCore + +#endif // _LLCORE_BUFFER_STREAM_H_ diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index 2b36d3a982..e863ddd13f 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -36,12 +36,13 @@ #include "../test/lltut.h" // Pull in each of the test sets +#include "test_bufferarray.hpp" +#include "test_bufferstream.hpp" #include "test_httpstatus.hpp" #include "test_refcounted.hpp" #include "test_httpoperation.hpp" #include "test_httprequest.hpp" #include "test_httpheaders.hpp" -#include "test_bufferarray.hpp" #include "test_httprequestqueue.hpp" #include "llproxy.h" diff --git a/indra/llcorehttp/tests/test_bufferarray.hpp b/indra/llcorehttp/tests/test_bufferarray.hpp index 3f947db967..8a2a64d970 100644 --- a/indra/llcorehttp/tests/test_bufferarray.hpp +++ b/indra/llcorehttp/tests/test_bufferarray.hpp @@ -33,7 +33,7 @@ #include "test_allocator.h" -using namespace LLCoreInt; +using namespace LLCore; diff --git a/indra/llcorehttp/tests/test_bufferstream.hpp b/indra/llcorehttp/tests/test_bufferstream.hpp new file mode 100644 index 0000000000..5afaba0966 --- /dev/null +++ b/indra/llcorehttp/tests/test_bufferstream.hpp @@ -0,0 +1,252 @@ +/** + * @file test_bufferstream.hpp + * @brief unit tests for the LLCore::BufferArrayStreamBuf/BufferArrayStream classes + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_BUFFER_STREAM_H_ +#define TEST_LLCORE_BUFFER_STREAM_H_ + +#include "bufferstream.h" + +#include + +#include "test_allocator.h" + + +using namespace LLCore; + + +namespace tut +{ + +struct BufferStreamTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; +}; + +typedef test_group BufferStreamTestGroupType; +typedef BufferStreamTestGroupType::object BufferStreamTestObjectType; +BufferStreamTestGroupType BufferStreamTestGroup("BufferStream Tests"); +typedef BufferArrayStreamBuf::traits_type tst_traits_t; + + +template <> template <> +void BufferStreamTestObjectType::test<1>() +{ + set_test_name("BufferArrayStreamBuf construction with NULL BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(NULL); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // Not much will work with a NULL + ensure("underflow() on NULL fails", tst_traits_t::eof() == bsb->underflow()); + ensure("uflow() on NULL fails", tst_traits_t::eof() == bsb->uflow()); + ensure("pbackfail() on NULL fails", tst_traits_t::eof() == bsb->pbackfail('c')); + ensure("showmanyc() on NULL fails", bsb->showmanyc() == -1); + ensure("overflow() on NULL fails", tst_traits_t::eof() == bsb->overflow('c')); + ensure("xsputn() on NULL fails", bsb->xsputn("blah", 4) == 0); + ensure("seekoff() on NULL fails", bsb->seekoff(0, std::ios_base::beg, std::ios_base::in) == std::streampos(-1)); + + // release the implicit reference, causing the object to be released + delete bsb; + bsb = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<2>() +{ + set_test_name("BufferArrayStream construction with NULL BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArrayStream * bas = new BufferArrayStream(NULL); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // Not much will work with a NULL here + ensure("eof() is false on NULL", ! bas->eof()); + ensure("fail() is false on NULL", ! bas->fail()); + ensure("good() on NULL", bas->good()); + + // release the implicit reference, causing the object to be released + delete bas; + bas = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<3>() +{ + set_test_name("BufferArrayStreamBuf construction with empty BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // I can release my ref on the BA + ba->release(); + ba = NULL; + + // release the implicit reference, causing the object to be released + delete bsb; + bsb = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<4>() +{ + set_test_name("BufferArrayStream construction with empty BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + + { + // create a new ref counted object with an implicit reference + BufferArrayStream bas(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + } + + // release the implicit reference, causing the object to be released + ba->release(); + ba = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<5>() +{ + set_test_name("BufferArrayStreamBuf construction with real BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + const char * content("This is a string. A fragment."); + const size_t c_len(strlen(content)); + ba->append(content, c_len); + BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // I can release my ref on the BA + ba->release(); + ba = NULL; + + // Various static state + ensure("underflow() returns 'T'", bsb->underflow() == 'T'); + ensure("underflow() returns 'T' again", bsb->underflow() == 'T'); + ensure("uflow() returns 'T'", bsb->uflow() == 'T'); + ensure("uflow() returns 'h'", bsb->uflow() == 'h'); + ensure("pbackfail('i') fails", tst_traits_t::eof() == bsb->pbackfail('i')); + ensure("pbackfail('T') fails", tst_traits_t::eof() == bsb->pbackfail('T')); + ensure("pbackfail('h') succeeds", bsb->pbackfail('h') == 'h'); + ensure("showmanyc() is everything but the 'T'", bsb->showmanyc() == (c_len - 1)); + ensure("overflow() appends", bsb->overflow('c') == 'c'); + ensure("showmanyc() reflects append", bsb->showmanyc() == (c_len - 1 + 1)); + ensure("xsputn() appends some more", bsb->xsputn("bla!", 4) == 4); + ensure("showmanyc() reflects 2nd append", bsb->showmanyc() == (c_len - 1 + 5)); + ensure("seekoff() succeeds", bsb->seekoff(0, std::ios_base::beg, std::ios_base::in) == std::streampos(0)); + ensure("seekoff() succeeds 2", bsb->seekoff(4, std::ios_base::cur, std::ios_base::in) == std::streampos(4)); + ensure("showmanyc() picks up seekoff", bsb->showmanyc() == (c_len + 5 - 4)); + ensure("seekoff() succeeds 3", bsb->seekoff(0, std::ios_base::end, std::ios_base::in) == std::streampos(c_len + 4)); + ensure("pbackfail('!') succeeds", tst_traits_t::eof() == bsb->pbackfail('!')); + + // release the implicit reference, causing the object to be released + delete bsb; + bsb = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<6>() +{ + set_test_name("BufferArrayStream construction with real BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + //const char * content("This is a string. A fragment."); + //const size_t c_len(strlen(content)); + //ba->append(content, strlen(content)); + + { + // create a new ref counted object with an implicit reference + BufferArrayStream bas(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // Basic operations + bas << "Hello" << 27 << "."; + ensure("BA length 8", ba->size() == 8); + + std::string str; + bas >> str; + ensure("reads correctly", str == "Hello27."); + } + + // release the implicit reference, causing the object to be released + ba->release(); + ba = NULL; + + // make sure we didn't leak any memory + // ensure("Allocated memory returned", mMemTotal == GetMemTotal()); + static U64 mem = GetMemTotal(); +} + + +} // end namespace tut + + +#endif // TEST_LLCORE_BUFFER_STREAM_H_ -- cgit v1.2.3 From eed28348f2668c93bc572cffd8a284e65228ed02 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 21 Jun 2012 20:02:24 -0400 Subject: Compiler warning fix on linux. --- indra/llcorehttp/tests/test_bufferstream.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcorehttp/tests/test_bufferstream.hpp b/indra/llcorehttp/tests/test_bufferstream.hpp index 5afaba0966..45ddb7fd80 100644 --- a/indra/llcorehttp/tests/test_bufferstream.hpp +++ b/indra/llcorehttp/tests/test_bufferstream.hpp @@ -242,7 +242,7 @@ void BufferStreamTestObjectType::test<6>() // make sure we didn't leak any memory // ensure("Allocated memory returned", mMemTotal == GetMemTotal()); - static U64 mem = GetMemTotal(); + // static U64 mem = GetMemTotal(); } -- cgit v1.2.3 From ed5db306545e414a1c975c1fff5908b6c2fe1389 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 21 Jun 2012 21:32:33 -0400 Subject: Preparing for better shutdown/cleanup logic. --- indra/llcorehttp/_httprequestqueue.cpp | 29 +++++++++++-- indra/llcorehttp/_httprequestqueue.h | 11 ++++- indra/llcorehttp/_httpservice.cpp | 2 + indra/llcorehttp/httprequest.cpp | 67 ++++++++++++++++++++++++----- indra/llcorehttp/tests/test_httprequest.hpp | 14 +++--- 5 files changed, 100 insertions(+), 23 deletions(-) diff --git a/indra/llcorehttp/_httprequestqueue.cpp b/indra/llcorehttp/_httprequestqueue.cpp index 92bb5ec5c1..6487ef6fa5 100644 --- a/indra/llcorehttp/_httprequestqueue.cpp +++ b/indra/llcorehttp/_httprequestqueue.cpp @@ -39,7 +39,8 @@ HttpRequestQueue * HttpRequestQueue::sInstance(NULL); HttpRequestQueue::HttpRequestQueue() - : RefCounted(true) + : RefCounted(true), + mQueueStopped(false) { } @@ -72,14 +73,25 @@ void HttpRequestQueue::term() } -void HttpRequestQueue::addOp(HttpOperation * op) +HttpStatus HttpRequestQueue::addOp(HttpOperation * op) { + bool wake(false); { HttpScopedLock lock(mQueueMutex); + if (mQueueStopped) + { + // Return op and error to caller + return HttpStatus(HttpStatus::LLCORE, HE_SHUTTING_DOWN); + } + wake = mQueue.empty(); mQueue.push_back(op); } - mQueueCV.notify_all(); + if (wake) + { + mQueueCV.notify_all(); + } + return HttpStatus(); } @@ -129,4 +141,15 @@ void HttpRequestQueue::fetchAll(bool wait, OpContainer & ops) return; } + +void HttpRequestQueue::stopQueue() +{ + { + HttpScopedLock lock(mQueueMutex); + + mQueueStopped = true; + } +} + + } // end namespace LLCore diff --git a/indra/llcorehttp/_httprequestqueue.h b/indra/llcorehttp/_httprequestqueue.h index 26d7d9dca6..6e8f00c4da 100644 --- a/indra/llcorehttp/_httprequestqueue.h +++ b/indra/llcorehttp/_httprequestqueue.h @@ -30,6 +30,7 @@ #include +#include "httpcommon.h" #include "_refcounted.h" #include "_mutex.h" @@ -74,11 +75,11 @@ public: /// Insert an object at the back of the reply queue. /// - /// Caller my provide one refcount to the Library which takes + /// Caller must provide one refcount to the queue which takes /// possession of the count. /// /// Threading: callable by any thread. - void addOp(HttpOperation * op); + HttpStatus addOp(HttpOperation * op); /// Caller acquires reference count on returned operation /// @@ -89,6 +90,11 @@ public: /// /// Threading: callable by any thread. void fetchAll(bool wait, OpContainer & ops); + + /// Disallow further request queuing + /// + /// Threading: callable by any thread. + void stopQueue(); protected: static HttpRequestQueue * sInstance; @@ -97,6 +103,7 @@ protected: OpContainer mQueue; LLCoreInt::HttpMutex mQueueMutex; LLCoreInt::HttpConditionVariable mQueueCV; + bool mQueueStopped; }; // end class HttpRequestQueue diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index 25f64acc42..faafd9a6c7 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -172,6 +172,8 @@ bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t prio void HttpService::shutdown() { + mRequestQueue->stopQueue(); + // *FIXME: Run down everything.... } diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index e906ff8a1e..6d13a213f5 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -154,7 +154,12 @@ HttpHandle HttpRequest::requestGet(policy_t policy_id, return handle; } op->setReplyPath(mReplyQueue, user_handler); - mRequestQueue->addOp(op); // transfers refcount + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } mLastReqStatus = status; handle = static_cast(op); @@ -183,7 +188,12 @@ HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id, return handle; } op->setReplyPath(mReplyQueue, user_handler); - mRequestQueue->addOp(op); // transfers refcount + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } mLastReqStatus = status; handle = static_cast(op); @@ -211,7 +221,12 @@ HttpHandle HttpRequest::requestPost(policy_t policy_id, return handle; } op->setReplyPath(mReplyQueue, user_handler); - mRequestQueue->addOp(op); // transfers refcount + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } mLastReqStatus = status; handle = static_cast(op); @@ -239,7 +254,12 @@ HttpHandle HttpRequest::requestPut(policy_t policy_id, return handle; } op->setReplyPath(mReplyQueue, user_handler); - mRequestQueue->addOp(op); // transfers refcount + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } mLastReqStatus = status; handle = static_cast(op); @@ -255,7 +275,12 @@ HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) HttpOpNull * op = new HttpOpNull(); op->setReplyPath(mReplyQueue, user_handler); - mRequestQueue->addOp(op); // transfer refcount as well + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } mLastReqStatus = status; handle = static_cast(op); @@ -287,14 +312,19 @@ HttpStatus HttpRequest::update(long millis) // Request Management Methods // ==================================== -HttpHandle HttpRequest::requestCancel(HttpHandle handle, HttpHandler * user_handler) +HttpHandle HttpRequest::requestCancel(HttpHandle request, HttpHandler * user_handler) { HttpStatus status; HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); - HttpOpCancel * op = new HttpOpCancel(handle); + HttpOpCancel * op = new HttpOpCancel(request); op->setReplyPath(mReplyQueue, user_handler); - mRequestQueue->addOp(op); // transfer refcount as well + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return ret_handle; + } mLastReqStatus = status; ret_handle = static_cast(op); @@ -311,7 +341,12 @@ HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priori HttpOpSetPriority * op = new HttpOpSetPriority(request, priority); op->setReplyPath(mReplyQueue, handler); - mRequestQueue->addOp(op); // transfer refcount as well + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return ret_handle; + } mLastReqStatus = status; ret_handle = static_cast(op); @@ -368,7 +403,12 @@ HttpHandle HttpRequest::requestStopThread(HttpHandler * user_handler) HttpOpStop * op = new HttpOpStop(); op->setReplyPath(mReplyQueue, user_handler); - mRequestQueue->addOp(op); // transfer refcount as well + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } mLastReqStatus = status; handle = static_cast(op); @@ -388,7 +428,12 @@ HttpHandle HttpRequest::requestSetHttpProxy(const std::string & proxy, HttpHandl HttpOpSetGet * op = new HttpOpSetGet(); op->setupSet(GP_HTTP_PROXY, proxy); op->setReplyPath(mReplyQueue, handler); - mRequestQueue->addOp(op); // transfer refcount as well + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } mLastReqStatus = status; handle = static_cast(op); diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index cac927cfca..8e62cd0f92 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -149,7 +149,7 @@ void HttpRequestTestObjectType::test<1>() // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure(mMemTotal < GetMemTotal()); + ensure("Memory being used", mMemTotal < GetMemTotal()); // release the request object delete req; @@ -158,7 +158,7 @@ void HttpRequestTestObjectType::test<1>() HttpRequest::destroyService(); // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); + ensure("Memory returned", mMemTotal == GetMemTotal()); } catch (...) { @@ -187,11 +187,11 @@ void HttpRequestTestObjectType::test<2>() // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure(mMemTotal < GetMemTotal()); + ensure("Memory being used", mMemTotal < GetMemTotal()); // Issue a NoOp HttpHandle handle = req->requestNoOp(NULL); - ensure(handle != LLCORE_HTTP_HANDLE_INVALID); + ensure("Request issued", handle != LLCORE_HTTP_HANDLE_INVALID); // release the request object delete req; @@ -199,15 +199,15 @@ void HttpRequestTestObjectType::test<2>() // We're still holding onto the operation which is // sitting, unserviced, on the request queue so... - ensure(mMemTotal < GetMemTotal()); + ensure("Memory being used 2", mMemTotal < GetMemTotal()); // Request queue should have two references: global singleton & service object ensure("Two references to request queue", 2 == HttpRequestQueue::instanceOf()->getRefCount()); // Okay, tear it down HttpRequest::destroyService(); - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure(mMemTotal == GetMemTotal()); + printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory returned", mMemTotal == GetMemTotal()); } catch (...) { -- cgit v1.2.3 From ed55eec8e352a9c38670a9155bc239748f578da4 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 22 Jun 2012 12:44:54 -0400 Subject: Always another platform build fix. Mac this time. --- indra/llcorehttp/tests/test_httprequest.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 8e62cd0f92..b09db6db28 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -206,7 +206,7 @@ void HttpRequestTestObjectType::test<2>() // Okay, tear it down HttpRequest::destroyService(); - printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); ensure("Memory returned", mMemTotal == GetMemTotal()); } catch (...) -- cgit v1.2.3 From 5ff1758b633f1984f601aacbb7920c3c744b87f7 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 22 Jun 2012 14:41:08 -0400 Subject: SH-3177, SH-3180 std::iostream and LLSD serialization for BufferArray objects. Seems to be working correctly. Not certain this is the fastest possible way to provide a std::streambuf interface but it's visually acceptable. --- indra/llcorehttp/bufferstream.h | 59 ++++++++++++++++++++++++++++ indra/llcorehttp/tests/test_bufferstream.hpp | 54 ++++++++++++++++++++++++- indra/newview/lltexturefetch.cpp | 27 ++++++------- indra/newview/lltexturefetch.h | 8 ++++ 4 files changed, 133 insertions(+), 15 deletions(-) diff --git a/indra/llcorehttp/bufferstream.h b/indra/llcorehttp/bufferstream.h index 60bda9ff9a..9327a798aa 100644 --- a/indra/llcorehttp/bufferstream.h +++ b/indra/llcorehttp/bufferstream.h @@ -34,13 +34,59 @@ #include "bufferarray.h" +/// @file bufferstream.h +/// +/// std::streambuf and std::iostream adapters for BufferArray +/// objects. +/// +/// BufferArrayStreamBuf inherits std::streambuf and implements +/// an unbuffered interface for streambuf. This may or may not +/// be the most time efficient implementation and it is a little +/// challenging. +/// +/// BufferArrayStream inherits std::iostream and will be the +/// adapter object most callers will be interested in (though +/// it uses BufferArrayStreamBuf internally). Instances allow +/// for the usual streaming operators ('<<', '>>') and serialization +/// methods. +/// +/// Example of LLSD serialization to a BufferArray: +/// +/// BufferArray * ba = new BufferArray; +/// BufferArrayStream bas(ba); +/// LLSDSerialize::toXML(llsd, bas); +/// operationOnBufferArray(ba); +/// ba->release(); +/// ba = NULL; +/// // operationOnBufferArray and bas are each holding +/// // references to the ba instance at this point. +/// + namespace LLCore { +// ===================================================== +// BufferArrayStreamBuf +// ===================================================== + +/// Adapter class to put a std::streambuf interface on a BufferArray +/// +/// Application developers will rarely be interested in anything +/// other than the constructor and even that will rarely be used +/// except indirectly via the @BufferArrayStream class. The +/// choice of interfaces implemented yields a bufferless adapter +/// that doesn't used either the input or output pointer triplets +/// of the more common buffered implementations. This may or may +/// not be faster and that question could stand to be looked at +/// sometime. +/// + class BufferArrayStreamBuf : public std::streambuf { public: + /// Constructor increments the reference count on the + /// BufferArray argument and calls release() on destruction. BufferArrayStreamBuf(BufferArray * array); virtual ~BufferArrayStreamBuf(); @@ -74,9 +120,22 @@ protected: }; // end class BufferArrayStreamBuf +// ===================================================== +// BufferArrayStream +// ===================================================== + +/// Adapter class that supplies streaming operators to BufferArray +/// +/// Provides a streaming adapter to an existing BufferArray +/// instance so that the convenient '<<' and '>>' conversions +/// can be applied to a BufferArray. Very convenient for LLSD +/// serialization and parsing as well. + class BufferArrayStream : public std::iostream { public: + /// Constructor increments the reference count on the + /// BufferArray argument and calls release() on destruction. BufferArrayStream(BufferArray * ba); ~BufferArrayStream(); diff --git a/indra/llcorehttp/tests/test_bufferstream.hpp b/indra/llcorehttp/tests/test_bufferstream.hpp index 45ddb7fd80..831c901b9d 100644 --- a/indra/llcorehttp/tests/test_bufferstream.hpp +++ b/indra/llcorehttp/tests/test_bufferstream.hpp @@ -31,6 +31,8 @@ #include #include "test_allocator.h" +#include "llsd.h" +#include "llsdserialize.h" using namespace LLCore; @@ -173,6 +175,8 @@ void BufferStreamTestObjectType::test<5>() const char * content("This is a string. A fragment."); const size_t c_len(strlen(content)); ba->append(content, c_len); + + // Creat an adapter for the BufferArray BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba); ensure("Memory being used", mMemTotal < GetMemTotal()); @@ -223,7 +227,7 @@ void BufferStreamTestObjectType::test<6>() //ba->append(content, strlen(content)); { - // create a new ref counted object with an implicit reference + // Creat an adapter for the BufferArray BufferArrayStream bas(ba); ensure("Memory being used", mMemTotal < GetMemTotal()); @@ -246,6 +250,54 @@ void BufferStreamTestObjectType::test<6>() } +template <> template <> +void BufferStreamTestObjectType::test<7>() +{ + set_test_name("BufferArrayStream with LLSD serialization"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + + { + // Creat an adapter for the BufferArray + BufferArrayStream bas(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // LLSD + LLSD llsd = LLSD::emptyMap(); + + llsd["int"] = LLSD::Integer(3); + llsd["float"] = LLSD::Real(923289.28992); + llsd["string"] = LLSD::String("aksjdl;ajsdgfjgfal;sdgjakl;sdfjkl;ajsdfkl;ajsdfkl;jaskl;dfj"); + + LLSD llsd_map = LLSD::emptyMap(); + llsd_map["int"] = LLSD::Integer(-2889); + llsd_map["float"] = LLSD::Real(2.37829e32); + llsd_map["string"] = LLSD::String("OHIGODHSPDGHOSDHGOPSHDGP"); + + llsd["map"] = llsd_map; + + // Serialize it + LLSDSerialize::toXML(llsd, bas); + + std::string str; + bas >> str; + // std::cout << "SERIALIZED LLSD: " << str << std::endl; + ensure("Extracted string has reasonable length", str.size() > 60); + } + + // release the implicit reference, causing the object to be released + ba->release(); + ba = NULL; + + // make sure we didn't leak any memory + // ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + } // end namespace tut diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 4995d9f5ea..6b186811f1 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -58,6 +58,7 @@ #include "httphandler.h" #include "httpresponse.h" #include "bufferarray.h" +#include "bufferstream.h" ////////////////////////////////////////////////////////////////////////////// @@ -2182,6 +2183,7 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mHttpRequest(NULL), mHttpOptions(NULL), mHttpHeaders(NULL), + mHttpMetricsHeaders(NULL), mHttpSemaphore(HTTP_REQUESTS_IN_QUEUE_HIGH_WATER), mTotalCacheReadCount(0U), mTotalCacheWriteCount(0U), @@ -2194,6 +2196,8 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mHttpOptions = new LLCore::HttpOptions; mHttpHeaders = new LLCore::HttpHeaders; mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c"); + mHttpMetricsHeaders = new LLCore::HttpHeaders; + mHttpMetricsHeaders->mHeaders.push_back("Content-Type: application/llsd+xml"); } LLTextureFetch::~LLTextureFetch() @@ -2219,6 +2223,12 @@ LLTextureFetch::~LLTextureFetch() mHttpHeaders = NULL; } + if (mHttpMetricsHeaders) + { + mHttpMetricsHeaders->release(); + mHttpMetricsHeaders = NULL; + } + mHttpWaitResource.clear(); delete mHttpRequest; @@ -3501,29 +3511,18 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) if (! mCapsURL.empty()) { - // *FIXME: This mess to get an llsd into a string though - // it's actually no worse than what we currently do... - std::stringstream body; - LLSDSerialize::toXML(merged_llsd, body); - std::string body_str(body.str()); - body.clear(); - - LLCore::HttpHeaders * headers = new LLCore::HttpHeaders; - headers->mHeaders.push_back("Content-Type: application/llsd+xml"); - LLCore::BufferArray * ba = new LLCore::BufferArray; - ba->append(body_str.c_str(), body_str.length()); - body_str.clear(); + LLCore::BufferArrayStream bas(ba); + LLSDSerialize::toXML(merged_llsd, bas); fetcher->getHttpRequest().requestPost(report_policy_class, report_priority, mCapsURL, ba, NULL, - headers, + fetcher->getMetricsHeaders(), handler); ba->release(); - headers->release(); LLTextureFetch::svMetricsDataBreak = false; } else diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index e17c71113a..4d762a0e05 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -157,6 +157,13 @@ public: // Threads: T* LLCore::HttpRequest & getHttpRequest() { return *mHttpRequest; } + // Return a pointer to the shared metrics headers definition. + // Does not increment the reference count, caller is required + // to do that to hold a reference for any length of time. + // + // Threads: T* + LLCore::HttpHeaders * getMetricsHeaders() const { return mHttpMetricsHeaders; } + bool isQAMode() const { return mQAMode; } // ---------------------------------- @@ -322,6 +329,7 @@ private: LLCore::HttpRequest * mHttpRequest; // Ttf LLCore::HttpOptions * mHttpOptions; // Ttf LLCore::HttpHeaders * mHttpHeaders; // Ttf + LLCore::HttpHeaders * mHttpMetricsHeaders; // Ttf // We use a resource semaphore to keep HTTP requests in // WAIT_HTTP_RESOURCE2 if there aren't sufficient slots in the -- cgit v1.2.3 From bc7d5b24d16963a2715e880c518a4706a99f02fa Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 22 Jun 2012 19:13:50 -0400 Subject: This sets down the groundwork for dynamic policy classes. Groundwork is used for the default class which currently represents texture fetching. Class options implemented from API user into HttpLibcurl. Policy layer is going to start doing some traffic shaping like work to solve problems with consumer-grade gear. Need to have dynamic aspects to policies and that starts now... --- indra/llcorehttp/CMakeLists.txt | 2 + indra/llcorehttp/_httplibcurl.cpp | 51 ++++++++------ indra/llcorehttp/_httplibcurl.h | 7 +- indra/llcorehttp/_httppolicy.cpp | 69 ++++++++++++++++--- indra/llcorehttp/_httppolicy.h | 22 +++--- indra/llcorehttp/_httppolicyclass.cpp | 125 ++++++++++++++++++++++++++++++++++ indra/llcorehttp/_httppolicyclass.h | 59 ++++++++++++++++ indra/llcorehttp/_httpservice.cpp | 21 +++++- indra/llcorehttp/_httpservice.h | 28 +++++--- indra/llcorehttp/httprequest.cpp | 29 +++++--- 10 files changed, 351 insertions(+), 62 deletions(-) create mode 100644 indra/llcorehttp/_httppolicyclass.cpp create mode 100644 indra/llcorehttp/_httppolicyclass.h diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 4c00cc04e9..f3df9bb94f 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -37,6 +37,7 @@ set(llcorehttp_SOURCE_FILES _httpopsetget.cpp _httpopsetpriority.cpp _httppolicy.cpp + _httppolicyclass.cpp _httppolicyglobal.cpp _httpreplyqueue.cpp _httprequestqueue.cpp @@ -63,6 +64,7 @@ set(llcorehttp_HEADER_FILES _httpopsetget.h _httpopsetpriority.h _httppolicy.h + _httppolicyclass.h _httppolicyglobal.h _httpreadyqueue.h _httpreplyqueue.h diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 65eb642056..45e16d420e 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -39,17 +39,10 @@ namespace LLCore HttpLibcurl::HttpLibcurl(HttpService * service) - : mService(service) -{ - // *FIXME: Use active policy class count later - for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mMultiHandles); ++policy_class) - { - mMultiHandles[policy_class] = 0; - } - - // Create multi handle for default class - mMultiHandles[0] = curl_multi_init(); -} + : mService(service), + mPolicyCount(0), + mMultiHandles(NULL) +{} HttpLibcurl::~HttpLibcurl() @@ -64,17 +57,23 @@ HttpLibcurl::~HttpLibcurl() mActiveOps.erase(item); } - for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mMultiHandles); ++policy_class) + if (mMultiHandles) { - if (mMultiHandles[policy_class]) + for (int policy_class(0); policy_class < mPolicyCount; ++policy_class) { - // *FIXME: Do some multi cleanup here first + if (mMultiHandles[policy_class]) + { + // *FIXME: Do some multi cleanup here first - curl_multi_cleanup(mMultiHandles[policy_class]); - mMultiHandles[policy_class] = 0; + curl_multi_cleanup(mMultiHandles[policy_class]); + mMultiHandles[policy_class] = 0; + } } - } + delete [] mMultiHandles; + mMultiHandles = NULL; + } + mService = NULL; } @@ -87,12 +86,26 @@ void HttpLibcurl::term() {} +void HttpLibcurl::setPolicyCount(int policy_count) +{ + llassert_always(policy_count <= POLICY_CLASS_LIMIT); + llassert_always(! mMultiHandles); // One-time call only + + mPolicyCount = policy_count; + mMultiHandles = new CURLM * [mPolicyCount]; + for (int policy_class(0); policy_class < mPolicyCount; ++policy_class) + { + mMultiHandles[policy_class] = curl_multi_init(); + } +} + + HttpService::ELoopSpeed HttpLibcurl::processTransport() { HttpService::ELoopSpeed ret(HttpService::REQUEST_SLEEP); // Give libcurl some cycles to do I/O & callbacks - for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mMultiHandles); ++policy_class) + for (int policy_class(0); policy_class < mPolicyCount; ++policy_class) { if (! mMultiHandles[policy_class]) continue; @@ -147,7 +160,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() void HttpLibcurl::addOp(HttpOpRequest * op) { - llassert_always(op->mReqPolicy < POLICY_CLASS_LIMIT); + llassert_always(op->mReqPolicy < mPolicyCount); llassert_always(mMultiHandles[op->mReqPolicy] != NULL); // Create standard handle diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 0d0c4cad6d..5e1dd1bfbf 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -78,6 +78,10 @@ public: /// additional references will be added.) void addOp(HttpOpRequest * op); + /// One-time call to set the number of policy classes to be + /// serviced and to create the resources for each. + void setPolicyCount(int policy_count); + int getActiveCount() const; int getActiveCountInClass(int policy_class) const; @@ -92,7 +96,8 @@ protected: protected: HttpService * mService; // Simple reference, not owner active_set_t mActiveOps; - CURLM * mMultiHandles[POLICY_CLASS_LIMIT]; + int mPolicyCount; + CURLM ** mMultiHandles; }; // end class HttpLibcurl } // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 4be9f1d45f..1dae20add6 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -31,6 +31,7 @@ #include "_httpoprequest.h" #include "_httpservice.h" #include "_httplibcurl.h" +#include "_httppolicyclass.h" #include "lltimer.h" @@ -38,14 +39,44 @@ namespace LLCore { + +struct HttpPolicy::State +{ +public: + State() + : mConnMax(DEFAULT_CONNECTIONS), + mConnAt(DEFAULT_CONNECTIONS), + mConnMin(2), + mNextSample(0), + mErrorCount(0), + mErrorFactor(0) + {} + + HttpReadyQueue mReadyQueue; + HttpRetryQueue mRetryQueue; + + HttpPolicyClass mOptions; + + long mConnMax; + long mConnAt; + long mConnMin; + + HttpTime mNextSample; + unsigned long mErrorCount; + unsigned long mErrorFactor; +}; + + HttpPolicy::HttpPolicy(HttpService * service) - : mService(service) + : mActiveClasses(0), + mState(NULL), + mService(service) {} HttpPolicy::~HttpPolicy() { - for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mState); ++policy_class) + for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) { HttpRetryQueue & retryq(mState[policy_class].mRetryQueue); while (! retryq.empty()) @@ -67,13 +98,27 @@ HttpPolicy::~HttpPolicy() readyq.pop(); } } + delete [] mState; + mState = NULL; mService = NULL; } -void HttpPolicy::setPolicies(const HttpPolicyGlobal & global) +void HttpPolicy::setPolicies(const HttpPolicyGlobal & global, + const std::vector & classes) { + llassert_always(! mState); + mGlobalOptions = global; + mActiveClasses = classes.size(); + mState = new State [mActiveClasses]; + for (int i(0); i < mActiveClasses; ++i) + { + mState[i].mOptions = classes[i]; + mState[i].mConnMax = classes[i].mConnectionLimit; + mState[i].mConnAt = mState[i].mConnMax; + mState[i].mConnMin = 2; + } } @@ -123,13 +168,14 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() HttpService::ELoopSpeed result(HttpService::REQUEST_SLEEP); HttpLibcurl & transport(mService->getTransport()); - for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mState); ++policy_class) + for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) { + State & state(mState[policy_class]); int active(transport.getActiveCountInClass(policy_class)); - int needed(DEFAULT_CONNECTIONS - active); // *FIXME: move to policy class + int needed(state.mConnAt - active); // Expect negatives here - HttpRetryQueue & retryq(mState[policy_class].mRetryQueue); - HttpReadyQueue & readyq(mState[policy_class].mReadyQueue); + HttpRetryQueue & retryq(state.mRetryQueue); + HttpReadyQueue & readyq(state.mReadyQueue); if (needed > 0) { @@ -174,9 +220,10 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t priority) { - for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mState); ++policy_class) + for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) { - HttpReadyQueue::container_type & c(mState[policy_class].mReadyQueue.get_container()); + State & state(mState[policy_class]); + HttpReadyQueue::container_type & c(state.mReadyQueue.get_container()); // Scan ready queue for requests that match policy for (HttpReadyQueue::container_type::iterator iter(c.begin()); c.end() != iter;) @@ -188,7 +235,7 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior HttpOpRequest * op(*cur); c.erase(cur); // All iterators are now invalidated op->mReqPriority = priority; - mState[policy_class].mReadyQueue.push(op); // Re-insert using adapter class + state.mReadyQueue.push(op); // Re-insert using adapter class return true; } } @@ -242,7 +289,7 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) { - if (policy_class < POLICY_CLASS_LIMIT) // *FIXME: use actual active class count + if (policy_class < mActiveClasses) { return (mState[policy_class].mReadyQueue.size() + mState[policy_class].mRetryQueue.size()); diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 05de9303b5..c93279bc83 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -33,6 +33,7 @@ #include "_httpreadyqueue.h" #include "_httpretryqueue.h" #include "_httppolicyglobal.h" +#include "_httppolicyclass.h" #include "_httpinternal.h" @@ -92,26 +93,25 @@ public: // Get pointer to global policy options. Caller is expected // to do context checks like no setting once running. - HttpPolicyGlobal & getGlobalOptions() + HttpPolicyGlobal & getGlobalOptions() { return mGlobalOptions; } - void setPolicies(const HttpPolicyGlobal & global); + void setPolicies(const HttpPolicyGlobal & global, + const std::vector & classes); + // Get ready counts for a particular class int getReadyCount(HttpRequest::policy_t policy_class); protected: - struct State - { - HttpReadyQueue mReadyQueue; - HttpRetryQueue mRetryQueue; - }; - - State mState[POLICY_CLASS_LIMIT]; - HttpService * mService; // Naked pointer, not refcounted, not owner - HttpPolicyGlobal mGlobalOptions; + struct State; + + int mActiveClasses; + State * mState; + HttpService * mService; // Naked pointer, not refcounted, not owner + HttpPolicyGlobal mGlobalOptions; }; // end class HttpPolicy diff --git a/indra/llcorehttp/_httppolicyclass.cpp b/indra/llcorehttp/_httppolicyclass.cpp new file mode 100644 index 0000000000..8007468d3c --- /dev/null +++ b/indra/llcorehttp/_httppolicyclass.cpp @@ -0,0 +1,125 @@ +/** + * @file _httppolicyclass.cpp + * @brief Definitions for internal class defining class policy option. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httppolicyclass.h" + +#include "_httpinternal.h" + + +namespace LLCore +{ + + +HttpPolicyClass::HttpPolicyClass() + : mSetMask(0UL), + mConnectionLimit(DEFAULT_CONNECTIONS), + mPerHostConnectionLimit(DEFAULT_CONNECTIONS), + mPipelining(0) +{} + + +HttpPolicyClass::~HttpPolicyClass() +{} + + +HttpPolicyClass & HttpPolicyClass::operator=(const HttpPolicyClass & other) +{ + if (this != &other) + { + mSetMask = other.mSetMask; + mConnectionLimit = other.mConnectionLimit; + mPerHostConnectionLimit = other.mPerHostConnectionLimit; + mPipelining = other.mPipelining; + } + return *this; +} + + +HttpPolicyClass::HttpPolicyClass(const HttpPolicyClass & other) + : mSetMask(other.mSetMask), + mConnectionLimit(other.mConnectionLimit), + mPerHostConnectionLimit(other.mPerHostConnectionLimit), + mPipelining(other.mPipelining) +{} + + +HttpStatus HttpPolicyClass::set(HttpRequest::EClassPolicy opt, long value) +{ + switch (opt) + { + case HttpRequest::CP_CONNECTION_LIMIT: + mConnectionLimit = llclamp(value, long(LIMIT_CONNECTIONS_MIN), long(LIMIT_CONNECTIONS_MAX)); + break; + + case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT: + mPerHostConnectionLimit = llclamp(value, long(LIMIT_CONNECTIONS_MIN), mConnectionLimit); + break; + + case HttpRequest::CP_ENABLE_PIPELINING: + mPipelining = llclamp(value, 0L, 1L); + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + mSetMask |= 1UL << int(opt); + return HttpStatus(); +} + + +HttpStatus HttpPolicyClass::get(HttpRequest::EClassPolicy opt, long * value) +{ + static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); + long * src(NULL); + + switch (opt) + { + case HttpRequest::CP_CONNECTION_LIMIT: + src = &mConnectionLimit; + break; + + case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT: + src = &mPerHostConnectionLimit; + break; + + case HttpRequest::CP_ENABLE_PIPELINING: + src = &mPipelining; + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + if (! (mSetMask & (1UL << int(opt)))) + return not_set; + + *value = *src; + return HttpStatus(); +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicyclass.h b/indra/llcorehttp/_httppolicyclass.h new file mode 100644 index 0000000000..d175413cbd --- /dev/null +++ b/indra/llcorehttp/_httppolicyclass.h @@ -0,0 +1,59 @@ +/** + * @file _httppolicyclass.h + * @brief Declarations for internal class defining policy class options. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_POLICY_CLASS_H_ +#define _LLCORE_HTTP_POLICY_CLASS_H_ + + +#include "httprequest.h" + + +namespace LLCore +{ + +class HttpPolicyClass +{ +public: + HttpPolicyClass(); + ~HttpPolicyClass(); + + HttpPolicyClass & operator=(const HttpPolicyClass &); + HttpPolicyClass(const HttpPolicyClass &); // Not defined + +public: + HttpStatus set(HttpRequest::EClassPolicy opt, long value); + HttpStatus get(HttpRequest::EClassPolicy opt, long * value); + +public: + unsigned long mSetMask; + long mConnectionLimit; + long mPerHostConnectionLimit; + long mPipelining; +}; // end class HttpPolicyClass + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_POLICY_CLASS_H_ diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index faafd9a6c7..9c5c7bf9b4 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -53,6 +53,12 @@ HttpService::HttpService() mPolicy(NULL), mTransport(NULL) { + HttpPolicyClass pol_class; + pol_class.set(HttpRequest::CP_CONNECTION_LIMIT, DEFAULT_CONNECTIONS); + pol_class.set(HttpRequest::CP_PER_HOST_CONNECTION_LIMIT, DEFAULT_CONNECTIONS); + pol_class.set(HttpRequest::CP_ENABLE_PIPELINING, 0L); + + mPolicyClasses.push_back(pol_class); } @@ -114,6 +120,18 @@ void HttpService::term() } +HttpRequest::policy_t HttpService::createPolicyClass() +{ + const HttpRequest::policy_t policy_class(mPolicyClasses.size()); + if (policy_class >= POLICY_CLASS_LIMIT) + { + return 0; + } + mPolicyClasses.push_back(HttpPolicyClass()); + return policy_class; +} + + bool HttpService::isStopped() { // What is really wanted here is something like: @@ -142,7 +160,8 @@ void HttpService::startThread() } // Push current policy definitions - mPolicy->setPolicies(mPolicyGlobal); + mPolicy->setPolicies(mPolicyGlobal, mPolicyClasses); + mTransport->setPolicyCount(mPolicyClasses.size()); mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1)); mThread->addRef(); // Need an explicit reference, implicit one is used internally diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index 3f953ec1a7..43044d97c0 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -28,9 +28,12 @@ #define _LLCORE_HTTP_SERVICE_H_ +#include + #include "httpcommon.h" #include "httprequest.h" #include "_httppolicyglobal.h" +#include "_httppolicyclass.h" namespace LLCoreInt @@ -163,6 +166,14 @@ public: { return mPolicyGlobal; } + + HttpRequest::policy_t createPolicyClass(); + + HttpPolicyClass & getClassOptions(HttpRequest::policy_t policy_class) + { + llassert(policy_class >= 0 && policy_class < mPolicyClasses.size()); + return mPolicyClasses[policy_class]; + } protected: void threadRun(LLCoreInt::HttpThread * thread); @@ -170,20 +181,21 @@ protected: ELoopSpeed processRequestQueue(ELoopSpeed loop); protected: - static HttpService * sInstance; + static HttpService * sInstance; // === shared data === - static volatile EState sState; - HttpRequestQueue * mRequestQueue; - volatile bool mExitRequested; + static volatile EState sState; + HttpRequestQueue * mRequestQueue; + volatile bool mExitRequested; // === calling-thread-only data === - LLCoreInt::HttpThread * mThread; - HttpPolicyGlobal mPolicyGlobal; + LLCoreInt::HttpThread * mThread; + HttpPolicyGlobal mPolicyGlobal; + std::vector mPolicyClasses; // === working-thread-only data === - HttpPolicy * mPolicy; // Simple pointer, has ownership - HttpLibcurl * mTransport; // Simple pointer, has ownership + HttpPolicy * mPolicy; // Simple pointer, has ownership + HttpLibcurl * mTransport; // Simple pointer, has ownership }; // end class HttpService } // end namespace LLCore diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 6d13a213f5..a525d8f9ea 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -92,26 +92,31 @@ HttpRequest::~HttpRequest() HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value) { - // *FIXME: Fail if thread is running. - + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); + } return HttpService::instanceOf()->getGlobalOptions().set(opt, value); } HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value) { - // *FIXME: Fail if thread is running. - + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); + } return HttpService::instanceOf()->getGlobalOptions().set(opt, value); } HttpRequest::policy_t HttpRequest::createPolicyClass() { - // *FIXME: Implement classes - policy_t policy_id = 1; - - return policy_id; + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return 0; + } + return HttpService::instanceOf()->createPolicyClass(); } @@ -119,9 +124,11 @@ HttpStatus HttpRequest::setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value) { - HttpStatus status; - - return status; + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); + } + return HttpService::instanceOf()->getClassOptions(policy_id).set(opt, value); } -- cgit v1.2.3 From e172ec84fa217aae8d1e51c1e0673322c30891fe Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Sat, 23 Jun 2012 23:33:50 -0400 Subject: SH-3184/SH-3221 Improve cleanup, destructor, thread termination, etc. logic in library. With this commit, the cleanup paths should be production quality. Unit tests have been expanded to include cases requiring thread termination and cleanup by the worker thread. Special operation/request added to support the unit tests. Thread interface expanded to include a very aggressive cancel() method that does not do cleanup but prevents the thread from accessing objects that will be destroyed. --- indra/llcorehttp/_httplibcurl.cpp | 97 +++++++++++++----- indra/llcorehttp/_httplibcurl.h | 16 +-- indra/llcorehttp/_httpoperation.cpp | 36 +++++++ indra/llcorehttp/_httpoperation.h | 25 +++++ indra/llcorehttp/_httppolicy.cpp | 15 ++- indra/llcorehttp/_httppolicy.h | 15 ++- indra/llcorehttp/_httprequestqueue.h | 6 +- indra/llcorehttp/_httpservice.cpp | 75 ++++++++++---- indra/llcorehttp/_httpservice.h | 11 +- indra/llcorehttp/_thread.h | 20 +++- indra/llcorehttp/httprequest.cpp | 43 ++++++-- indra/llcorehttp/httprequest.h | 9 ++ indra/llcorehttp/tests/test_httprequest.hpp | 154 +++++++++++++++++++++++++++- 13 files changed, 447 insertions(+), 75 deletions(-) diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 45e16d420e..bc8b3cc9be 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -47,12 +47,19 @@ HttpLibcurl::HttpLibcurl(HttpService * service) HttpLibcurl::~HttpLibcurl() { - // *FIXME: need to cancel requests in this class, not in op class. + shutdown(); + + mService = NULL; +} + + +void HttpLibcurl::shutdown() +{ while (! mActiveOps.empty()) { active_set_t::iterator item(mActiveOps.begin()); - (*item)->cancel(); + cancelRequest(*item); (*item)->release(); mActiveOps.erase(item); } @@ -63,8 +70,6 @@ HttpLibcurl::~HttpLibcurl() { if (mMultiHandles[policy_class]) { - // *FIXME: Do some multi cleanup here first - curl_multi_cleanup(mMultiHandles[policy_class]); mMultiHandles[policy_class] = 0; } @@ -73,20 +78,12 @@ HttpLibcurl::~HttpLibcurl() delete [] mMultiHandles; mMultiHandles = NULL; } - - mService = NULL; -} - -void HttpLibcurl::init() -{} - - -void HttpLibcurl::term() -{} + mPolicyCount = 0; +} -void HttpLibcurl::setPolicyCount(int policy_count) +void HttpLibcurl::start(int policy_count) { llassert_always(policy_count <= POLICY_CLASS_LIMIT); llassert_always(! mMultiHandles); // One-time call only @@ -143,8 +140,9 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() } else { - // *FIXME: Issue a logging event for this. - ; + LL_WARNS_ONCE("CoreHttp") << "Unexpected message from libcurl. Msg code: " + << msg->msg + << LL_ENDL; } msgs_in_queue = 0; } @@ -191,30 +189,61 @@ void HttpLibcurl::addOp(HttpOpRequest * op) } +// *NOTE: cancelRequest logic parallels completeRequest logic. +// Keep them synchronized as necessary. Caller is expected to +// remove to op from the active list and release the op *after* +// calling this method. It must be called first to deliver the +// op to the reply queue with refcount intact. +void HttpLibcurl::cancelRequest(HttpOpRequest * op) +{ + // Deactivate request + op->mCurlActive = false; + + // Detach from multi and recycle handle + curl_multi_remove_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle); + curl_easy_cleanup(op->mCurlHandle); + op->mCurlHandle = NULL; + + // Tracing + if (op->mTracing > TRACE_OFF) + { + LL_INFOS("CoreHttp") << "TRACE, RequestCanceled, Handle: " + << static_cast(op) + << ", Status: " << op->mStatus.toHex() + << LL_ENDL; + } + + // Cancel op and deliver for notification + op->cancel(); +} + + +// *NOTE: cancelRequest logic parallels completeRequest logic. +// Keep them synchronized as necessary. bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status) { HttpOpRequest * op(NULL); curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); - // *FIXME: check the pointer if (handle != op->mCurlHandle || ! op->mCurlActive) { - // *FIXME: This is a sanity check that needs validation/termination. - ; + LL_WARNS("CoreHttp") << "libcurl handle and HttpOpRequest handle in disagreement or inactive request." + << " Handle: " << static_cast(handle) + << LL_ENDL; + return false; } active_set_t::iterator it(mActiveOps.find(op)); if (mActiveOps.end() == it) { - // *FIXME: Fatal condition. This must be here. - ; - } - else - { - mActiveOps.erase(it); + LL_WARNS("CoreHttp") << "libcurl completion for request not on active list. Continuing." + << " Handle: " << static_cast(handle) + << LL_ENDL; + return false; } // Deactivate request + mActiveOps.erase(it); op->mCurlActive = false; // Set final status of request if it hasn't failed by other mechanisms yet @@ -258,9 +287,21 @@ int HttpLibcurl::getActiveCount() const } -int HttpLibcurl::getActiveCountInClass(int /* policy_class */) const +int HttpLibcurl::getActiveCountInClass(int policy_class) const { - return getActiveCount(); + int count(0); + + for (active_set_t::const_iterator iter(mActiveOps.begin()); + mActiveOps.end() != iter; + ++iter) + { + if ((*iter)->mReqPolicy == policy_class) + { + ++count; + } + } + + return count; } diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 5e1dd1bfbf..69f7bb2b6d 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -65,9 +65,6 @@ private: void operator=(const HttpLibcurl &); // Not defined public: - static void init(); - static void term(); - /// Give cycles to libcurl to run active requests. Completed /// operations (successful or failed) will be retried or handed /// over to the reply queue as final responses. @@ -79,16 +76,23 @@ public: void addOp(HttpOpRequest * op); /// One-time call to set the number of policy classes to be - /// serviced and to create the resources for each. - void setPolicyCount(int policy_count); + /// serviced and to create the resources for each. Value + /// must agree with HttpPolicy::setPolicies() call. + void start(int policy_count); + + void shutdown(); int getActiveCount() const; int getActiveCountInClass(int policy_class) const; - + protected: /// Invoked when libcurl has indicated a request has been processed /// to completion and we need to move the request to a new state. bool completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status); + + /// Invoked to cancel an active request, mainly during shutdown + /// and destroy. + void cancelRequest(HttpOpRequest * op); protected: typedef std::set active_set_t; diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index 0d9553434e..b35ab79d65 100644 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -208,4 +208,40 @@ void HttpOpNull::stageFromRequest(HttpService * service) } +// ================================== +// HttpOpSpin +// ================================== + + +HttpOpSpin::HttpOpSpin(int mode) + : HttpOperation(), + mMode(mode) +{} + + +HttpOpSpin::~HttpOpSpin() +{} + + +void HttpOpSpin::stageFromRequest(HttpService * service) +{ + if (0 == mMode) + { + // Spin forever + while (true) + { + ms_sleep(100); + } + } + else + { + this->addRef(); + if (! HttpRequestQueue::instanceOf()->addOp(this)) + { + this->release(); + } + } +} + + } // end namespace LLCore diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index 5823c08c7b..717a9b0d72 100644 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -170,6 +170,31 @@ public: }; // end class HttpOpNull +/// HttpOpSpin is a test-only request that puts the worker +/// thread into a cpu spin. Used for unit tests and cleanup +/// evaluation. You do not want to use this. +class HttpOpSpin : public HttpOperation +{ +public: + // 0 does a hard spin in the operation + // 1 does a soft spin continuously requeuing itself + HttpOpSpin(int mode); + +protected: + virtual ~HttpOpSpin(); + +private: + HttpOpSpin(const HttpOpSpin &); // Not defined + void operator=(const HttpOpSpin &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + +protected: + int mMode; +}; // end class HttpOpSpin + + } // end namespace LLCore #endif // _LLCORE_HTTP_OPERATION_H_ diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 1dae20add6..93e295537c 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -75,6 +75,14 @@ HttpPolicy::HttpPolicy(HttpService * service) HttpPolicy::~HttpPolicy() +{ + shutdown(); + + mService = NULL; +} + + +void HttpPolicy::shutdown() { for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) { @@ -100,12 +108,12 @@ HttpPolicy::~HttpPolicy() } delete [] mState; mState = NULL; - mService = NULL; + mActiveClasses = 0; } -void HttpPolicy::setPolicies(const HttpPolicyGlobal & global, - const std::vector & classes) +void HttpPolicy::start(const HttpPolicyGlobal & global, + const std::vector & classes) { llassert_always(! mState); @@ -244,6 +252,7 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior return false; } + bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) { static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index c93279bc83..90bb3b571d 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -60,6 +60,17 @@ private: void operator=(const HttpPolicy &); // Not defined public: + /// Cancel all ready and retry requests sending them to + /// their notification queues. Release state resources + /// making further request handling impossible. + void shutdown(); + + /// Deliver policy definitions and enable handling of + /// requests. One-time call invoked before starting + /// the worker thread. + void start(const HttpPolicyGlobal & global, + const std::vector & classes); + /// Give the policy layer some cycles to scan the ready /// queue promoting higher-priority requests to active /// as permited. @@ -98,10 +109,6 @@ public: return mGlobalOptions; } - void setPolicies(const HttpPolicyGlobal & global, - const std::vector & classes); - - // Get ready counts for a particular class int getReadyCount(HttpRequest::policy_t policy_class); diff --git a/indra/llcorehttp/_httprequestqueue.h b/indra/llcorehttp/_httprequestqueue.h index 6e8f00c4da..e11fd17c90 100644 --- a/indra/llcorehttp/_httprequestqueue.h +++ b/indra/llcorehttp/_httprequestqueue.h @@ -73,11 +73,15 @@ public: public: typedef std::vector OpContainer; - /// Insert an object at the back of the reply queue. + /// Insert an object at the back of the request queue. /// /// Caller must provide one refcount to the queue which takes /// possession of the count. /// + /// @return Standard status. On failure, caller + /// must dispose of the operation with + /// an explicit release() call. + /// /// Threading: callable by any thread. HttpStatus addOp(HttpOperation * op); diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index 9c5c7bf9b4..afbab2ab71 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -53,35 +53,43 @@ HttpService::HttpService() mPolicy(NULL), mTransport(NULL) { + // Create the default policy class HttpPolicyClass pol_class; pol_class.set(HttpRequest::CP_CONNECTION_LIMIT, DEFAULT_CONNECTIONS); pol_class.set(HttpRequest::CP_PER_HOST_CONNECTION_LIMIT, DEFAULT_CONNECTIONS); pol_class.set(HttpRequest::CP_ENABLE_PIPELINING, 0L); - mPolicyClasses.push_back(pol_class); } HttpService::~HttpService() { + mExitRequested = true; + if (RUNNING == sState) + { + // Trying to kill the service object with a running thread + // is a bit tricky. + if (mThread) + { + mThread->cancel(); + + if (! mThread->timedJoin(2000)) + { + // Failed to join, expect problems ahead... + LL_WARNS("CoreHttp") << "Destroying HttpService with running thread. Expect problems." + << LL_ENDL; + } + } + } + if (mRequestQueue) { mRequestQueue->release(); mRequestQueue = NULL; } - if (mPolicy) - { - // *TODO: need a finalization here - ; - } - - if (mTransport) - { - // *TODO: need a finalization here - delete mTransport; - mTransport = NULL; - } + delete mTransport; + mTransport = NULL; delete mPolicy; mPolicy = NULL; @@ -110,9 +118,22 @@ void HttpService::init(HttpRequestQueue * queue) void HttpService::term() { - llassert_always(RUNNING != sState); if (sInstance) { + if (RUNNING == sState) + { + // Unclean termination. Thread appears to be running. We'll + // try to give the worker thread a chance to cancel using the + // exit flag... + sInstance->mExitRequested = true; + + // And a little sleep + ms_sleep(1000); + + // Dtor will make some additional efforts and issue any final + // warnings... + } + delete sInstance; sInstance = NULL; } @@ -159,9 +180,9 @@ void HttpService::startThread() mThread->release(); } - // Push current policy definitions - mPolicy->setPolicies(mPolicyGlobal, mPolicyClasses); - mTransport->setPolicyCount(mPolicyClasses.size()); + // Push current policy definitions, enable policy & transport components + mPolicy->start(mPolicyGlobal, mPolicyClasses); + mTransport->start(mPolicyClasses.size()); mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1)); mThread->addRef(); // Need an explicit reference, implicit one is used internally @@ -174,6 +195,7 @@ void HttpService::stopRequested() mExitRequested = true; } + bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t priority) { bool found(false); @@ -191,9 +213,26 @@ bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t prio void HttpService::shutdown() { + // Disallow future enqueue of requests mRequestQueue->stopQueue(); - // *FIXME: Run down everything.... + // Cancel requests alread on the request queue + HttpRequestQueue::OpContainer ops; + mRequestQueue->fetchAll(false, ops); + while (! ops.empty()) + { + HttpOperation * op(ops.front()); + ops.erase(ops.begin()); + + op->cancel(); + op->release(); + } + + // Shutdown transport canceling requests, freeing resources + mTransport->shutdown(); + + // And now policy + mPolicy->shutdown(); } diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index 43044d97c0..a74235c475 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -134,7 +134,7 @@ public: /// acquires its weaknesses. static bool isStopped(); - /// Threading: callable by application thread *once*. + /// Threading: callable by consumer thread *once*. void startThread(); /// Threading: callable by worker thread. @@ -152,23 +152,28 @@ public: /// Threading: callable by worker thread. bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); + /// Threading: callable by worker thread. HttpPolicy & getPolicy() { return *mPolicy; } + /// Threading: callable by worker thread. HttpLibcurl & getTransport() { return *mTransport; } + /// Threading: callable by consumer thread. HttpPolicyGlobal & getGlobalOptions() { return mPolicyGlobal; } + /// Threading: callable by consumer thread. HttpRequest::policy_t createPolicyClass(); + /// Threading: callable by consumer thread. HttpPolicyClass & getClassOptions(HttpRequest::policy_t policy_class) { llassert(policy_class >= 0 && policy_class < mPolicyClasses.size()); @@ -187,9 +192,9 @@ protected: static volatile EState sState; HttpRequestQueue * mRequestQueue; volatile bool mExitRequested; - - // === calling-thread-only data === LLCoreInt::HttpThread * mThread; + + // === consumer-thread-only data === HttpPolicyGlobal mPolicyGlobal; std::vector mPolicyClasses; diff --git a/indra/llcorehttp/_thread.h b/indra/llcorehttp/_thread.h index 46a333a749..4cf35055e9 100644 --- a/indra/llcorehttp/_thread.h +++ b/indra/llcorehttp/_thread.h @@ -27,9 +27,11 @@ #ifndef LLCOREINT_THREAD_H_ #define LLCOREINT_THREAD_H_ +#include "linden_common.h" + #include #include - +#include #include "_refcounted.h" @@ -91,11 +93,27 @@ public: mThread->join(); } + inline bool timedJoin(S32 millis) + { + return mThread->timed_join(boost::posix_time::milliseconds(millis)); + } + inline bool joinable() const { return mThread->joinable(); } + // A very hostile method to force a thread to quit + inline void cancel() + { + boost::thread::native_handle_type thread(mThread->native_handle()); +#if LL_WINDOWS + TerminateThread(thread, 0); +#else + pthread_cancel(thread); +#endif + } + private: boost::function mThreadFunc; boost::thread * mThread; diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index a525d8f9ea..3a55a849b9 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -370,11 +370,13 @@ HttpStatus HttpRequest::createService() { HttpStatus status; - llassert_always(! has_inited); - HttpRequestQueue::init(); - HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); - HttpService::init(rq); - has_inited = true; + if (! has_inited) + { + HttpRequestQueue::init(); + HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + HttpService::init(rq); + has_inited = true; + } return status; } @@ -384,10 +386,12 @@ HttpStatus HttpRequest::destroyService() { HttpStatus status; - llassert_always(has_inited); - HttpService::term(); - HttpRequestQueue::term(); - has_inited = false; + if (has_inited) + { + HttpService::term(); + HttpRequestQueue::term(); + has_inited = false; + } return status; } @@ -423,6 +427,27 @@ HttpHandle HttpRequest::requestStopThread(HttpHandler * user_handler) return handle; } + +HttpHandle HttpRequest::requestSpin(int mode) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpSpin * op = new HttpOpSpin(mode); + op->setReplyPath(mReplyQueue, NULL); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + // ==================================== // Dynamic Policy Methods // ==================================== diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 24fff24b83..134a61b618 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -409,6 +409,15 @@ public: /// HttpHandle requestStopThread(HttpHandler * handler); + /// Queue a Spin request. + /// DEBUG/TESTING ONLY. This puts the worker into a CPU spin for + /// test purposes. + /// + /// @param mode 0 for hard spin, 1 for soft spin + /// @return Standard handle return cases. + /// + HttpHandle requestSpin(int mode); + /// @} /// @name DynamicPolicyMethods diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index b09db6db28..ed049aa09c 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -1230,10 +1230,158 @@ void HttpRequestTestObjectType::test<11>() } } +template <> template <> +void HttpRequestTestObjectType::test<12>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest Spin + NoOp + hard termination"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a Spin + HttpHandle handle = req->requestSpin(0); // Hard spin + ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Issue a NoOp + handle = req->requestNoOp(&handler); + ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("No notifications received", mHandlerCalls == 0); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + // Check memory usage + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + // ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + // This memory test won't work because we're killing the thread + // hard with the hard spinner. There's no opportunity to join + // nicely so many things leak or get destroyed unilaterally. + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + +template <> template <> +void HttpRequestTestObjectType::test<13>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest Spin (soft) + NoOp + hard termination"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a Spin + HttpHandle handle = req->requestSpin(1); + ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Issue a NoOp + handle = req->requestNoOp(&handler); + ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("NoOp notification received", mHandlerCalls == 1); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + // Check memory usage + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + // This memory test should work but could give problems as it + // relies on the worker thread picking up a friendly request + // to shutdown. Doing so, it drops references to things and + // we should go back to where we started. If it gives you + // problems, look into the code before commenting things out. + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + // *NB: This test must be last. The sleeping webserver // won't respond for a long time. template <> template <> -void HttpRequestTestObjectType::test<12>() +void HttpRequestTestObjectType::test<14>() { ScopedCurlInit ready; @@ -1352,7 +1500,9 @@ void HttpRequestTestObjectType::test<12>() throw; } } - +// *NOTE: This test ^^^^^^^^ must be the last one in the set. It uses a +// sleeping service that interferes with other HTTP tests. Keep it +// last until that little HTTP server can get some attention... } // end namespace tut -- cgit v1.2.3 From e8b0088d1a0c02bfa1f9768dc91fc3df4322adae Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 26 Jun 2012 12:28:58 -0400 Subject: SH-3184/SH-3221 More work on cleanup with better unit test work and more aggressive shutdown of a thread. Some additional work let me enable a memory check for the clean shutdown case and generally do a better job on other interfaces. Request queue waiters now awake on shutdown and don't sleep once the queue is turned off. Much better semantically for how this will be used. --- indra/llcorehttp/_httplibcurl.cpp | 8 +- indra/llcorehttp/_httpoperation.cpp | 3 +- indra/llcorehttp/_httppolicy.cpp | 6 +- indra/llcorehttp/_httprequestqueue.cpp | 11 +- indra/llcorehttp/_httprequestqueue.h | 30 ++- indra/llcorehttp/_httpservice.cpp | 43 ++-- indra/llcorehttp/_httpservice.h | 10 +- indra/llcorehttp/_thread.h | 27 ++- indra/llcorehttp/tests/test_httprequest.hpp | 309 ++++++++++++++-------------- 9 files changed, 248 insertions(+), 199 deletions(-) diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index bc8b3cc9be..39abca12c5 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -57,11 +57,11 @@ void HttpLibcurl::shutdown() { while (! mActiveOps.empty()) { - active_set_t::iterator item(mActiveOps.begin()); + HttpOpRequest * op(* mActiveOps.begin()); + mActiveOps.erase(mActiveOps.begin()); - cancelRequest(*item); - (*item)->release(); - mActiveOps.erase(item); + cancelRequest(op); + op->release(); } if (mMultiHandles) diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index b35ab79d65..d80a8236e6 100644 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -235,8 +235,9 @@ void HttpOpSpin::stageFromRequest(HttpService * service) } else { + ms_sleep(1); // backoff interlock plumbing a bit this->addRef(); - if (! HttpRequestQueue::instanceOf()->addOp(this)) + if (! service->getRequestQueue().addOp(this)) { this->release(); } diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 93e295537c..4350ff617b 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -90,20 +90,20 @@ void HttpPolicy::shutdown() while (! retryq.empty()) { HttpOpRequest * op(retryq.top()); + retryq.pop(); op->cancel(); op->release(); - retryq.pop(); } HttpReadyQueue & readyq(mState[policy_class].mReadyQueue); while (! readyq.empty()) { HttpOpRequest * op(readyq.top()); + readyq.pop(); op->cancel(); op->release(); - readyq.pop(); } } delete [] mState; @@ -218,7 +218,7 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() if (! readyq.empty() || ! retryq.empty()) { // If anything is ready, continue looping... - result = (std::min)(result, HttpService::NORMAL); + result = HttpService::NORMAL; } } // end foreach policy_class diff --git a/indra/llcorehttp/_httprequestqueue.cpp b/indra/llcorehttp/_httprequestqueue.cpp index 6487ef6fa5..9acac665a9 100644 --- a/indra/llcorehttp/_httprequestqueue.cpp +++ b/indra/llcorehttp/_httprequestqueue.cpp @@ -104,7 +104,7 @@ HttpOperation * HttpRequestQueue::fetchOp(bool wait) while (mQueue.empty()) { - if (! wait) + if (! wait || mQueueStopped) return NULL; mQueueCV.wait(lock); } @@ -129,7 +129,7 @@ void HttpRequestQueue::fetchAll(bool wait, OpContainer & ops) while (mQueue.empty()) { - if (! wait) + if (! wait || mQueueStopped) return; mQueueCV.wait(lock); } @@ -142,12 +142,19 @@ void HttpRequestQueue::fetchAll(bool wait, OpContainer & ops) } +void HttpRequestQueue::wakeAll() +{ + mQueueCV.notify_all(); +} + + void HttpRequestQueue::stopQueue() { { HttpScopedLock lock(mQueueMutex); mQueueStopped = true; + wakeAll(); } } diff --git a/indra/llcorehttp/_httprequestqueue.h b/indra/llcorehttp/_httprequestqueue.h index e11fd17c90..c9c52b7233 100644 --- a/indra/llcorehttp/_httprequestqueue.h +++ b/indra/llcorehttp/_httprequestqueue.h @@ -76,7 +76,7 @@ public: /// Insert an object at the back of the request queue. /// /// Caller must provide one refcount to the queue which takes - /// possession of the count. + /// possession of the count on success. /// /// @return Standard status. On failure, caller /// must dispose of the operation with @@ -85,17 +85,41 @@ public: /// Threading: callable by any thread. HttpStatus addOp(HttpOperation * op); - /// Caller acquires reference count on returned operation + /// Return the operation on the front of the queue. If + /// the queue is empty and @wait is false, call returns + /// immediately and a NULL pointer is returned. If true, + /// caller will sleep until explicitly woken. Wakeups + /// can be spurious and callers must expect NULL pointers + /// even if waiting is indicated. + /// + /// Caller acquires reference count any returned operation /// /// Threading: callable by any thread. HttpOperation * fetchOp(bool wait); + /// Return all queued requests to caller. The @ops argument + /// should be empty when called and will be swap()'d with + /// current contents. Handling of the @wait argument is + /// identical to @fetchOp. + /// /// Caller acquires reference count on each returned operation /// /// Threading: callable by any thread. void fetchAll(bool wait, OpContainer & ops); - /// Disallow further request queuing + /// Wake any sleeping threads. Normal queuing operations + /// won't require this but it may be necessary for termination + /// requests. + /// + /// Threading: callable by any thread. + void wakeAll(); + + /// Disallow further request queuing. Callers to @addOp will + /// get a failure status (LLCORE, HE_SHUTTING_DOWN). Callers + /// to @fetchAll or @fetchOp will get requests that are on the + /// queue but the calls will no longer wait. Instead they'll + /// return immediately. Also wakes up all sleepers to send + /// them on their way. /// /// Threading: callable by any thread. void stopQueue(); diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index afbab2ab71..92c15b5b8f 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -48,7 +48,7 @@ volatile HttpService::EState HttpService::sState(NOT_INITIALIZED); HttpService::HttpService() : mRequestQueue(NULL), - mExitRequested(false), + mExitRequested(0U), mThread(NULL), mPolicy(NULL), mTransport(NULL) @@ -64,18 +64,23 @@ HttpService::HttpService() HttpService::~HttpService() { - mExitRequested = true; + mExitRequested = 1U; if (RUNNING == sState) { // Trying to kill the service object with a running thread // is a bit tricky. + if (mRequestQueue) + { + mRequestQueue->stopQueue(); + } + if (mThread) { - mThread->cancel(); - - if (! mThread->timedJoin(2000)) + if (! mThread->timedJoin(250)) { - // Failed to join, expect problems ahead... + // Failed to join, expect problems ahead so do a hard termination. + mThread->cancel(); + LL_WARNS("CoreHttp") << "Destroying HttpService with running thread. Expect problems." << LL_ENDL; } @@ -120,18 +125,19 @@ void HttpService::term() { if (sInstance) { - if (RUNNING == sState) + if (RUNNING == sState && sInstance->mThread) { // Unclean termination. Thread appears to be running. We'll // try to give the worker thread a chance to cancel using the // exit flag... - sInstance->mExitRequested = true; - + sInstance->mExitRequested = 1U; + sInstance->mRequestQueue->stopQueue(); + // And a little sleep - ms_sleep(1000); - - // Dtor will make some additional efforts and issue any final - // warnings... + for (int i(0); i < 10 && RUNNING == sState; ++i) + { + ms_sleep(100); + } } delete sInstance; @@ -170,6 +176,7 @@ bool HttpService::isStopped() } +/// Threading: callable by consumer thread *once*. void HttpService::startThread() { llassert_always(! mThread || STOPPED == sState); @@ -183,19 +190,20 @@ void HttpService::startThread() // Push current policy definitions, enable policy & transport components mPolicy->start(mPolicyGlobal, mPolicyClasses); mTransport->start(mPolicyClasses.size()); - + mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1)); - mThread->addRef(); // Need an explicit reference, implicit one is used internally sState = RUNNING; } +/// Threading: callable by worker thread. void HttpService::stopRequested() { - mExitRequested = true; + mExitRequested = 1U; } +/// Threading: callable by worker thread. bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t priority) { bool found(false); @@ -211,12 +219,13 @@ bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t prio } +/// Threading: callable by worker thread. void HttpService::shutdown() { // Disallow future enqueue of requests mRequestQueue->stopQueue(); - // Cancel requests alread on the request queue + // Cancel requests already on the request queue HttpRequestQueue::OpContainer ops; mRequestQueue->fetchAll(false, ops); while (! ops.empty()) diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index a74235c475..d67e6e95a5 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -30,6 +30,8 @@ #include +#include "linden_common.h" +#include "llapr.h" #include "httpcommon.h" #include "httprequest.h" #include "_httppolicyglobal.h" @@ -164,6 +166,12 @@ public: return *mTransport; } + /// Threading: callable by worker thread. + HttpRequestQueue & getRequestQueue() + { + return *mRequestQueue; + } + /// Threading: callable by consumer thread. HttpPolicyGlobal & getGlobalOptions() { @@ -191,7 +199,7 @@ protected: // === shared data === static volatile EState sState; HttpRequestQueue * mRequestQueue; - volatile bool mExitRequested; + LLAtomicU32 mExitRequested; LLCoreInt::HttpThread * mThread; // === consumer-thread-only data === diff --git a/indra/llcorehttp/_thread.h b/indra/llcorehttp/_thread.h index 4cf35055e9..e058d660e5 100644 --- a/indra/llcorehttp/_thread.h +++ b/indra/llcorehttp/_thread.h @@ -52,12 +52,10 @@ private: } void run() - { // THREAD CONTEXT + { // THREAD CONTEXT - // The implicit reference to this object is taken for the at_exit - // function so that the HttpThread instance doesn't disappear out - // from underneath it. Other holders of the object may want to - // take a reference as well. + // Take out additional reference for the at_exit handler + addRef(); boost::this_thread::at_thread_exit(boost::bind(&HttpThread::at_exit, this)); // run the thread function @@ -65,13 +63,17 @@ private: } // THREAD CONTEXT +protected: + virtual ~HttpThread() + { + delete mThread; + } + public: /// Constructs a thread object for concurrent execution but does - /// not start running. Unlike other classes that mixin RefCounted, - /// this does take out a reference but it is used internally for - /// final cleanup during at_exit processing. Callers needing to - /// keep a reference must increment it themselves. - /// + /// not start running. Caller receives on refcount on the thread + /// instance. If the thread is started, another will be taken + /// out for the exit handler. explicit HttpThread(boost::function threadFunc) : RefCounted(true), // implicit reference mThreadFunc(threadFunc) @@ -83,11 +85,6 @@ public: mThread = new boost::thread(f); } - virtual ~HttpThread() - { - delete mThread; - } - inline void join() { mThread->join(); diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index ed049aa09c..ed4e239fe7 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -416,6 +416,156 @@ void HttpRequestTestObjectType::test<4>() template <> template <> void HttpRequestTestObjectType::test<5>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest Spin (soft) + NoOp + hard termination"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a Spin + HttpHandle handle = req->requestSpin(1); + ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Issue a NoOp + handle = req->requestNoOp(&handler); + ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("NoOp notification received", mHandlerCalls == 1); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + // Check memory usage + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + // This memory test should work but could give problems as it + // relies on the worker thread picking up a friendly request + // to shutdown. Doing so, it drops references to things and + // we should go back to where we started. If it gives you + // problems, look into the code before commenting things out. + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<6>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest Spin + NoOp + hard termination"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a Spin + HttpHandle handle = req->requestSpin(0); // Hard spin + ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Issue a NoOp + handle = req->requestNoOp(&handler); + ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("No notifications received", mHandlerCalls == 0); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + // Check memory usage + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + // ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + // This memory test won't work because we're killing the thread + // hard with the hard spinner. There's no opportunity to join + // nicely so many things leak or get destroyed unilaterally. + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<7>() { ScopedCurlInit ready; @@ -535,7 +685,7 @@ void HttpRequestTestObjectType::test<5>() template <> template <> -void HttpRequestTestObjectType::test<6>() +void HttpRequestTestObjectType::test<8>() { ScopedCurlInit ready; @@ -643,7 +793,7 @@ void HttpRequestTestObjectType::test<6>() template <> template <> -void HttpRequestTestObjectType::test<7>() +void HttpRequestTestObjectType::test<9>() { ScopedCurlInit ready; @@ -753,7 +903,7 @@ void HttpRequestTestObjectType::test<7>() template <> template <> -void HttpRequestTestObjectType::test<8>() +void HttpRequestTestObjectType::test<10>() { ScopedCurlInit ready; @@ -872,7 +1022,7 @@ void HttpRequestTestObjectType::test<8>() } template <> template <> -void HttpRequestTestObjectType::test<9>() +void HttpRequestTestObjectType::test<11>() { ScopedCurlInit ready; @@ -991,7 +1141,7 @@ void HttpRequestTestObjectType::test<9>() } template <> template <> -void HttpRequestTestObjectType::test<10>() +void HttpRequestTestObjectType::test<12>() { ScopedCurlInit ready; @@ -1104,7 +1254,7 @@ void HttpRequestTestObjectType::test<10>() template <> template <> -void HttpRequestTestObjectType::test<11>() +void HttpRequestTestObjectType::test<13>() { ScopedCurlInit ready; @@ -1230,153 +1380,6 @@ void HttpRequestTestObjectType::test<11>() } } -template <> template <> -void HttpRequestTestObjectType::test<12>() -{ - ScopedCurlInit ready; - - set_test_name("HttpRequest Spin + NoOp + hard termination"); - - // Handler can be stack-allocated *if* there are no dangling - // references to it after completion of this method. - // Create before memory record as the string copy will bump numbers. - TestHandler2 handler(this, "handler"); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - mHandlerCalls = 0; - - HttpRequest * req = NULL; - - try - { - - // Get singletons created - HttpRequest::createService(); - - // Start threading early so that thread memory is invariant - // over the test. - HttpRequest::startThread(); - - // create a new ref counted object with an implicit reference - req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); - - // Issue a Spin - HttpHandle handle = req->requestSpin(0); // Hard spin - ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID); - - // Issue a NoOp - handle = req->requestNoOp(&handler); - ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID); - - // Run the notification pump. - int count(0); - int limit(10); - while (count++ < limit && mHandlerCalls < 1) - { - req->update(1000); - usleep(100000); - } - ensure("No notifications received", mHandlerCalls == 0); - - // release the request object - delete req; - req = NULL; - - // Shut down service - HttpRequest::destroyService(); - - // Check memory usage - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - // ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); - // This memory test won't work because we're killing the thread - // hard with the hard spinner. There's no opportunity to join - // nicely so many things leak or get destroyed unilaterally. - } - catch (...) - { - stop_thread(req); - delete req; - HttpRequest::destroyService(); - throw; - } -} - -template <> template <> -void HttpRequestTestObjectType::test<13>() -{ - ScopedCurlInit ready; - - set_test_name("HttpRequest Spin (soft) + NoOp + hard termination"); - - // Handler can be stack-allocated *if* there are no dangling - // references to it after completion of this method. - // Create before memory record as the string copy will bump numbers. - TestHandler2 handler(this, "handler"); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - mHandlerCalls = 0; - - HttpRequest * req = NULL; - - try - { - - // Get singletons created - HttpRequest::createService(); - - // Start threading early so that thread memory is invariant - // over the test. - HttpRequest::startThread(); - - // create a new ref counted object with an implicit reference - req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); - - // Issue a Spin - HttpHandle handle = req->requestSpin(1); - ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID); - - // Issue a NoOp - handle = req->requestNoOp(&handler); - ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID); - - // Run the notification pump. - int count(0); - int limit(10); - while (count++ < limit && mHandlerCalls < 1) - { - req->update(1000); - usleep(100000); - } - ensure("NoOp notification received", mHandlerCalls == 1); - - // release the request object - delete req; - req = NULL; - - // Shut down service - HttpRequest::destroyService(); - - // Check memory usage - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); - // This memory test should work but could give problems as it - // relies on the worker thread picking up a friendly request - // to shutdown. Doing so, it drops references to things and - // we should go back to where we started. If it gives you - // problems, look into the code before commenting things out. - } - catch (...) - { - stop_thread(req); - delete req; - HttpRequest::destroyService(); - throw; - } -} // *NB: This test must be last. The sleeping webserver // won't respond for a long time. -- cgit v1.2.3 From 7997a9c4e58f67a8cf4d13cdc3e2a1b536bc1e4d Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 2 Jul 2012 18:08:36 -0400 Subject: Dos-to-unix line ending conversion. --- .../xui/en/floater_texture_fetch_debugger.xml | 682 ++++++++++----------- 1 file changed, 341 insertions(+), 341 deletions(-) diff --git a/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml b/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml index 44b6a63bca..f3f8d4ddca 100644 --- a/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml +++ b/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml @@ -1,341 +1,341 @@ - - - - 1, Total number of fetched textures: [NUM] - - - 2, Total number of fetching requests: [NUM] - - - 3, Total number of cache hits: [NUM] - - - 4, Total number of visible textures: [NUM] - - - 5, Total number of visible texture fetching requests: [NUM] - - - 6, Total number of fetched data: [SIZE1]KB, Decoded Data: [SIZE2]KB, [PIXEL]MPixels - - - 7, Total number of visible data: [SIZE1]KB, Decoded Data: [SIZE2]KB - - - 8, Total number of rendered data: [SIZE1]KB, Decoded Data: [SIZE2]KB, [PIXEL]MPixels - - - 9, Total time on cache readings: [TIME] seconds - - - 10, Total time on cache writings: [TIME] seconds - - - 11, Total time on decodings: [TIME] seconds - - - 12, Total time on gl texture creation: [TIME] seconds - - - 13, Total time on HTTP fetching: [TIME] seconds - - - 14, Total time on entire fetching: [TIME] seconds - - - 15, Refetching visibles from cache, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels - - - 16, Refetching visibles from HTTP, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels - - - - - - - - - - - - - - - + + + + 1, Total number of fetched textures: [NUM] + + + 2, Total number of fetching requests: [NUM] + + + 3, Total number of cache hits: [NUM] + + + 4, Total number of visible textures: [NUM] + + + 5, Total number of visible texture fetching requests: [NUM] + + + 6, Total number of fetched data: [SIZE1]KB, Decoded Data: [SIZE2]KB, [PIXEL]MPixels + + + 7, Total number of visible data: [SIZE1]KB, Decoded Data: [SIZE2]KB + + + 8, Total number of rendered data: [SIZE1]KB, Decoded Data: [SIZE2]KB, [PIXEL]MPixels + + + 9, Total time on cache readings: [TIME] seconds + + + 10, Total time on cache writings: [TIME] seconds + + + 11, Total time on decodings: [TIME] seconds + + + 12, Total time on gl texture creation: [TIME] seconds + + + 13, Total time on HTTP fetching: [TIME] seconds + + + 14, Total time on entire fetching: [TIME] seconds + + + 15, Refetching visibles from cache, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels + + + 16, Refetching visibles from HTTP, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels + + + + + + + + + + + + + + + -- cgit v1.2.3 From 2d7b7de20327a40be12a620debaae9917af16cd6 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 3 Jul 2012 13:06:46 -0400 Subject: More integration work for texture fetch timeouts. The fetch state machine received a new timeout during the WAIT_HTTP_REQ state. For the integration, rather than jump the state to done, we issue a request cancel and let the notification plumbing do the rest without any race conditions or special-case logic. --- indra/llcorehttp/_httplibcurl.cpp | 24 +++++++++++++++++++ indra/llcorehttp/_httplibcurl.h | 3 +++ indra/llcorehttp/_httpopcancel.cpp | 6 ++++- indra/llcorehttp/_httppolicy.cpp | 49 ++++++++++++++++++++++++++++++++++++-- indra/llcorehttp/_httppolicy.h | 3 +++ indra/llcorehttp/_httpservice.cpp | 25 +++++++++++++++++++ indra/llcorehttp/_httpservice.h | 8 +++++++ indra/newview/lltexturefetch.cpp | 20 ++++++++++++++-- 8 files changed, 133 insertions(+), 5 deletions(-) diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 39abca12c5..3c69ae1c96 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -189,6 +189,30 @@ void HttpLibcurl::addOp(HttpOpRequest * op) } +// Implements the transport part of any cancel operation. +// See if the handle is an active operation and if so, +// use the more complicated transport-based cancelation +// method to kill the request. +bool HttpLibcurl::cancel(HttpHandle handle) +{ + HttpOpRequest * op(static_cast(handle)); + active_set_t::iterator it(mActiveOps.find(op)); + if (mActiveOps.end() == it) + { + return false; + } + + // Cancel request + cancelRequest(op); + + // Drop references + mActiveOps.erase(it); + op->release(); + + return true; +} + + // *NOTE: cancelRequest logic parallels completeRequest logic. // Keep them synchronized as necessary. Caller is expected to // remove to op from the active list and release the op *after* diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 69f7bb2b6d..53972b1ffa 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -85,6 +85,9 @@ public: int getActiveCount() const; int getActiveCountInClass(int policy_class) const; + // Shadows HttpService's method + bool cancel(HttpHandle handle); + protected: /// Invoked when libcurl has indicated a request has been processed /// to completion and we need to move the request to a new state. diff --git a/indra/llcorehttp/_httpopcancel.cpp b/indra/llcorehttp/_httpopcancel.cpp index ad624d2e57..5c1f484109 100644 --- a/indra/llcorehttp/_httpopcancel.cpp +++ b/indra/llcorehttp/_httpopcancel.cpp @@ -61,7 +61,11 @@ HttpOpCancel::~HttpOpCancel() void HttpOpCancel::stageFromRequest(HttpService * service) { - // *FIXME: Need cancel functionality into services + if (! service->cancel(mHandle)) + { + mStatus = HttpStatus(HttpStatus::LLCORE, HE_HANDLE_NOT_FOUND); + } + addAsReply(); } diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 4350ff617b..1b10805b72 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -231,9 +231,12 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) { State & state(mState[policy_class]); - HttpReadyQueue::container_type & c(state.mReadyQueue.get_container()); - + // We don't scan retry queue because a priority change there + // is meaningless. The request will be issued based on retry + // intervals not priority value, which is now moot. + // Scan ready queue for requests that match policy + HttpReadyQueue::container_type & c(state.mReadyQueue.get_container()); for (HttpReadyQueue::container_type::iterator iter(c.begin()); c.end() != iter;) { HttpReadyQueue::container_type::iterator cur(iter++); @@ -253,6 +256,48 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior } +bool HttpPolicy::cancel(HttpHandle handle) +{ + for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) + { + State & state(mState[policy_class]); + + // Scan retry queue + HttpRetryQueue::container_type & c1(state.mRetryQueue.get_container()); + for (HttpRetryQueue::container_type::iterator iter(c1.begin()); c1.end() != iter;) + { + HttpRetryQueue::container_type::iterator cur(iter++); + + if (static_cast(*cur) == handle) + { + HttpOpRequest * op(*cur); + c1.erase(cur); // All iterators are now invalidated + op->cancel(); + op->release(); + return true; + } + } + + // Scan ready queue + HttpReadyQueue::container_type & c2(state.mReadyQueue.get_container()); + for (HttpReadyQueue::container_type::iterator iter(c2.begin()); c2.end() != iter;) + { + HttpReadyQueue::container_type::iterator cur(iter++); + + if (static_cast(*cur) == handle) + { + HttpOpRequest * op(*cur); + c2.erase(cur); // All iterators are now invalidated + op->cancel(); + op->release(); + return true; + } + } + } + + return false; +} + bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) { static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 90bb3b571d..a02bf084c1 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -92,6 +92,9 @@ public: // Shadows HttpService's method bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); + // Shadows HttpService's method as well + bool cancel(HttpHandle handle); + /// When transport is finished with an op and takes it off the /// active queue, it is delivered here for dispatch. Policy /// may send it back to the ready/retry queues if it needs another diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index 92c15b5b8f..f7d9813db0 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -219,6 +219,31 @@ bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t prio } + /// Try to find the given request handle on any of the request + /// queues and cancel the operation. + /// + /// @return True if the request was canceled. + /// + /// Threading: callable by worker thread. +bool HttpService::cancel(HttpHandle handle) +{ + bool canceled(false); + + // Request can't be on request queue so skip that. + + // Check the policy component's queues first + canceled = mPolicy->cancel(handle); + + if (! canceled) + { + // If that didn't work, check transport's. + canceled = mTransport->cancel(handle); + } + + return canceled; +} + + /// Threading: callable by worker thread. void HttpService::shutdown() { diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index d67e6e95a5..d24c497ca9 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -154,6 +154,14 @@ public: /// Threading: callable by worker thread. bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); + /// Try to find the given request handle on any of the request + /// queues and cancel the operation. + /// + /// @return True if the request was found and canceled. + /// + /// Threading: callable by worker thread. + bool cancel(HttpHandle handle); + /// Threading: callable by worker thread. HttpPolicy & getPolicy() { diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 36b878d6f2..b30b25e543 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1560,8 +1560,24 @@ bool LLTextureFetchWorker::doWork(S32 param) if(FETCHING_TIMEOUT < mRequestedTimer.getElapsedTimeF32()) { //timeout, abort. - mState = DONE; - return true; + LL_WARNS("Texture") << "Fetch of texture " << mID << " timed out after " + << mRequestedTimer.getElapsedTimeF32() + << " seconds. Canceling request." << LL_ENDL; + + if (LLCORE_HTTP_HANDLE_INVALID != mHttpHandle) + { + // Issue cancel on any outstanding request. Asynchronous + // so cancel may not actually take effect if operation is + // complete & queued. Either way, notification will + // complete and the request can be transitioned. + mFetcher->mHttpRequest->requestCancel(mHttpHandle, NULL); + } + else + { + // Shouldn't happen but if it does, cancel quickly. + mState = DONE; + return true; + } } setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); -- cgit v1.2.3 From e38a676c087d0adce9bd35cf3bdf8ff0e898f201 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 3 Jul 2012 19:22:24 -0400 Subject: Add CURLE_SEND_ERROR and CURLE_RECV_ERROR to the set of retryable errors. Data problems after connections are established should be retried as well. Extend to appropriate libcurl codes. Also allow our connectivity to drop to as low as a single connection when trying to recover. --- indra/llcorehttp/_httppolicy.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 1b10805b72..1e64924198 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -46,7 +46,7 @@ public: State() : mConnMax(DEFAULT_CONNECTIONS), mConnAt(DEFAULT_CONNECTIONS), - mConnMin(2), + mConnMin(1), mNextSample(0), mErrorCount(0), mErrorFactor(0) @@ -303,6 +303,8 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); static const HttpStatus cant_res_proxy(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_PROXY); static const HttpStatus cant_res_host(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST); + static const HttpStatus send_error(HttpStatus::EXT_CURL_EASY, CURLE_SEND_ERROR); + static const HttpStatus recv_error(HttpStatus::EXT_CURL_EASY, CURLE_RECV_ERROR); // Retry or finalize if (! op->mStatus) @@ -313,7 +315,9 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) ((op->mStatus.isHttpStatus() && op->mStatus.mType >= 499 && op->mStatus.mType <= 599) || cant_connect == op->mStatus || cant_res_proxy == op->mStatus || - cant_res_host == op->mStatus)) + cant_res_host == op->mStatus || + send_error == op->mStatus || + recv_error == op->mStatus)) { // Okay, worth a retry. We include 499 in this test as // it's the old 'who knows?' error from many grid services... -- cgit v1.2.3 From adce38800a3ac428c3e0b89fe5d62ca3baf97471 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 4 Jul 2012 23:30:58 -0400 Subject: Example program needs to set Accept: header to talk to Caps router. --- indra/llcorehttp/examples/http_texture_load.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp index 7efdf53959..e5951e8415 100644 --- a/indra/llcorehttp/examples/http_texture_load.cpp +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -39,6 +39,7 @@ #include "httprequest.h" #include "httphandler.h" #include "httpresponse.h" +#include "httpheaders.h" #include "bufferarray.h" #include "_mutex.h" @@ -76,6 +77,7 @@ class WorkingSet : public LLCore::HttpHandler { public: WorkingSet(); + ~WorkingSet(); bool reload(LLCore::HttpRequest *); @@ -111,6 +113,7 @@ public: int mErrorsHttp503; int mSuccesses; long mByteCount; + LLCore::HttpHeaders * mHeaders; }; @@ -316,6 +319,19 @@ WorkingSet::WorkingSet() mByteCount(0L) { mTextures.reserve(30000); + + mHeaders = new LLCore::HttpHeaders; + mHeaders->mHeaders.push_back("Accept: image/x-j2c"); +} + + +WorkingSet::~WorkingSet() +{ + if (mHeaders) + { + mHeaders->release(); + mHeaders = NULL; + } } @@ -337,11 +353,11 @@ bool WorkingSet::reload(LLCore::HttpRequest * hr) LLCore::HttpHandle handle; if (offset || length) { - handle = hr->requestGetByteRange(0, 0, buffer, offset, length, NULL, NULL, this); + handle = hr->requestGetByteRange(0, 0, buffer, offset, length, NULL, mHeaders, this); } else { - handle = hr->requestGet(0, 0, buffer, NULL, NULL, this); + handle = hr->requestGet(0, 0, buffer, NULL, mHeaders, this); } if (! handle) { -- cgit v1.2.3 From f37b90df5046fbe50309beada01022e35c5aa424 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 6 Jul 2012 18:09:17 -0400 Subject: SH-3222 Slow loading textures on Lag Me 1 Think I have found the major factor that causes the Linksys WRT54G V5 to fall over in testing scenarios: DNS. For some historical reason, we're trying to use libcurl without any DNS caching. My implementation echoed that and implemented it correctly and I was seeing a DNS request per request on the wire. The existing implementation tries to do that and has bugs because it is clearing caching DNS data querying only once every few seconds. Once I started emulating the bug, comms through the WRT became much, much more reliable. --- indra/llcorehttp/_httpinternal.h | 3 +++ indra/llcorehttp/_httpoprequest.cpp | 11 +++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h index bc0bd6a2ab..4ccace2b30 100644 --- a/indra/llcorehttp/_httpinternal.h +++ b/indra/llcorehttp/_httpinternal.h @@ -77,6 +77,9 @@ const int LOOP_SLEEP_NORMAL_MS = 2; // Block allocation size (a tuning parameter) is found // in bufferarray.h. +// Compatibility controls +const bool ENABLE_LINKSYS_WRT54G_V5_DNS_FIX = true; + } // end namespace LLCore #endif // _LLCORE_HTTP_INTERNAL_H_ diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 86ecee5b26..a19b6bd0dc 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -385,8 +385,15 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this); curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); - // *FIXME: Revisit this old DNS timeout setting - may no longer be valid - curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); + if (ENABLE_LINKSYS_WRT54G_V5_DNS_FIX) + { + curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 10); + } + else + { + // *FIXME: Revisit this old DNS timeout setting - may no longer be valid + curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); + } curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1); curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, DEFAULT_HTTP_REDIRECTS); // *FIXME: parameterize this later -- cgit v1.2.3 From d2af82aafc9ef569c9552f269ccf4e4fd38a1f33 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 6 Jul 2012 19:14:42 -0400 Subject: Experiment with ignoring priority in the library. Let upper layers sort things out or use policy classes (eventually) to arrange low and high priority traffic. Subjectively, I think this works better in practice (as I haven't implemented a dynamic priority setter yet). --- indra/llcorehttp/_httpinternal.h | 8 ++++++++ indra/llcorehttp/_httpreadyqueue.h | 39 +++++++++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h index 4ccace2b30..5f966500c9 100644 --- a/indra/llcorehttp/_httpinternal.h +++ b/indra/llcorehttp/_httpinternal.h @@ -32,6 +32,14 @@ // something wrong is probably happening. +// If '1', internal ready queues will not order ready +// requests by priority, instead it's first-come-first-served. +// Reprioritization requests have the side-effect of then +// putting the modified request at the back of the ready queue. + +#define LLCORE_READY_QUEUE_IGNORES_PRIORITY 1 + + namespace LLCore { diff --git a/indra/llcorehttp/_httpreadyqueue.h b/indra/llcorehttp/_httpreadyqueue.h index 87828834dc..968ca01258 100644 --- a/indra/llcorehttp/_httpreadyqueue.h +++ b/indra/llcorehttp/_httpreadyqueue.h @@ -30,6 +30,7 @@ #include +#include "_httpinternal.h" #include "_httpoprequest.h" @@ -47,10 +48,18 @@ namespace LLCore /// Threading: not thread-safe. Expected to be used entirely by /// a single thread, typically a worker thread of some sort. +#if LLCORE_READY_QUEUE_IGNORES_PRIORITY + +typedef std::deque HttpReadyQueueBase; + +#else + typedef std::priority_queue, LLCore::HttpOpRequestCompare> HttpReadyQueueBase; +#endif // LLCORE_READY_QUEUE_IGNORES_PRIORITY + class HttpReadyQueue : public HttpReadyQueueBase { public: @@ -66,16 +75,40 @@ protected: void operator=(const HttpReadyQueue &); // Not defined public: + +#if LLCORE_READY_QUEUE_IGNORES_PRIORITY + // Types and methods needed to make a std::deque look + // more like a std::priority_queue, at least for our + // purposes. + typedef HttpReadyQueueBase container_type; + + const_reference & top() const + { + return front(); + } + + void pop() + { + pop_front(); + } + + void push(const value_type & v) + { + push_back(v); + } + +#endif // LLCORE_READY_QUEUE_IGNORES_PRIORITY + const container_type & get_container() const { - return c; + return *this; } container_type & get_container() { - return c; + return *this; } - + }; // end class HttpReadyQueue -- cgit v1.2.3 From 70e976d1a87f4225c7593129a9c1c4732e2d38e4 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 6 Jul 2012 19:17:23 -0400 Subject: Odd that this was accepted by VS2010. It clearly wasn't right. --- indra/llcorehttp/_httpreadyqueue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcorehttp/_httpreadyqueue.h b/indra/llcorehttp/_httpreadyqueue.h index 968ca01258..8462b174b5 100644 --- a/indra/llcorehttp/_httpreadyqueue.h +++ b/indra/llcorehttp/_httpreadyqueue.h @@ -82,7 +82,7 @@ public: // purposes. typedef HttpReadyQueueBase container_type; - const_reference & top() const + const_reference top() const { return front(); } -- cgit v1.2.3 From f5f51d3cda8861b3b3a380cc96aaca98e572c377 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Sat, 7 Jul 2012 19:35:32 -0400 Subject: SH-3185 Fill in some FIXME/TODO cases Also added some comments and changed the callback userdata argument to be an HttpOpRequest rather than a libcurl handle. Less code, less clutter. --- indra/llcorehttp/_httpopcancel.cpp | 5 +++ indra/llcorehttp/_httpopcancel.h | 7 ++-- indra/llcorehttp/_httpoperation.cpp | 12 +++---- indra/llcorehttp/_httpoprequest.cpp | 62 ++++++++++++++++++++--------------- indra/llcorehttp/_httpoprequest.h | 12 +++++++ indra/llcorehttp/_httpopsetpriority.h | 3 ++ indra/llcorehttp/httpcommon.h | 18 ++++++++++ 7 files changed, 84 insertions(+), 35 deletions(-) diff --git a/indra/llcorehttp/_httpopcancel.cpp b/indra/llcorehttp/_httpopcancel.cpp index 5c1f484109..8e1105dc81 100644 --- a/indra/llcorehttp/_httpopcancel.cpp +++ b/indra/llcorehttp/_httpopcancel.cpp @@ -59,6 +59,11 @@ HttpOpCancel::~HttpOpCancel() {} +// Immediately search for the request on various queues +// and cancel operations if found. Return the status of +// the search and cancel as the status of this request. +// The canceled request will return a canceled status to +// its handler. void HttpOpCancel::stageFromRequest(HttpService * service) { if (! service->cancel(mHandle)) diff --git a/indra/llcorehttp/_httpopcancel.h b/indra/llcorehttp/_httpopcancel.h index 4d927d1aaf..659d28955f 100644 --- a/indra/llcorehttp/_httpopcancel.h +++ b/indra/llcorehttp/_httpopcancel.h @@ -43,9 +43,10 @@ namespace LLCore /// HttpOpCancel requests that a previously issued request -/// be canceled, if possible. Requests that have been made -/// active and are available for sending on the wire cannot -/// be canceled. +/// be canceled, if possible. This includes active requests +/// that may be in the middle of an HTTP transaction. Any +/// completed request will not be canceled and will return +/// its final status unchanged. class HttpOpCancel : public HttpOperation { diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index d80a8236e6..910dbf1f2f 100644 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -91,31 +91,31 @@ void HttpOperation::setReplyPath(HttpReplyQueue * reply_queue, void HttpOperation::stageFromRequest(HttpService *) { - // *FIXME: Message this a better way later. // Default implementation should never be called. This // indicates an operation making a transition that isn't // defined. - llassert_always(false); + LL_ERRS("HttpCore") << "Default stateFromRequest method may not be called." + << LL_ENDL; } void HttpOperation::stageFromReady(HttpService *) { - // *FIXME: Message this a better way later. // Default implementation should never be called. This // indicates an operation making a transition that isn't // defined. - llassert_always(false); + LL_ERRS("HttpCore") << "Default stateFromReady method may not be called." + << LL_ENDL; } void HttpOperation::stageFromActive(HttpService *) { - // *FIXME: Message this a better way later. // Default implementation should never be called. This // indicates an operation making a transition that isn't // defined. - llassert_always(false); + LL_ERRS("HttpCore") << "Default stateFromActive method may not be called." + << LL_ENDL; } diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index a19b6bd0dc..bec82d8449 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -344,6 +344,13 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id, } +// Sets all libcurl options and data for a request. +// +// Used both for initial requests and to 'reload' for +// a retry, generally with a different CURL handle. +// Junk may be left around from a failed request and that +// needs to be cleaned out. +// HttpStatus HttpOpRequest::prepareRequest(HttpService * service) { // Scrub transport and result data for retried op case @@ -387,20 +394,29 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) if (ENABLE_LINKSYS_WRT54G_V5_DNS_FIX) { + // The Linksys WRT54G V5 router has an issue with frequent + // DNS lookups from LAN machines. If they happen too often, + // like for every HTTP request, the router gets annoyed after + // about 700 or so requests and starts issuing TCP RSTs to + // new connections. Reuse the DNS lookups for even a few + // seconds and no RSTs. curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 10); } else { - // *FIXME: Revisit this old DNS timeout setting - may no longer be valid + // *TODO: Revisit this old DNS timeout setting - may no longer be valid + // I don't think this is valid anymore, the Multi shared DNS + // cache is working well. For the case of naked easy handles, + // consider using a shared DNS object. curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); } curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1); curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, DEFAULT_HTTP_REDIRECTS); // *FIXME: parameterize this later + curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, DEFAULT_HTTP_REDIRECTS); curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback); - curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, mCurlHandle); + curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, this); curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback); - curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, mCurlHandle); + curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, this); curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1); curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0); @@ -468,7 +484,9 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) break; default: - // *FIXME: fail out here + LL_ERRS("CoreHttp") << "Invalid HTTP method in request: " + << int(mReqMethod) << ". Can't recover." + << LL_ENDL; break; } @@ -476,7 +494,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) if (mTracing >= TRACE_CURL_HEADERS) { curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1); - curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, mCurlHandle); + curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, this); curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGFUNCTION, debugCallback); } @@ -524,7 +542,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS)) { curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback); - curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, mCurlHandle); + curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, this); } if (status) @@ -537,10 +555,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) size_t HttpOpRequest::writeCallback(void * data, size_t size, size_t nmemb, void * userdata) { - CURL * handle(static_cast(userdata)); - HttpOpRequest * op(NULL); - curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); - // *FIXME: check the pointer + HttpOpRequest * op(static_cast(userdata)); if (! op->mReplyBody) { @@ -554,10 +569,7 @@ size_t HttpOpRequest::writeCallback(void * data, size_t size, size_t nmemb, void size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void * userdata) { - CURL * handle(static_cast(userdata)); - HttpOpRequest * op(NULL); - curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); - // *FIXME: check the pointer + HttpOpRequest * op(static_cast(userdata)); if (! op->mReqBody) { @@ -567,7 +579,8 @@ size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void const size_t body_size(op->mReqBody->size()); if (body_size <= op->mCurlBodyPos) { - // *FIXME: should probably log this event - unexplained + LL_WARNS("HttpCore") << "Request body position beyond body size. Aborting request." + << LL_ENDL; return 0; } @@ -586,10 +599,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi static const char con_ran_line[] = "content-range:"; static const size_t con_ran_line_len = sizeof(con_ran_line) - 1; - CURL * handle(static_cast(userdata)); - HttpOpRequest * op(NULL); - curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); - // *FIXME: check the pointer + HttpOpRequest * op(static_cast(userdata)); const size_t hdr_size(size * nmemb); const char * hdr_data(static_cast(data)); // Not null terminated @@ -609,7 +619,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi } else if (op->mProcFlags & PF_SCAN_RANGE_HEADER) { - char hdr_buffer[128]; + char hdr_buffer[128]; // Enough for a reasonable header size_t frag_size((std::min)(hdr_size, sizeof(hdr_buffer) - 1)); memcpy(hdr_buffer, hdr_data, frag_size); @@ -638,8 +648,10 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi else { // Ignore the unparsable. - // *FIXME: Maybe issue a warning into the log here - ; + LL_INFOS_ONCE("CoreHttp") << "Problem parsing odd Content-Range header: '" + << std::string(hdr_data, frag_size) + << "'. Ignoring." + << LL_ENDL; } } } @@ -668,9 +680,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffer, size_t len, void * userdata) { - HttpOpRequest * op(NULL); - curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); - // *FIXME: check the pointer + HttpOpRequest * op(static_cast(userdata)); std::string safe_line; std::string tag; diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 5d2417466c..a4c5fbb3c2 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -49,6 +49,16 @@ class HttpOptions; /// HttpOpRequest requests a supported HTTP method invocation with /// option and header overrides. +/// +/// Essentially an RPC to get an HTTP GET, POST or PUT executed +/// asynchronously with options to override behaviors and HTTP +/// headers. +/// +/// Constructor creates a raw object incapable of useful work. +/// A subsequent call to one of the setupXXX() methods provides +/// the information needed to make a working request which can +/// then be enqueued to a request queue. +/// class HttpOpRequest : public HttpOperation { @@ -177,6 +187,8 @@ public: // Free functions // --------------------------------------- +// Internal function to append the contents of an HttpHeaders +// instance to a curl_slist object. curl_slist * append_headers_to_slist(const HttpHeaders *, curl_slist * slist); } // end namespace LLCore diff --git a/indra/llcorehttp/_httpopsetpriority.h b/indra/llcorehttp/_httpopsetpriority.h index 724293ef78..31706b737c 100644 --- a/indra/llcorehttp/_httpopsetpriority.h +++ b/indra/llcorehttp/_httpopsetpriority.h @@ -42,6 +42,9 @@ namespace LLCore /// searches the various queues looking for a given /// request handle and changing it's priority if /// found. +/// +/// *NOTE: This will very likely be removed in the near future +/// when priority is removed from the library. class HttpOpSetPriority : public HttpOperation { diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index 42b75edb41..576a113e54 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -168,6 +168,24 @@ enum HttpError /// a successful status or an error. The application is responsible /// for making that determination and a range like [200, 299] isn't /// automatically assumed to be definitive. +/// +/// Examples: +/// +/// 1. Construct a default, successful status code: +/// HttpStatus(); +/// +/// 2. Construct a successful, HTTP 200 status code: +/// HttpStatus(200); +/// +/// 3. Construct a failed, HTTP 404 not-found status code: +/// HttpStatus(404); +/// +/// 4. Construct a failed libcurl couldn't connect status code: +/// HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); +/// +/// 5. Construct an HTTP 301 status code to be treated as success: +/// HttpStatus(301, HE_SUCCESS); +/// struct HttpStatus { -- cgit v1.2.3 From 348db20b92f1f1f85712c5a9a862ef079102bbee Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 9 Jul 2012 11:47:47 -0400 Subject: SH-3187 Issue smarter 'Range' requests for textures. First, try to issue ranged GETs that are always at least partially satisfiable. This will keep Varnish-type caches from simply sending back 200/full asset responses to unsatisfiable requests. Implement awareness of Content-Range headers as well. Currently they're not coming back but they will be someday. --- indra/llcorehttp/httpresponse.h | 6 +++ indra/newview/lltexturefetch.cpp | 90 +++++++++++++++++++++++++++++++++------- 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h index 925cf81586..65e403cec8 100644 --- a/indra/llcorehttp/httpresponse.h +++ b/indra/llcorehttp/httpresponse.h @@ -111,6 +111,12 @@ public: /// If a 'Range:' header was used, these methods are involved /// in setting and returning data about the actual response. + /// If both @offset and @length are returned as 0, we probably + /// didn't get a Content-Range header in the response. This + /// occurs with various Capabilities-based services and the + /// caller is going to have to make assumptions on receipt of + /// a 206 status. The @full value may also be zero in cases of + /// parsing problems or a wild-carded length response. void getRange(unsigned int * offset, unsigned int * length, unsigned int * full) const { *offset = mReplyOffset; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index b30b25e543..214a6099d0 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -557,14 +557,14 @@ private: LLCore::BufferArray * mHttpBufferArray; // Refcounted pointer to response data int mHttpPolicyClass; bool mHttpActive; // Active request to http library - unsigned int mHttpReplySize; - unsigned int mHttpReplyOffset; + unsigned int mHttpReplySize; // Actual received data size + unsigned int mHttpReplyOffset; // Actual received data offset bool mHttpHasResource; // Counts against Fetcher's mHttpSemaphore // State history - U32 mCacheReadCount; - U32 mCacheWriteCount; - U32 mResourceWaitCount; + U32 mCacheReadCount; + U32 mCacheWriteCount; + U32 mResourceWaitCount; // Requests entering WAIT_HTTP_RESOURCE2 }; ////////////////////////////////////////////////////////////////////////////// @@ -1043,6 +1043,8 @@ void LLTextureFetchWorker::resetFormattedData() { mFormattedImage->deleteData(); } + mHttpReplySize = 0; + mHttpReplyOffset = 0; mHaveAllData = FALSE; } @@ -1120,6 +1122,8 @@ bool LLTextureFetchWorker::doWork(S32 param) mHttpBufferArray->release(); mHttpBufferArray = NULL; } + mHttpReplySize = 0; + mHttpReplyOffset = 0; mHaveAllData = FALSE; clearPackets(); // TODO: Shouldn't be necessary mCacheReadHandle = LLTextureCache::nullHandle(); @@ -1402,6 +1406,21 @@ bool LLTextureFetchWorker::doWork(S32 param) mRequestedDiscard = mDesiredDiscard; mRequestedSize -= cur_size; mRequestedOffset = cur_size; + if (mRequestedOffset) + { + // Texture fetching often issues 'speculative' loads that + // start beyond the end of the actual asset. Some cache/web + // systems, e.g. Varnish, will respond to this not with a + // 416 but with a 200 and the entire asset in the response + // body. By ensuring that we always have a partially + // satisfiable Range request, we avoid that hit to the network. + // We just have to deal with the overlapping data which is made + // somewhat harder by the fact that grid services don't necessarily + // return the Content-Range header on 206 responses. *Sigh* + mRequestedOffset -= 1; + mRequestedSize += 1; + } + mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; if (!mUrl.empty()) { @@ -1507,9 +1526,29 @@ bool LLTextureFetchWorker::doWork(S32 param) return true; } - const S32 append_size(mHttpBufferArray->size()); - const S32 total_size(cur_size + append_size); + S32 append_size(mHttpBufferArray->size()); + S32 total_size(cur_size + append_size); + S32 src_offset(0); llassert_always(append_size == mRequestedSize); + if (mHttpReplyOffset && mHttpReplyOffset != cur_size) + { + // In case of a partial response, our offset may + // not be trivially contiguous with the data we have. + // Get back into alignment. + if (mHttpReplyOffset > cur_size) + { + LL_WARNS("Texture") << "Partial HTTP response produces break in image data for texture " + << mID << ". Aborting load." << LL_ENDL; + mState = DONE; + releaseHttpSemaphore(); + return true; + } + src_offset = cur_size - mHttpReplyOffset; + append_size -= src_offset; + total_size -= src_offset; + mRequestedSize -= src_offset; // Make requested values reflect useful part + mRequestedOffset += src_offset; + } if (mFormattedImage.isNull()) { @@ -1522,7 +1561,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } } - if (mHaveAllData /* && mRequestedDiscard == 0*/) //the image file is fully loaded. + if (mHaveAllData) //the image file is fully loaded. { mFileSize = total_size; } @@ -1536,7 +1575,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { memcpy(buffer, mFormattedImage->getData(), cur_size); } - mHttpBufferArray->read(0, (char *) buffer + cur_size, append_size); + mHttpBufferArray->read(src_offset, (char *) buffer + cur_size, append_size); // NOTE: setData releases current data and owns new data (buffer) mFormattedImage->setData(buffer, total_size); @@ -1544,6 +1583,8 @@ bool LLTextureFetchWorker::doWork(S32 param) // Done with buffer array mHttpBufferArray->release(); mHttpBufferArray = NULL; + mHttpReplySize = 0; + mHttpReplyOffset = 0; mLoadedDiscard = mRequestedDiscard; mState = DECODE_IMAGE; @@ -1576,6 +1617,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { // Shouldn't happen but if it does, cancel quickly. mState = DONE; + releaseHttpSemaphore(); return true; } } @@ -2014,15 +2056,33 @@ S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, if (data_size > 0) { // *TODO: set the formatted image data here directly to avoid the copy - // *FIXME: deal with actual offset and actual datasize, don't assume - // server gave exactly what was asked for. - - llassert_always(NULL == mHttpBufferArray); // Hold on to body for later copy + llassert_always(NULL == mHttpBufferArray); body->addRef(); mHttpBufferArray = body; + if (partial) + { + unsigned int offset(0), length(0), full_length(0); + response->getRange(&offset, &length, &full_length); + if (! offset && ! length) + { + // This is the case where we receive a 206 status but + // there wasn't a useful Content-Range header in the response. + // This could be because it was badly formatted but is more + // likely due to capabilities services which scrub headers + // from responses. Assume we got what we asked for... + mHttpReplySize = mRequestedSize; + mHttpReplyOffset = mRequestedOffset; + } + else + { + mHttpReplySize = length; + mHttpReplyOffset = offset; + } + } + if (! partial) { // Response indicates this is the entire asset regardless @@ -2040,10 +2100,8 @@ S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, llassert_always(mDecodeHandle == 0); mFormattedImage = NULL; // discard any previous data we had } - else if (data_size < mRequestedSize /*&& mRequestedDiscard == 0*/) + else if (data_size < mRequestedSize) { - // *FIXME: I think we can treat this as complete regardless - // of requested discard level. Revisit this... mHaveAllData = TRUE; } else if (data_size > mRequestedSize) -- cgit v1.2.3 From 398d78a77348fb4c5ee4e96b7ed6f981b1042627 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 9 Jul 2012 13:28:50 -0400 Subject: Rework the 'sleep' logic in the test HTTP server so that the 30-second hang doesn't break subsequent tests. Did this by introducing threads into the HTTP server as I can't find the magic to detect that my client has gone away. --- indra/llcorehttp/tests/test_httprequest.hpp | 5 ----- indra/llcorehttp/tests/test_llcorehttp_peer.py | 6 ++++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index ed4e239fe7..914f35ec3d 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -1381,8 +1381,6 @@ void HttpRequestTestObjectType::test<13>() } -// *NB: This test must be last. The sleeping webserver -// won't respond for a long time. template <> template <> void HttpRequestTestObjectType::test<14>() { @@ -1503,9 +1501,6 @@ void HttpRequestTestObjectType::test<14>() throw; } } -// *NOTE: This test ^^^^^^^^ must be the last one in the set. It uses a -// sleeping service that interferes with other HTTP tests. Keep it -// last until that little HTTP server can get some attention... } // end namespace tut diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 0e38e5a87f..9e1847c66e 100644 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -32,10 +32,12 @@ $/LicenseInfo$ import os import sys import time +import select from threading import Thread from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler +from SocketServer import ThreadingMixIn -mydir = os.path.dirname(__file__) # expected to be .../indra/llmessage/tests/ +mydir = os.path.dirname(__file__) # expected to be .../indra/llcorehttp/tests/ sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python")) from indra.util.fastest_elementtree import parse as xml_parse from indra.base import llsd @@ -144,7 +146,7 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): # Suppress error output as well pass -class Server(HTTPServer): +class Server(ThreadingMixIn, HTTPServer): # This pernicious flag is on by default in HTTPServer. But proper # operation of freeport() absolutely depends on it being off. allow_reuse_address = False -- cgit v1.2.3 From b3659f2eba2a0a43f48e2c395f1a327dc1114098 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 9 Jul 2012 17:04:07 -0400 Subject: Safe implementation of the HTTP resource waiter release method. Doesn't use sets or maps and so there's no ordering assumption to be violated when priorities are changed. Should also be faster. Still want to get rid of the ancillary list, however... --- indra/newview/lltexturefetch.cpp | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 214a6099d0..9d3e7eb2b6 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -28,6 +28,7 @@ #include #include +#include #include "llstl.h" @@ -3358,32 +3359,35 @@ void LLTextureFetch::releaseHttpWaiters() if (mHttpWaitResource.empty()) return; - - const size_t limit(mHttpWaitResource.size()); - tids.reserve(limit); - for (wait_http_res_queue_t::iterator iter(mHttpWaitResource.begin()); - mHttpWaitResource.end() != iter; - ++iter) - { - tids.push_back(*iter); - } + tids.reserve(mHttpWaitResource.size()); + tids.assign(mHttpWaitResource.begin(), mHttpWaitResource.end()); } // -Mfnq // Now lookup the UUUIDs to find valid requests and sort - // them in priority order, highest to lowest. - typedef std::set worker_set_t; - worker_set_t tids2; - - for (uuid_vec_t::const_iterator iter(tids.begin()); + // them in priority order, highest to lowest. We're going + // to modify priority later as a side-effect of releasing + // these objects. That, in turn, would violate the partial + // ordering assumption of std::set, std::map, etc. so we + // don't use those containers. We use a vector and an explicit + // sort to keep the containers valid later. + typedef std::vector worker_list_t; + worker_list_t tids2; + + tids2.reserve(tids.size()); + for (uuid_vec_t::iterator iter(tids.begin()); tids.end() != iter; ++iter) { LLTextureFetchWorker * worker(getWorker(* iter)); if (worker) { - tids2.insert(worker); + tids2.push_back(worker); } } + + // Sort into priority order + LLTextureFetchWorker::Compare compare; + std::sort(tids2.begin(), tids2.end(), compare); tids.clear(); // Release workers up to the high water mark. Since we aren't @@ -3391,7 +3395,7 @@ void LLTextureFetch::releaseHttpWaiters() // with other callers. Do defensive things like getting // refreshed counts of requests and checking if someone else // has moved any worker state around.... - for (worker_set_t::iterator iter2(tids2.begin()); + for (worker_list_t::iterator iter2(tids2.begin()); tids2.end() != iter2 && mHttpSemaphore > 0; ++iter2) { -- cgit v1.2.3 From d6cbe006d3a9448b8897b42c3a817f914a279f02 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 9 Jul 2012 17:29:21 -0400 Subject: Take body size as the reply size when Content-Range header isn't available. --- indra/newview/lltexturefetch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 9d3e7eb2b6..a1f99eeb25 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -2074,7 +2074,7 @@ S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, // This could be because it was badly formatted but is more // likely due to capabilities services which scrub headers // from responses. Assume we got what we asked for... - mHttpReplySize = mRequestedSize; + mHttpReplySize = data_size; mHttpReplyOffset = mRequestedOffset; } else -- cgit v1.2.3 From 1c3af2658e30b672d658e1d208764a7e50cb7e5b Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 10 Jul 2012 13:29:24 -0400 Subject: SH-3192 Fix Boost 1.48.0 libraries to support Debug builds on Windows. Tweaked the boost source as per Boost issue #6185 using 1.49.0 sources and this picks up the new build. Debug viewer builds and runs. --- autobuild.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 8a9a01ca61..da4a50638d 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -186,9 +186,9 @@ archive hash - 0c4678ac85395f5f5294b63da1d79007 + 36aa500e13cdde61607b6e93065206ec url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/249117/arch/Darwin/installer/boost-1.48.0-darwin-20120208.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/261457/arch/Darwin/installer/boost-1.48.0-darwin-20120710.tar.bz2 name darwin @@ -198,9 +198,9 @@ archive hash - 848766eac189e0fa785f4a025532acd9 + 18602d44bd435eb0d7189f436ff2cb0f url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/249117/arch/Linux/installer/boost-1.48.0-linux-20120208.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/261457/arch/Linux/installer/boost-1.48.0-linux-20120710.tar.bz2 name linux @@ -210,9 +210,9 @@ archive hash - cd1e60a00d40f4475ae5e0aca86f74c1 + dc8f5dc6be04c64bf3460b4932b18457 url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/249117/arch/CYGWIN/installer/boost-1.48.0-windows-20120208.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/261457/arch/CYGWIN/installer/boost-1.48.0-windows-20120710.tar.bz2 name windows -- cgit v1.2.3 From a5ba9c0eb327d2fb38a39560a34712e844a71a79 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 10 Jul 2012 16:56:38 -0400 Subject: SH-3276 Handle 416 status back from texture fetches as okay. A 416 will just mean there's no more data and whatever we have is complete. --- indra/newview/lltexturefetch.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index a1f99eeb25..5c39504243 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1364,6 +1364,10 @@ bool LLTextureFetchWorker::doWork(S32 param) } mState = SEND_HTTP_REQ; acquireHttpSemaphore(); + + // *NOTE: You must invoke releaseHttpSemaphore() if you transition + // to a state other than SEND_HTTP_REQ or WAIT_HTTP_REQ or abort + // the request. } if (mState == WAIT_HTTP_RESOURCE2) @@ -1486,13 +1490,16 @@ bool LLTextureFetchWorker::doWork(S32 param) { LL_INFOS_ONCE("Texture") << "Texture server busy (503): " << mUrl << LL_ENDL; } + else if (http_not_sat == mGetStatus) + { + // Allowed, we'll accept whatever data we have as complete. + mHaveAllData = TRUE; + } else { llinfos << "HTTP GET failed for: " << mUrl << " Status: " << mGetStatus.toHex() << " Reason: '" << mGetReason << "'" - // *FIXME: Add retry info for reporting purposes... - // << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl; } @@ -1839,7 +1846,7 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe success = false; std::string reason(status.toString()); setGetStatus(status, reason); - llwarns << "CURL GET FAILED, status: " << status.toHex() + llwarns << "CURL GET FAILED, status: " << status.toHex() << " reason: " << reason << llendl; } else -- cgit v1.2.3 From bc72acbfd2410e01946375bcfa29cf37a7c01c17 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 10 Jul 2012 18:50:21 -0400 Subject: SH-3244 Syscall avoidance in HttpRequest::update() method Well, achieved that by doing work in bulk when needed. But turned into some additional things. Change timebase from mS to uS as, well, things are headed that way. Implement an HttpReplyQueue::fetchAll method (advertised one, hadn't implemented it). --- indra/llcorehttp/_httpreplyqueue.cpp | 20 +++++++++++ indra/llcorehttp/_httprequestqueue.cpp | 7 ++-- indra/llcorehttp/examples/http_texture_load.cpp | 2 +- indra/llcorehttp/httprequest.cpp | 40 +++++++++++++++++---- indra/llcorehttp/httprequest.h | 7 ++-- indra/llcorehttp/tests/test_httprequest.hpp | 48 ++++++++++++------------- indra/newview/llappviewer.cpp | 2 +- indra/newview/lltexturefetch.cpp | 19 ++++------ 8 files changed, 92 insertions(+), 53 deletions(-) diff --git a/indra/llcorehttp/_httpreplyqueue.cpp b/indra/llcorehttp/_httpreplyqueue.cpp index a354ed7e10..558b7bdee9 100644 --- a/indra/llcorehttp/_httpreplyqueue.cpp +++ b/indra/llcorehttp/_httpreplyqueue.cpp @@ -84,4 +84,24 @@ HttpOperation * HttpReplyQueue::fetchOp() return result; } + +void HttpReplyQueue::fetchAll(OpContainer & ops) +{ + // Not valid putting something back on the queue... + llassert_always(ops.empty()); + + { + HttpScopedLock lock(mQueueMutex); + + if (! mQueue.empty()) + { + mQueue.swap(ops); + } + } + + // Caller also acquires the reference counts on each op. + return; +} + + } // end namespace LLCore diff --git a/indra/llcorehttp/_httprequestqueue.cpp b/indra/llcorehttp/_httprequestqueue.cpp index 9acac665a9..c16966d078 100644 --- a/indra/llcorehttp/_httprequestqueue.cpp +++ b/indra/llcorehttp/_httprequestqueue.cpp @@ -120,10 +120,9 @@ HttpOperation * HttpRequestQueue::fetchOp(bool wait) void HttpRequestQueue::fetchAll(bool wait, OpContainer & ops) { - // Note: Should probably test whether we're empty or not here. - // A target passed in with entries is likely also carrying - // reference counts and we're going to leak something. - ops.clear(); + // Not valid putting something back on the queue... + llassert_always(ops.empty()); + { HttpScopedLock lock(mQueueMutex); diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp index e5951e8415..bcb322bd5c 100644 --- a/indra/llcorehttp/examples/http_texture_load.cpp +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -244,7 +244,7 @@ int main(int argc, char** argv) int passes(0); while (! ws.reload(hr)) { - hr->update(5000); + hr->update(5000000); ms_sleep(2); if (0 == (++passes % 200)) { diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 3a55a849b9..9b739a8825 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -296,17 +296,43 @@ HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) } -HttpStatus HttpRequest::update(long millis) +HttpStatus HttpRequest::update(long usecs) { - const HttpTime limit(totalTime() + (1000 * HttpTime(millis))); HttpOperation * op(NULL); - while (limit >= totalTime() && (op = mReplyQueue->fetchOp())) + + if (usecs) { - // Process operation - op->visitNotifier(this); + const HttpTime limit(totalTime() + HttpTime(usecs)); + while (limit >= totalTime() && (op = mReplyQueue->fetchOp())) + { + // Process operation + op->visitNotifier(this); - // We're done with the operation - op->release(); + // We're done with the operation + op->release(); + } + } + else + { + // Same as above, just no time limit + HttpReplyQueue::OpContainer replies; + mReplyQueue->fetchAll(replies); + if (! replies.empty()) + { + for (HttpReplyQueue::OpContainer::iterator iter(replies.begin()); + replies.end() != iter; + ++iter) + { + // Swap op pointer for NULL; + op = *iter; *iter = NULL; + + // Process operation + op->visitNotifier(this); + + // We're done with the operation + op->release(); + } + } } return HttpStatus(); diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 134a61b618..9dd53f4483 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -341,13 +341,14 @@ public: /// are expected to return 'quickly' and do any significant processing /// outside of the notification callback to onCompleted(). /// - /// @param millis Maximum number of wallclock milliseconds to + /// @param usecs Maximum number of wallclock microseconds to /// spend in the call. As hinted at above, this /// is partly a function of application code so it's - /// a soft limit. + /// a soft limit. A '0' value will run without + /// time limit. /// /// @return Standard status code. - HttpStatus update(long millis); + HttpStatus update(long usecs); /// @} diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 914f35ec3d..ba7c757af4 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -260,7 +260,7 @@ void HttpRequestTestObjectType::test<3>() int limit(20); while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -275,7 +275,7 @@ void HttpRequestTestObjectType::test<3>() limit = 100; while (count++ < limit && mHandlerCalls < 2) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); @@ -358,8 +358,8 @@ void HttpRequestTestObjectType::test<4>() int limit(20); while (count++ < limit && mHandlerCalls < 2) { - req1->update(1000); - req2->update(1000); + req1->update(1000000); + req2->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -375,8 +375,8 @@ void HttpRequestTestObjectType::test<4>() limit = 100; while (count++ < limit && mHandlerCalls < 3) { - req1->update(1000); - req2->update(1000); + req1->update(1000000); + req2->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); @@ -459,7 +459,7 @@ void HttpRequestTestObjectType::test<5>() int limit(10); while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("NoOp notification received", mHandlerCalls == 1); @@ -535,7 +535,7 @@ void HttpRequestTestObjectType::test<6>() int limit(10); while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("No notifications received", mHandlerCalls == 0); @@ -616,7 +616,7 @@ void HttpRequestTestObjectType::test<7>() int limit(50); // With one retry, should fail quickish while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -632,7 +632,7 @@ void HttpRequestTestObjectType::test<7>() limit = 100; while (count++ < limit && mHandlerCalls < 2) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); @@ -733,7 +733,7 @@ void HttpRequestTestObjectType::test<8>() int limit(10); while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -749,7 +749,7 @@ void HttpRequestTestObjectType::test<8>() limit = 10; while (count++ < limit && mHandlerCalls < 2) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); @@ -843,7 +843,7 @@ void HttpRequestTestObjectType::test<9>() int limit(10); while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -859,7 +859,7 @@ void HttpRequestTestObjectType::test<9>() limit = 10; while (count++ < limit && mHandlerCalls < 2) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); @@ -955,7 +955,7 @@ void HttpRequestTestObjectType::test<10>() int limit(10); while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -971,7 +971,7 @@ void HttpRequestTestObjectType::test<10>() limit = 10; while (count++ < limit && mHandlerCalls < 2) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); @@ -1074,7 +1074,7 @@ void HttpRequestTestObjectType::test<11>() int limit(10); while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -1090,7 +1090,7 @@ void HttpRequestTestObjectType::test<11>() limit = 10; while (count++ < limit && mHandlerCalls < 2) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); @@ -1194,7 +1194,7 @@ void HttpRequestTestObjectType::test<12>() int limit(10); while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -1210,7 +1210,7 @@ void HttpRequestTestObjectType::test<12>() limit = 10; while (count++ < limit && mHandlerCalls < 2) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); @@ -1316,7 +1316,7 @@ void HttpRequestTestObjectType::test<13>() int limit(10); while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -1333,7 +1333,7 @@ void HttpRequestTestObjectType::test<13>() limit = 10; while (count++ < limit && mHandlerCalls < 2) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); @@ -1435,7 +1435,7 @@ void HttpRequestTestObjectType::test<14>() int limit(50); // With one retry, should fail quickish while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -1451,7 +1451,7 @@ void HttpRequestTestObjectType::test<14>() limit = 100; while (count++ < limit && mHandlerCalls < 2) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 8243d4f2f3..0549a972e1 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5398,7 +5398,7 @@ void CoreHttp::cleanup() { while (! mStopped && LLTimer::getTotalSeconds() < (mStopRequested + MAX_THREAD_WAIT_TIME)) { - mRequest->update(200); + mRequest->update(200000); ms_sleep(50); } if (! mStopped) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 5c39504243..8314031f14 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -2740,22 +2740,14 @@ void LLTextureFetch::commonUpdate() // Run a cross-thread command, if any. cmdDoWork(); - // Update Curl on same thread as mCurlGetRequest was constructed - LLCore::HttpStatus status = mHttpRequest->update(200); + // Deliver all completion notifications + LLCore::HttpStatus status = mHttpRequest->update(0); if (! status) { LL_INFOS_ONCE("Texture") << "Problem during HTTP servicing. Reason: " << status.toString() << LL_ENDL; } - -#if 0 - // *FIXME: maybe implement this another way... - if (processed > 0) - { - lldebugs << "processed: " << processed << " messages." << llendl; - } -#endif } @@ -2840,7 +2832,8 @@ void LLTextureFetch::endThread() void LLTextureFetch::threadedUpdate() { llassert_always(mHttpRequest); - + +#if 0 // Limit update frequency const F32 PROCESS_TIME = 0.05f; static LLFrameTimer process_timer; @@ -2849,9 +2842,10 @@ void LLTextureFetch::threadedUpdate() return; } process_timer.reset(); +#endif commonUpdate(); - + #if 0 const F32 INFO_TIME = 1.0f; static LLFrameTimer info_timer; @@ -2865,7 +2859,6 @@ void LLTextureFetch::threadedUpdate() } } #endif - } ////////////////////////////////////////////////////////////////////////////// -- cgit v1.2.3 From 7010459f04177aef1875a110b3d33e10c8ec5cad Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 11 Jul 2012 15:53:57 -0400 Subject: SH-3240 Capture Content-Type and Content-Encoding headers. HttpResponse object now has two strings for these content headers. Either or both may be empty. Tidied up the cross-platform string code and got more defensive about the length of a header line. Integration test for the new response object. --- indra/llcorehttp/_httpoprequest.cpp | 158 ++++++++++++++++++------- indra/llcorehttp/_httpoprequest.h | 4 + indra/llcorehttp/httpresponse.h | 19 ++- indra/llcorehttp/tests/test_httprequest.hpp | 126 ++++++++++++++++++++ indra/llcorehttp/tests/test_llcorehttp_peer.py | 2 +- 5 files changed, 263 insertions(+), 46 deletions(-) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index bec82d8449..1854d7ada4 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -74,16 +74,16 @@ void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::string & safe_line); -#if LL_WINDOWS +// OS-neutral string comparisons of various types +int os_strncasecmp(const char *s1, const char *s2, size_t n); +int os_strcasecmp(const char *s1, const char *s2); +char * os_strtok_r(char *str, const char *delim, char **saveptr); -// Not available on windows where the legacy strtok interface -// is thread-safe. -char *strtok_r(char *str, const char *delim, char **saveptr); -#endif // LL_WINDOWS +static const char * const hdr_whitespace(" \t"); +static const char * const hdr_separator(": \t"); - -} +} // end anonymous namespace namespace LLCore @@ -228,7 +228,8 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) // Got an explicit offset/length in response response->setRange(mReplyOffset, mReplyLength, mReplyFullLength); } - + response->setContent(mReplyConType, mReplyConEncode); + mUserHandler->onCompleted(static_cast(this), response); response->release(); @@ -315,7 +316,7 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id, HttpOptions * options, HttpHeaders * headers) { - mProcFlags = 0; + mProcFlags = PF_SCAN_CONTENT_HEADERS; // Always scan for content headers mReqPolicy = policy_id; mReqPriority = priority; mReqURL = url; @@ -377,6 +378,8 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) mReplyHeaders->release(); mReplyHeaders = NULL; } + mReplyConType.clear(); + mReplyConEncode.clear(); // *FIXME: better error handling later HttpStatus status; @@ -539,7 +542,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders); - if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS)) + if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS | PF_SCAN_CONTENT_HEADERS)) { curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback); curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, this); @@ -598,12 +601,18 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi static const char con_ran_line[] = "content-range:"; static const size_t con_ran_line_len = sizeof(con_ran_line) - 1; + + static const char con_type_line[] = "content-type:"; + static const size_t con_type_line_len = sizeof(con_type_line) - 1; + + static const char con_enc_line[] = "content-encoding:"; + static const size_t con_enc_line_len = sizeof(con_enc_line) - 1; HttpOpRequest * op(static_cast(userdata)); const size_t hdr_size(size * nmemb); const char * hdr_data(static_cast(data)); // Not null terminated - + if (hdr_size >= status_line_len && ! strncmp(status_line, hdr_data, status_line_len)) { // One of possibly several status lines. Reset what we know and start over @@ -611,24 +620,47 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi op->mReplyOffset = 0; op->mReplyLength = 0; op->mReplyFullLength = 0; + op->mReplyConType.clear(); + op->mReplyConEncode.clear(); op->mStatus = HttpStatus(); if (op->mReplyHeaders) { op->mReplyHeaders->mHeaders.clear(); } } - else if (op->mProcFlags & PF_SCAN_RANGE_HEADER) + + // Nothing in here wants a final CR/LF combination. Remove + // it as much as possible. + size_t wanted_hdr_size(hdr_size); + if (wanted_hdr_size && '\n' == hdr_data[wanted_hdr_size - 1]) + { + if (--wanted_hdr_size && '\r' == hdr_data[wanted_hdr_size - 1]) + { + --wanted_hdr_size; + } + } + + // Save header if caller wants them in the response + if (op->mProcFlags & PF_SAVE_HEADERS) + { + // Save headers in response + if (! op->mReplyHeaders) + { + op->mReplyHeaders = new HttpHeaders; + } + op->mReplyHeaders->mHeaders.push_back(std::string(hdr_data, wanted_hdr_size)); + } + + // Detect and parse 'Content-Range' headers + if (op->mProcFlags & PF_SCAN_RANGE_HEADER) { char hdr_buffer[128]; // Enough for a reasonable header - size_t frag_size((std::min)(hdr_size, sizeof(hdr_buffer) - 1)); + size_t frag_size((std::min)(wanted_hdr_size, sizeof(hdr_buffer) - 1)); memcpy(hdr_buffer, hdr_data, frag_size); hdr_buffer[frag_size] = '\0'; -#if LL_WINDOWS - if (! _strnicmp(hdr_buffer, con_ran_line, (std::min)(frag_size, con_ran_line_len))) -#else - if (! strncasecmp(hdr_buffer, con_ran_line, (std::min)(frag_size, con_ran_line_len))) -#endif // LL_WINDOWS + if (frag_size > con_ran_line_len && + ! os_strncasecmp(hdr_buffer, con_ran_line, con_ran_line_len)) { unsigned int first(0), last(0), length(0); int status; @@ -656,22 +688,43 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi } } - if (op->mProcFlags & PF_SAVE_HEADERS) + // Detect and parse 'Content-Type' and 'Content-Encoding' headers + if (op->mProcFlags & PF_SCAN_CONTENT_HEADERS) { - // Save headers in response - if (! op->mReplyHeaders) + if (wanted_hdr_size > con_type_line_len && + ! os_strncasecmp(hdr_data, con_type_line, con_type_line_len)) { - op->mReplyHeaders = new HttpHeaders; + // Found 'Content-Type:', extract single-token value + std::string rhs(hdr_data + con_type_line_len, wanted_hdr_size - con_type_line_len); + std::string::size_type begin(0), end(rhs.size()), pos; + + if ((pos = rhs.find_first_not_of(hdr_whitespace)) != std::string::npos) + { + begin = pos; + } + if ((pos = rhs.find_first_of(hdr_whitespace, begin)) != std::string::npos) + { + end = pos; + } + op->mReplyConType.assign(rhs, begin, end - begin); } - size_t wanted_size(hdr_size); - if (wanted_size && '\n' == hdr_data[wanted_size - 1]) + else if (wanted_hdr_size > con_enc_line_len && + ! os_strncasecmp(hdr_data, con_enc_line, con_enc_line_len)) { - if (--wanted_size && '\r' == hdr_data[wanted_size - 1]) + // Found 'Content-Encoding:', extract single-token value + std::string rhs(hdr_data + con_enc_line_len, wanted_hdr_size - con_enc_line_len); + std::string::size_type begin(0), end(rhs.size()), pos; + + if ((pos = rhs.find_first_not_of(hdr_whitespace)) != std::string::npos) { - --wanted_size; + begin = pos; } + if ((pos = rhs.find_first_of(hdr_whitespace, begin)) != std::string::npos) + { + end = pos; + } + op->mReplyConEncode.assign(rhs, begin, end - begin); } - op->mReplyHeaders->mHeaders.push_back(std::string(hdr_data, wanted_size)); } return hdr_size; @@ -788,15 +841,11 @@ int parse_content_range_header(char * buffer, char * tok_state(NULL), * tok(NULL); bool match(true); - if (! strtok_r(buffer, ": \t", &tok_state)) + if (! os_strtok_r(buffer, hdr_separator, &tok_state)) match = false; - if (match && (tok = strtok_r(NULL, " \t", &tok_state))) -#if LL_WINDOWS - match = 0 == _stricmp("bytes", tok); -#else - match = 0 == strcasecmp("bytes", tok); -#endif // LL_WINDOWS - if (match && ! (tok = strtok_r(NULL, " \t", &tok_state))) + if (match && (tok = os_strtok_r(NULL, hdr_whitespace, &tok_state))) + match = 0 == os_strcasecmp("bytes", tok); + if (match && ! (tok = os_strtok_r(NULL, " \t", &tok_state))) match = false; if (match) { @@ -834,15 +883,6 @@ int parse_content_range_header(char * buffer, return 1; } -#if LL_WINDOWS - -char *strtok_r(char *str, const char *delim, char ** savestate) -{ - return strtok_s(str, delim, savestate); -} - -#endif // LL_WINDOWS - void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::string & safe_line) { @@ -880,6 +920,36 @@ void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::strin } +int os_strncasecmp(const char *s1, const char *s2, size_t n) +{ +#if LL_WINDOWS + return _strnicmp(s1, s2, n); +#else + return strncasecmp(s1, s2, n); +#endif // LL_WINDOWS +} + + +int os_strcasecmp(const char *s1, const char *s2) +{ +#if LL_WINDOWS + return _stricmp(s1, s2); +#else + return strcasecmp(s1, s2); +#endif // LL_WINDOWS +} + + +char * os_strtok_r(char *str, const char *delim, char ** savestate) +{ +#if LL_WINDOWS + return strtok_s(str, delim, savestate); +#else + return strtok_r(str, delim, savestate); +#endif +} + + } // end anonymous namespace diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index a4c5fbb3c2..200b925c4e 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -30,6 +30,7 @@ #include "linden_common.h" // Modifies curl/curl.h interfaces +#include #include #include "httpcommon.h" @@ -137,6 +138,7 @@ protected: unsigned int mProcFlags; static const unsigned int PF_SCAN_RANGE_HEADER = 0x00000001U; static const unsigned int PF_SAVE_HEADERS = 0x00000002U; + static const unsigned int PF_SCAN_CONTENT_HEADERS = 0x00000004U; public: // Request data @@ -162,6 +164,8 @@ public: size_t mReplyLength; size_t mReplyFullLength; HttpHeaders * mReplyHeaders; + std::string mReplyConType; + std::string mReplyConEncode; // Policy data int mPolicyRetries; diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h index 65e403cec8..3d35050c17 100644 --- a/indra/llcorehttp/httpresponse.h +++ b/indra/llcorehttp/httpresponse.h @@ -28,6 +28,8 @@ #define _LLCORE_HTTP_RESPONSE_H_ +#include + #include "httpcommon.h" #include "_refcounted.h" @@ -130,7 +132,20 @@ public: mReplyLength = length; mReplyFullLength = full_length; } - + + /// + void getContent(std::string & con_type, std::string & con_encode) const + { + con_type = mContentType; + con_encode = mContentEncoding; + } + + void setContent(const std::string & con_type, const std::string & con_encode) + { + mContentType = con_type; + mContentEncoding = con_encode; + } + protected: // Response data here HttpStatus mStatus; @@ -139,6 +154,8 @@ protected: unsigned int mReplyFullLength; BufferArray * mBufferArray; HttpHeaders * mHeaders; + std::string mContentType; + std::string mContentEncoding; }; diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index ba7c757af4..9edf3d19ec 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -117,6 +117,15 @@ public: ensure("Special header X-LL-Special in response", found_special); } + if (! mCheckContentType.empty()) + { + ensure("Response required with content type check", response != NULL); + std::string con_type, con_enc; + response->getContent(con_type, con_enc); + ensure("Content-Type as expected (" + mCheckContentType + ")", + mCheckContentType == con_type); + } + // std::cout << "TestHandler2::onCompleted() invoked" << std::endl; } @@ -124,6 +133,7 @@ public: std::string mName; HttpHandle mExpectHandle; bool mCheckHeader; + std::string mCheckContentType; }; typedef test_group HttpRequestTestGroupType; @@ -1502,6 +1512,122 @@ void HttpRequestTestObjectType::test<14>() } } +// Test retrieval of Content-Type/Content-Encoding headers +template <> template <> +void HttpRequestTestObjectType::test<15>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + // std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET with Content-Type"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // Load and clear the string setting to preload std::string object + // for memory return tests. + handler.mCheckContentType = "application/llsd+xml"; + handler.mCheckContentType.clear(); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + handler.mCheckContentType = "application/llsd+xml"; + HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mCheckContentType.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + } // end namespace tut namespace diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 9e1847c66e..58b5bedd09 100644 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -9,7 +9,7 @@ $LicenseInfo:firstyear=2008&license=viewerlgpl$ Second Life Viewer Source Code -Copyright (C) 2010, Linden Research, Inc. +Copyright (C) 2012, 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 -- cgit v1.2.3 From d45b2e7caece787dce4be501b103432c0f06c0f2 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 12 Jul 2012 17:46:53 +0000 Subject: SH-3183 Use valgrind on the library. Using http_texture_load as the test subject, library looks clean. Did some better shutdown in the program itself and it looks better. Libcurl itself is making a lot of noise. Adapted testrunner to run valgrind as well but the memory allocation tester in the tools themselves grossly interferes with Valgrind operations. --- indra/llcorehttp/examples/http_texture_load.cpp | 3 +++ indra/llcorehttp/tests/test_llcorehttp_peer.py | 14 +++++++++++++- indra/llcorehttp/tests/testrunner.py | 5 ++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp index bcb322bd5c..998dc9240b 100644 --- a/indra/llcorehttp/examples/http_texture_load.cpp +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -269,7 +269,10 @@ int main(int argc, char** argv) << std::endl; // Clean up + hr->requestStopThread(NULL); + ms_sleep(1000); delete hr; + LLCore::HttpRequest::destroyService(); term_curl(); return 0; diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 58b5bedd09..489e8b2979 100644 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -33,6 +33,7 @@ import os import sys import time import select +import getopt from threading import Thread from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler from SocketServer import ThreadingMixIn @@ -152,6 +153,13 @@ class Server(ThreadingMixIn, HTTPServer): allow_reuse_address = False if __name__ == "__main__": + do_valgrind = False + path_search = False + options, args = getopt.getopt(sys.argv[1:], "V", ["valgrind"]) + for option, value in options: + if option == "-V" or option == "--valgrind": + do_valgrind = True + # Instantiate a Server(TestHTTPRequestHandler) on the first free port # in the specified port range. Doing this inline is better than in a # daemon thread: if it blows up here, we'll get a traceback. If it blew up @@ -159,10 +167,14 @@ if __name__ == "__main__": # subject test program anyway. httpd, port = freeport(xrange(8000, 8020), lambda port: Server(('127.0.0.1', port), TestHTTPRequestHandler)) + # Pass the selected port number to the subject test program via the # environment. We don't want to impose requirements on the test program's # command-line parsing -- and anyway, for C++ integration tests, that's # performed in TUT code rather than our own. os.environ["LL_TEST_PORT"] = str(port) debug("$LL_TEST_PORT = %s", port) - sys.exit(run(server=Thread(name="httpd", target=httpd.serve_forever), *sys.argv[1:])) + if do_valgrind: + args = ["valgrind", "--log-file=./valgrind.log"] + args + path_search = True + sys.exit(run(server=Thread(name="httpd", target=httpd.serve_forever), use_path=path_search, *args)) diff --git a/indra/llcorehttp/tests/testrunner.py b/indra/llcorehttp/tests/testrunner.py index 5b9beb359b..9a2de71142 100644 --- a/indra/llcorehttp/tests/testrunner.py +++ b/indra/llcorehttp/tests/testrunner.py @@ -168,7 +168,10 @@ def run(*args, **kwds): # executable passed as our first arg, # - [no e] child should inherit this process's environment. debug("Running %s...", " ".join(args)) - rc = os.spawnv(os.P_WAIT, args[0], args) + if kwds.get("use_path", False): + rc = os.spawnvp(os.P_WAIT, args[0], args) + else: + rc = os.spawnv(os.P_WAIT, args[0], args) debug("%s returned %s", args[0], rc) return rc -- cgit v1.2.3 From 5eb5dc6b27c57f1c3e77fc04b471614968620068 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 13 Jul 2012 18:24:49 -0400 Subject: SH-3241 validate that request headers are correct First round of integration tests. Added a request header 'reflector' to the web server to sent the client's headers back with a 'X-Reflect-' prefix. Use boost::regex to check various headers. Run a test on a simple GET and a byte-ranged GET a la texture fetch. --- indra/llcorehttp/httpoptions.cpp | 4 +- indra/llcorehttp/httpoptions.h | 2 +- indra/llcorehttp/tests/test_httprequest.hpp | 235 +++++++++++++++++++++++-- indra/llcorehttp/tests/test_llcorehttp_peer.py | 11 +- 4 files changed, 232 insertions(+), 20 deletions(-) diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp index f2771c1f29..68f7277ed3 100644 --- a/indra/llcorehttp/httpoptions.cpp +++ b/indra/llcorehttp/httpoptions.cpp @@ -46,9 +46,9 @@ HttpOptions::~HttpOptions() {} -void HttpOptions::setWantHeaders() +void HttpOptions::setWantHeaders(bool wanted) { - mWantHeaders = true; + mWantHeaders = wanted; } diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h index a0b2253c11..97e46a8cd3 100644 --- a/indra/llcorehttp/httpoptions.h +++ b/indra/llcorehttp/httpoptions.h @@ -68,7 +68,7 @@ protected: void operator=(const HttpOptions &); // Not defined public: - void setWantHeaders(); + void setWantHeaders(bool wanted); bool getWantHeaders() const { return mWantHeaders; diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 9edf3d19ec..0f9eb93996 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -36,6 +36,8 @@ #include "_httprequestqueue.h" #include +#include +#include #include "test_allocator.h" #include "llcorehttp_test.h" @@ -74,8 +76,7 @@ public: const std::string & name) : mState(state), mName(name), - mExpectHandle(LLCORE_HTTP_HANDLE_INVALID), - mCheckHeader(false) + mExpectHandle(LLCORE_HTTP_HANDLE_INVALID) {} virtual void onCompleted(HttpHandle handle, HttpResponse * response) @@ -97,24 +98,50 @@ public: { mState->mHandlerCalls++; } - if (mCheckHeader) + if (! mHeadersRequired.empty() || ! mHeadersDisallowed.empty()) { ensure("Response required with header check", response != NULL); HttpHeaders * header(response->getHeaders()); // Will not hold onto this ensure("Some quantity of headers returned", header != NULL); - bool found_special(false); - - for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin()); - header->mHeaders.end() != iter; - ++iter) + + if (! mHeadersRequired.empty()) + { + for (int i(0); i < mHeadersRequired.size(); ++i) + { + bool found = false; + for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin()); + header->mHeaders.end() != iter; + ++iter) + { + if (boost::regex_match(*iter, mHeadersRequired[i])) + { + found = true; + break; + } + } + std::ostringstream str; + str << "Required header # " << i << " found in response"; + ensure(str.str(), found); + } + } + + if (! mHeadersDisallowed.empty()) { - if (std::string::npos != (*iter).find("X-LL-Special")) + for (int i(0); i < mHeadersDisallowed.size(); ++i) { - found_special = true; - break; + for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin()); + header->mHeaders.end() != iter; + ++iter) + { + if (boost::regex_match(*iter, mHeadersDisallowed[i])) + { + std::ostringstream str; + str << "Disallowed header # " << i << " not found in response"; + ensure(str.str(), false); + } + } } } - ensure("Special header X-LL-Special in response", found_special); } if (! mCheckContentType.empty()) @@ -132,8 +159,9 @@ public: HttpRequestTestData * mState; std::string mName; HttpHandle mExpectHandle; - bool mCheckHeader; std::string mCheckContentType; + std::vector mHeadersRequired; + std::vector mHeadersDisallowed; }; typedef test_group HttpRequestTestGroupType; @@ -1268,6 +1296,10 @@ void HttpRequestTestObjectType::test<13>() { ScopedCurlInit ready; + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + std::string url_base(get_base_url()); // std::cerr << "Base: " << url_base << std::endl; @@ -1277,6 +1309,7 @@ void HttpRequestTestObjectType::test<13>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); + handler.mHeadersRequired.reserve(20); // Avoid memory leak test failure // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); @@ -1302,11 +1335,11 @@ void HttpRequestTestObjectType::test<13>() ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); opts = new HttpOptions(); - opts->setWantHeaders(); + opts->setWantHeaders(true); // Issue a GET that succeeds mStatus = HttpStatus(200); - handler.mCheckHeader = true; + handler.mHeadersRequired.push_back(boost::regex("\\W*X-LL-Special:.*", boost::regex::icase)); HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, 0U, url_base, @@ -1334,7 +1367,7 @@ void HttpRequestTestObjectType::test<13>() // Okay, request a shutdown of the servicing thread mStatus = HttpStatus(); - handler.mCheckHeader = false; + handler.mHeadersRequired.clear(); handle = req->requestStopThread(&handler); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); @@ -1628,6 +1661,176 @@ void HttpRequestTestObjectType::test<15>() } +// Test header generation on GET requests +template <> template <> +void HttpRequestTestObjectType::test<16>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + + set_test_name("Header generation for HttpRequest GET"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * options = NULL; + HttpHeaders * headers = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + + // options set + options = new HttpOptions(); + options->setWantHeaders(true); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\W*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\W*\\*/\\*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\W*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\W*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\W*X-Reflect-range:.*", boost::regex::icase)); + HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + options, + NULL, + &handler); + ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Do a texture-style fetch + headers = new HttpHeaders; + headers->mHeaders.push_back("Accept: image/x-j2c"); + + mStatus = HttpStatus(200); + handler.mHeadersRequired.clear(); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\W*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\W*\\image/x-j2c", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\W*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.clear(); + handler.mHeadersDisallowed.push_back(boost::regex("\\W*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\W*X-Reflect-pragma:.*", boost::regex::icase)); + handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + 0, + 47, + options, + headers, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 2); + + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 3) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 3); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options & headers + if (options) + { + options->release(); + } + options = NULL; + + if (headers) + { + headers->release(); + } + headers = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + } + catch (...) + { + stop_thread(req); + if (options) + { + options->release(); + options = NULL; + } + if (headers) + { + headers->release(); + headers = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + } // end namespace tut namespace diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 489e8b2979..c527ce6ce0 100644 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -104,7 +104,7 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): def answer(self, data, withdata=True): debug("%s.answer(%s): self.path = %r", self.__class__.__name__, data, self.path) - if self.path.find("/sleep/") != -1: + if "/sleep/" in self.path: time.sleep(30) if "fail" not in self.path: @@ -114,6 +114,8 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): response = llsd.format_xml(data) debug("success: %s", response) self.send_response(200) + if "/reflect/" in self.path: + self.reflect_headers() self.send_header("Content-type", "application/llsd+xml") self.send_header("Content-Length", str(len(response))) self.send_header("X-LL-Special", "Mememememe"); @@ -133,6 +135,13 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): "without providing a reason" % status))[1]) debug("fail requested: %s: %r", status, reason) self.send_error(status, reason) + if "/reflect/" in self.path: + self.reflect_headers() + self.end_headers() + + def reflect_headers(self): + for name in self.headers.keys(): + self.send_header("X-Reflect-" + name, self.headers[name]) if not VERBOSE: # When VERBOSE is set, skip both these overrides because they exist to -- cgit v1.2.3 From d238341afaecedfe227141126c4c35dcde4a0671 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 16 Jul 2012 11:53:04 -0400 Subject: SH-3189 Remove/improve naive data structures When releasing HTTP waiters, avoid unnecessary sort activity. For Content-Type in responses, let libcurl do the work and removed my parsing of headers. Drop Content-Encoding as libcurl will deal with that. If anyone is interested, they can parse. --- indra/llcorehttp/_httplibcurl.cpp | 18 ++++++++++- indra/llcorehttp/_httpoprequest.cpp | 48 ++--------------------------- indra/llcorehttp/_httpoprequest.h | 2 -- indra/llcorehttp/httpcommon.cpp | 3 +- indra/llcorehttp/httpcommon.h | 5 ++- indra/llcorehttp/httpresponse.h | 9 ++---- indra/llcorehttp/tests/test_httprequest.hpp | 3 +- indra/newview/lltexturefetch.cpp | 22 ++++++++++--- 8 files changed, 48 insertions(+), 62 deletions(-) diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 3c69ae1c96..e031efbc91 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -280,7 +280,23 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode int http_status(HTTP_OK); curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_status); - op->mStatus = LLCore::HttpStatus(http_status); + if (http_status >= 100 && http_status <= 999) + { + char * cont_type(NULL); + curl_easy_getinfo(handle, CURLINFO_CONTENT_TYPE, &cont_type); + if (cont_type) + { + op->mReplyConType = cont_type; + } + op->mStatus = HttpStatus(http_status); + } + else + { + LL_WARNS("CoreHttp") << "Invalid HTTP response code (" + << http_status << ") received from server." + << LL_ENDL; + op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INVALID_HTTP_STATUS); + } } // Detach from multi and recycle handle diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 1854d7ada4..1a770f67be 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -228,7 +228,7 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) // Got an explicit offset/length in response response->setRange(mReplyOffset, mReplyLength, mReplyFullLength); } - response->setContent(mReplyConType, mReplyConEncode); + response->setContentType(mReplyConType); mUserHandler->onCompleted(static_cast(this), response); @@ -316,7 +316,7 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id, HttpOptions * options, HttpHeaders * headers) { - mProcFlags = PF_SCAN_CONTENT_HEADERS; // Always scan for content headers + mProcFlags = 0U; mReqPolicy = policy_id; mReqPriority = priority; mReqURL = url; @@ -379,7 +379,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) mReplyHeaders = NULL; } mReplyConType.clear(); - mReplyConEncode.clear(); // *FIXME: better error handling later HttpStatus status; @@ -542,7 +541,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders); - if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS | PF_SCAN_CONTENT_HEADERS)) + if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS)) { curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback); curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, this); @@ -620,8 +619,6 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi op->mReplyOffset = 0; op->mReplyLength = 0; op->mReplyFullLength = 0; - op->mReplyConType.clear(); - op->mReplyConEncode.clear(); op->mStatus = HttpStatus(); if (op->mReplyHeaders) { @@ -688,45 +685,6 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi } } - // Detect and parse 'Content-Type' and 'Content-Encoding' headers - if (op->mProcFlags & PF_SCAN_CONTENT_HEADERS) - { - if (wanted_hdr_size > con_type_line_len && - ! os_strncasecmp(hdr_data, con_type_line, con_type_line_len)) - { - // Found 'Content-Type:', extract single-token value - std::string rhs(hdr_data + con_type_line_len, wanted_hdr_size - con_type_line_len); - std::string::size_type begin(0), end(rhs.size()), pos; - - if ((pos = rhs.find_first_not_of(hdr_whitespace)) != std::string::npos) - { - begin = pos; - } - if ((pos = rhs.find_first_of(hdr_whitespace, begin)) != std::string::npos) - { - end = pos; - } - op->mReplyConType.assign(rhs, begin, end - begin); - } - else if (wanted_hdr_size > con_enc_line_len && - ! os_strncasecmp(hdr_data, con_enc_line, con_enc_line_len)) - { - // Found 'Content-Encoding:', extract single-token value - std::string rhs(hdr_data + con_enc_line_len, wanted_hdr_size - con_enc_line_len); - std::string::size_type begin(0), end(rhs.size()), pos; - - if ((pos = rhs.find_first_not_of(hdr_whitespace)) != std::string::npos) - { - begin = pos; - } - if ((pos = rhs.find_first_of(hdr_whitespace, begin)) != std::string::npos) - { - end = pos; - } - op->mReplyConEncode.assign(rhs, begin, end - begin); - } - } - return hdr_size; } diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 200b925c4e..36dc5dc876 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -138,7 +138,6 @@ protected: unsigned int mProcFlags; static const unsigned int PF_SCAN_RANGE_HEADER = 0x00000001U; static const unsigned int PF_SAVE_HEADERS = 0x00000002U; - static const unsigned int PF_SCAN_CONTENT_HEADERS = 0x00000004U; public: // Request data @@ -165,7 +164,6 @@ public: size_t mReplyFullLength; HttpHeaders * mReplyHeaders; std::string mReplyConType; - std::string mReplyConEncode; // Policy data int mPolicyRetries; diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp index 1b18976359..f2fcbf77a3 100644 --- a/indra/llcorehttp/httpcommon.cpp +++ b/indra/llcorehttp/httpcommon.cpp @@ -69,7 +69,8 @@ std::string HttpStatus::toString() const "Request handle not found", "Invalid datatype for argument or option", "Option has not been explicitly set", - "Option is not dynamic and must be set early" + "Option is not dynamic and must be set early", + "Invalid HTTP status code received from server" }; static const int llcore_errors_count(sizeof(llcore_errors) / sizeof(llcore_errors[0])); diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index 576a113e54..dd5798edf9 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -149,7 +149,10 @@ enum HttpError HE_OPT_NOT_SET = 7, // Option not dynamic, must be set during init phase - HE_OPT_NOT_DYNAMIC = 8 + HE_OPT_NOT_DYNAMIC = 8, + + // Invalid HTTP status code returned by server + HE_INVALID_HTTP_STATUS = 9 }; // end enum HttpError diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h index 3d35050c17..4a481db6ac 100644 --- a/indra/llcorehttp/httpresponse.h +++ b/indra/llcorehttp/httpresponse.h @@ -134,16 +134,14 @@ public: } /// - void getContent(std::string & con_type, std::string & con_encode) const + const std::string & getContentType() const { - con_type = mContentType; - con_encode = mContentEncoding; + return mContentType; } - void setContent(const std::string & con_type, const std::string & con_encode) + void setContentType(const std::string & con_type) { mContentType = con_type; - mContentEncoding = con_encode; } protected: @@ -155,7 +153,6 @@ protected: BufferArray * mBufferArray; HttpHeaders * mHeaders; std::string mContentType; - std::string mContentEncoding; }; diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 0f9eb93996..1acf4f9d4b 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -147,8 +147,7 @@ public: if (! mCheckContentType.empty()) { ensure("Response required with content type check", response != NULL); - std::string con_type, con_enc; - response->getContent(con_type, con_enc); + std::string con_type(response->getContentType()); ensure("Content-Type as expected (" + mCheckContentType + ")", mCheckContentType == con_type); } diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 8314031f14..225ea46558 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -3342,6 +3342,17 @@ void LLTextureFetch::removeHttpWaiter(const LLUUID & tid) mNetworkQueueMutex.unlock(); // -Mfnq } +// Release as many requests as permitted from the WAIT_HTTP_RESOURCE2 +// state to the SEND_HTTP_REQ state based on their current priority. +// +// This data structures and code associated with this looks a bit +// indirect and naive but it's done in the name of safety. An +// ordered container may become invalid from time to time due to +// priority changes caused by actions in other threads. State itself +// could also suffer the same fate with canceled operations. Even +// done this way, I'm not fully trusting we're truly safe. This +// module is due for a major refactoring and we'll deal with it then. +// // Threads: Ttf // Locks: -Mw (must not hold any worker when called) void LLTextureFetch::releaseHttpWaiters() @@ -3384,12 +3395,15 @@ void LLTextureFetch::releaseHttpWaiters() tids2.push_back(worker); } } - - // Sort into priority order - LLTextureFetchWorker::Compare compare; - std::sort(tids2.begin(), tids2.end(), compare); tids.clear(); + // Sort into priority order, if necessary and only as much as needed + if (tids2.size() > mHttpSemaphore) + { + LLTextureFetchWorker::Compare compare; + std::partial_sort(tids2.begin(), tids2.begin() + mHttpSemaphore, tids2.end(), compare); + } + // Release workers up to the high water mark. Since we aren't // holding any locks at this point, we can be in competition // with other callers. Do defensive things like getting -- cgit v1.2.3 From 8d3e5f3959b071226c4a5c2c68d9fe8664ae1ffd Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 16 Jul 2012 17:35:44 -0400 Subject: SH-3241 Validate header correctness, SH-3243 more unit tests Define expectations for headers for GET, POST, PUT requests. Document those in the interface, test those with integration tests. Verify that header overrides work as expected. --- indra/llcorehttp/_httpoprequest.cpp | 4 + indra/llcorehttp/httprequest.h | 175 +++-- indra/llcorehttp/tests/test_httprequest.hpp | 844 ++++++++++++++++++++++++- indra/llcorehttp/tests/test_llcorehttp_peer.py | 1 + 4 files changed, 960 insertions(+), 64 deletions(-) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 1a770f67be..a18a164f0d 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -468,6 +468,8 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, static_cast(NULL)); curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDSIZE, data_size); mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); } break; @@ -482,6 +484,8 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size); curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL); mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); } break; diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 9dd53f4483..ab2f302d34 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -214,14 +214,46 @@ public: /// request in other requests (like cancellation) and will be an /// argument when any HttpHandler object is invoked. /// + /// Headers supplied by default: + /// - Connection: keep-alive + /// - Accept: */* + /// - Accept-Encoding: deflate, gzip + /// - Keep-alive: 300 + /// - Host: + /// + /// Some headers excluded by default: + /// - Pragma: + /// - Cache-control: + /// - Range: + /// - Transfer-Encoding: + /// - Referer: + /// /// @param policy_id Default or user-defined policy class under /// which this request is to be serviced. /// @param priority Standard priority scheme inherited from /// Indra code base (U32-type scheme). - /// @param url - /// @param options (optional) - /// @param headers (optional) - /// @param handler (optional) + /// @param url URL with any encoded query parameters to + /// be accessed. + /// @param options Optional instance of an HttpOptions object + /// to provide additional controls over the request + /// function for this request only. Any such + /// object then becomes shared-read across threads + /// and no code should modify the HttpOptions + /// instance. + /// @param headers Optional instance of an HttpHeaders object + /// to provide additional and/or overridden + /// headers for the request. As with options, + /// the instance becomes shared-read across threads + /// and no code should modify the HttpHeaders + /// instance. + /// @param handler Optional pointer to an HttpHandler instance + /// whose onCompleted() method will be invoked + /// during calls to update(). This is a non- + /// reference-counted object which would be a + /// problem for shutdown and other edge cases but + /// the pointer is only dereferenced during + /// calls to update(). + /// /// @return The handle of the request if successfully /// queued or LLCORE_HTTP_HANDLE_INVALID if the /// request could not be queued. In the latter @@ -244,20 +276,29 @@ public: /// request in other requests (like cancellation) and will be an /// argument when any HttpHandler object is invoked. /// - /// @param policy_id Default or user-defined policy class under - /// which this request is to be serviced. - /// @param priority Standard priority scheme inherited from - /// Indra code base (U32-type scheme). - /// @param url - /// @param offset - /// @param len - /// @param options (optional) - /// @param headers (optional) - /// @param handler (optional) - /// @return The handle of the request if successfully - /// queued or LLCORE_HTTP_HANDLE_INVALID if the - /// request could not be queued. In the latter - /// case, @see getStatus() will return more info. + /// Headers supplied by default: + /// - Connection: keep-alive + /// - Accept: */* + /// - Accept-Encoding: deflate, gzip + /// - Keep-alive: 300 + /// - Host: + /// - Range: (will be omitted if offset == 0 and len == 0) + /// + /// Some headers excluded by default: + /// - Pragma: + /// - Cache-control: + /// - Transfer-Encoding: + /// - Referer: + /// + /// @param policy_id @see requestGet() + /// @param priority " + /// @param url " + /// @param offset Offset of first byte into resource to be returned. + /// @param len Count of bytes to be returned + /// @param options @see requestGet() + /// @param headers " + /// @param handler " + /// @return " /// HttpHandle requestGetByteRange(policy_t policy_id, priority_t priority, @@ -269,22 +310,37 @@ public: HttpHandler * handler); - /// - /// @param policy_id Default or user-defined policy class under - /// which this request is to be serviced. - /// @param priority Standard priority scheme inherited from - /// Indra code base. - /// @param url + /// Queue a full HTTP POST. Query arguments and body may + /// be provided. Caller is responsible for escaping and + /// encoding and communicating the content types. + /// + /// Headers supplied by default: + /// - Connection: keep-alive + /// - Accept: */* + /// - Accept-Encoding: deflate, gzip + /// - Keep-Alive: 300 + /// - Host: + /// - Content-Length: + /// - Content-Type: application/x-www-form-urlencoded + /// + /// Some headers excluded by default: + /// - Pragma: + /// - Cache-Control: + /// - Transfer-Encoding: ... chunked ... + /// - Referer: + /// - Content-Encoding: + /// - Expect: + /// + /// @param policy_id @see requestGet() + /// @param priority " + /// @param url " /// @param body Byte stream to be sent as the body. No /// further encoding or escaping will be done /// to the content. - /// @param options (optional) - /// @param headers (optional) - /// @param handler (optional) - /// @return The handle of the request if successfully - /// queued or LLCORE_HTTP_HANDLE_INVALID if the - /// request could not be queued. In the latter - /// case, @see getStatus() will return more info. + /// @param options @see requestGet()K(optional) + /// @param headers " + /// @param handler " + /// @return " /// HttpHandle requestPost(policy_t policy_id, priority_t priority, @@ -295,22 +351,37 @@ public: HttpHandler * handler); - /// - /// @param policy_id Default or user-defined policy class under - /// which this request is to be serviced. - /// @param priority Standard priority scheme inherited from - /// Indra code base. - /// @param url + /// Queue a full HTTP PUT. Query arguments and body may + /// be provided. Caller is responsible for escaping and + /// encoding and communicating the content types. + /// + /// Headers supplied by default: + /// - Connection: keep-alive + /// - Accept: */* + /// - Accept-Encoding: deflate, gzip + /// - Keep-Alive: 300 + /// - Host: + /// - Content-Length: + /// + /// Some headers excluded by default: + /// - Pragma: + /// - Cache-Control: + /// - Transfer-Encoding: ... chunked ... + /// - Referer: + /// - Content-Encoding: + /// - Expect: + /// - Content-Type: + /// + /// @param policy_id @see requestGet() + /// @param priority " + /// @param url " /// @param body Byte stream to be sent as the body. No /// further encoding or escaping will be done /// to the content. - /// @param options (optional) - /// @param headers (optional) - /// @param handler (optional) - /// @return The handle of the request if successfully - /// queued or LLCORE_HTTP_HANDLE_INVALID if the - /// request could not be queued. In the latter - /// case, @see getStatus() will return more info. + /// @param options @see requestGet()K(optional) + /// @param headers " + /// @param handler " + /// @return " /// HttpHandle requestPut(policy_t policy_id, priority_t priority, @@ -326,11 +397,8 @@ public: /// immediately processes it and returns the request to the reply /// queue. /// - /// @param handler (optional) - /// @return The handle of the request if successfully - /// queued or LLCORE_HTTP_HANDLE_INVALID if the - /// request could not be queued. In the latter - /// case, @see getStatus() will return more info. + /// @param handler @see requestGet() + /// @return " /// HttpHandle requestNoOp(HttpHandler * handler); @@ -345,7 +413,8 @@ public: /// spend in the call. As hinted at above, this /// is partly a function of application code so it's /// a soft limit. A '0' value will run without - /// time limit. + /// time limit until everything queued has been + /// delivered. /// /// @return Standard status code. HttpStatus update(long usecs); @@ -365,10 +434,8 @@ public: /// @param request Handle of previously-issued request to /// be changed. /// @param priority New priority value. - /// @param handler (optional) - /// @return The handle of the request if successfully - /// queued or LLCORE_HTTP_HANDLE_INVALID if the - /// request could not be queued. + /// @param handler @see requestGet() + /// @return " /// HttpHandle requestSetPriority(HttpHandle request, priority_t priority, HttpHandler * handler); diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 1acf4f9d4b..ec144693c3 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -1705,11 +1705,18 @@ void HttpRequestTestObjectType::test<16>() // Issue a GET that *can* connect mStatus = HttpStatus(200); - handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\W*keep-alive", boost::regex::icase)); - handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\W*\\*/\\*", boost::regex::icase)); - handler.mHeadersDisallowed.push_back(boost::regex("\\W*X-Reflect-cache-control:.*", boost::regex::icase)); - handler.mHeadersDisallowed.push_back(boost::regex("\\W*X-Reflect-pragma:.*", boost::regex::icase)); - handler.mHeadersDisallowed.push_back(boost::regex("\\W*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, 0U, url_base + "reflect/", @@ -1735,12 +1742,19 @@ void HttpRequestTestObjectType::test<16>() mStatus = HttpStatus(200); handler.mHeadersRequired.clear(); - handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\W*keep-alive", boost::regex::icase)); - handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\W*\\image/x-j2c", boost::regex::icase)); - handler.mHeadersDisallowed.push_back(boost::regex("\\W*X-Reflect-range:.*", boost::regex::icase)); handler.mHeadersDisallowed.clear(); - handler.mHeadersDisallowed.push_back(boost::regex("\\W*X-Reflect-cache-control:.*", boost::regex::icase)); - handler.mHeadersDisallowed.push_back(boost::regex("\\W*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*image/x-j2c", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("\\W*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, 0U, url_base + "reflect/", @@ -1830,6 +1844,816 @@ void HttpRequestTestObjectType::test<16>() } +// Test header generation on POST requests +template <> template <> +void HttpRequestTestObjectType::test<17>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + + set_test_name("Header generation for HttpRequest POST"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * options = NULL; + HttpHeaders * headers = NULL; + BufferArray * ba = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + + // options set + options = new HttpOptions(); + options->setWantHeaders(true); + + // And a buffer array + const char * msg("It was the best of times, it was the worst of times."); + ba = new BufferArray; + ba->append(msg, strlen(msg)); + + // Issue a default POST + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/x-www-form-urlencoded", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase)); + HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + ba, + options, + NULL, + &handler); + ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + ba->release(); + ba = NULL; + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options & headers + if (options) + { + options->release(); + } + options = NULL; + + if (headers) + { + headers->release(); + } + headers = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + } + catch (...) + { + stop_thread(req); + if (ba) + { + ba->release(); + ba = NULL; + } + if (options) + { + options->release(); + options = NULL; + } + if (headers) + { + headers->release(); + headers = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +// Test header generation on PUT requests +template <> template <> +void HttpRequestTestObjectType::test<18>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + + set_test_name("Header generation for HttpRequest PUT"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * options = NULL; + HttpHeaders * headers = NULL; + BufferArray * ba = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + + // options set + options = new HttpOptions(); + options->setWantHeaders(true); + + // And a buffer array + const char * msg("It was the best of times, it was the worst of times."); + ba = new BufferArray; + ba->append(msg, strlen(msg)); + + // Issue a default PUT + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:.*", boost::regex::icase)); + HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + ba, + options, + NULL, + &handler); + ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + ba->release(); + ba = NULL; + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options & headers + if (options) + { + options->release(); + } + options = NULL; + + if (headers) + { + headers->release(); + } + headers = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + } + catch (...) + { + stop_thread(req); + if (ba) + { + ba->release(); + ba = NULL; + } + if (options) + { + options->release(); + options = NULL; + } + if (headers) + { + headers->release(); + headers = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +// Test header generation on GET requests with overrides +template <> template <> +void HttpRequestTestObjectType::test<19>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + + set_test_name("Header generation for HttpRequest GET with header overrides"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * options = NULL; + HttpHeaders * headers = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + + // options set + options = new HttpOptions(); + options->setWantHeaders(true); + + // headers + headers = new HttpHeaders; + headers->mHeaders.push_back("Keep-Alive: 120"); + headers->mHeaders.push_back("Accept-encoding: deflate"); + headers->mHeaders.push_back("Accept: text/plain"); + + // Issue a GET with modified headers + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*text/plain", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*deflate", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*120", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-keep-alive:\\s*300", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); + HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + options, + headers, + &handler); + ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options & headers + if (options) + { + options->release(); + } + options = NULL; + + if (headers) + { + headers->release(); + } + headers = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + } + catch (...) + { + stop_thread(req); + if (options) + { + options->release(); + options = NULL; + } + if (headers) + { + headers->release(); + headers = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +// Test header generation on POST requests with overrides +template <> template <> +void HttpRequestTestObjectType::test<20>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + + set_test_name("Header generation for HttpRequest POST with header overrides"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * options = NULL; + HttpHeaders * headers = NULL; + BufferArray * ba = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + + // options set + options = new HttpOptions(); + options->setWantHeaders(true); + + // headers + headers = new HttpHeaders(); + headers->mHeaders.push_back("keep-Alive: 120"); + headers->mHeaders.push_back("Accept: text/html"); + headers->mHeaders.push_back("content-type: application/llsd+xml"); + headers->mHeaders.push_back("cache-control: no-store"); + + // And a buffer array + const char * msg("It was the best of times, it was the worst of times."); + ba = new BufferArray; + ba->append(msg, strlen(msg)); + + // Issue a default POST + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*text/html", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*120", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/llsd\\+xml", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("\\s*X-Reflect-cache-control:\\s*no-store", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*application/x-www-form-urlencoded", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-keep-alive:\\s*300", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase)); + HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + ba, + options, + headers, + &handler); + ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + ba->release(); + ba = NULL; + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options & headers + if (options) + { + options->release(); + } + options = NULL; + + if (headers) + { + headers->release(); + } + headers = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + } + catch (...) + { + stop_thread(req); + if (ba) + { + ba->release(); + ba = NULL; + } + if (options) + { + options->release(); + options = NULL; + } + if (headers) + { + headers->release(); + headers = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +// Test header generation on PUT requests with overrides +template <> template <> +void HttpRequestTestObjectType::test<21>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + + set_test_name("Header generation for HttpRequest PUT with header overrides"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * options = NULL; + HttpHeaders * headers = NULL; + BufferArray * ba = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + + // options set + options = new HttpOptions(); + options->setWantHeaders(true); + + // headers + headers = new HttpHeaders; + headers->mHeaders.push_back("content-type: text/plain"); + headers->mHeaders.push_back("content-type: text/html"); + headers->mHeaders.push_back("content-type: application/llsd+xml"); + + // And a buffer array + const char * msg("It was the best of times, it was the worst of times."); + ba = new BufferArray; + ba->append(msg, strlen(msg)); + + // Issue a default PUT + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/llsd\\+xml", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*text/plain", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*text/html", boost::regex::icase)); + HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + ba, + options, + headers, + &handler); + ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + ba->release(); + ba = NULL; + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options & headers + if (options) + { + options->release(); + } + options = NULL; + + if (headers) + { + headers->release(); + } + headers = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + } + catch (...) + { + stop_thread(req); + if (ba) + { + ba->release(); + ba = NULL; + } + if (options) + { + options->release(); + options = NULL; + } + if (headers) + { + headers->release(); + headers = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + } // end namespace tut namespace diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index c527ce6ce0..75a3c39ef2 100644 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -141,6 +141,7 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): def reflect_headers(self): for name in self.headers.keys(): + # print "Header: %s: %s" % (name, self.headers[name]) self.send_header("X-Reflect-" + name, self.headers[name]) if not VERBOSE: -- cgit v1.2.3 From c9f5bc7cae793b6965ceb9490243b4c52017c254 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 17 Jul 2012 11:24:52 -0400 Subject: SH-3189 Improve naive data structures Move releaseHttpWaiters() to commonUpdate from doWork. More appropriate home for it. Have deleteOK() defer deletion of anything in WAIT_HTTP_RESOURCE2 state to keep pointers valid for the releaseHttpWaiters() method. It will then transition canceled operations to SEND_HTTP_REQ where they can be deleted. --- indra/newview/lltexturefetch.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 225ea46558..51d57ccf68 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1064,9 +1064,6 @@ bool LLTextureFetchWorker::doWork(S32 param) static const LLCore::HttpStatus http_service_unavail(HTTP_SERVICE_UNAVAILABLE); // 503 static const LLCore::HttpStatus http_not_sat(HTTP_REQUESTED_RANGE_NOT_SATISFIABLE); // 416; - // Release waiters while we aren't holding the Mw lock. - mFetcher->releaseHttpWaiters(); - LLMutexLock lock(&mWorkMutex); // +Mw if ((mFetcher->isQuitting() || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) @@ -1927,6 +1924,14 @@ bool LLTextureFetchWorker::deleteOK() // and will dereference it to do notification. delete_ok = false; } + + if (WAIT_HTTP_RESOURCE2 == mState) + { + // Don't delete the worker out from under the + // releaseHttpWaiters() method. Keep the pointers + // valid, clean up after transition. + delete_ok = false; + } // Allow any pending reads or writes to complete if (mCacheReadHandle != LLTextureCache::nullHandle()) @@ -2737,6 +2742,9 @@ bool LLTextureFetch::runCondition() // Threads: Ttf void LLTextureFetch::commonUpdate() { + // Release waiters + releaseHttpWaiters(); + // Run a cross-thread command, if any. cmdDoWork(); -- cgit v1.2.3 From 3f032e33f2b2f929b229cf4d358b9c9d297856b8 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 19 Jul 2012 13:41:18 -0400 Subject: SH-3280 Better init/shutdown functionality for llcorehttp by llappviewer Isolate llcorehttp initialization into a utility class (LLAppCoreHttp) that provides glue between app and library (sets up policies, handles notifications). Introduce 'TextureFetchConcurrency' debug setting to provide some field control when absolutely necessary. --- indra/newview/CMakeLists.txt | 2 + indra/newview/app_settings/settings.xml | 11 ++ indra/newview/llappcorehttp.cpp | 186 +++++++++++++++++++++++++++++++ indra/newview/llappcorehttp.h | 86 +++++++++++++++ indra/newview/llappviewer.cpp | 189 +------------------------------- indra/newview/llappviewer.h | 7 ++ indra/newview/lltexturefetch.cpp | 9 +- indra/newview/lltexturefetch.h | 12 +- 8 files changed, 310 insertions(+), 192 deletions(-) create mode 100644 indra/newview/llappcorehttp.cpp create mode 100644 indra/newview/llappcorehttp.h diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index ab81cadc96..94286f0ff4 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -91,6 +91,7 @@ set(viewer_SOURCE_FILES llagentwearables.cpp llagentwearablesfetch.cpp llanimstatelabels.cpp + llappcorehttp.cpp llappearancemgr.cpp llappviewer.cpp llappviewerlistener.cpp @@ -648,6 +649,7 @@ set(viewer_HEADER_FILES llagentwearables.h llagentwearablesfetch.h llanimstatelabels.h + llappcorehttp.h llappearance.h llappearancemgr.h llappviewer.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index c9b4de0140..fc32e65410 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -10743,6 +10743,17 @@ Value 0 + TextureFetchConcurrency + + Comment + Maximum number of HTTP connections used for texture fetches + Persist + 1 + Type + U32 + Value + 0 + TextureFetchDebuggerEnabled Comment diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp new file mode 100644 index 0000000000..e89c8d5ac2 --- /dev/null +++ b/indra/newview/llappcorehttp.cpp @@ -0,0 +1,186 @@ +/** + * @file llappcorehttp.cpp + * @brief + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llappcorehttp.h" + +#include "llviewercontrol.h" + + +const F64 LLAppCoreHttp::MAX_THREAD_WAIT_TIME(10.0); + +LLAppCoreHttp::LLAppCoreHttp() + : mRequest(NULL), + mStopHandle(LLCORE_HTTP_HANDLE_INVALID), + mStopRequested(0.0), + mStopped(false), + mPolicyDefault(-1) +{} + + +LLAppCoreHttp::~LLAppCoreHttp() +{ + delete mRequest; + mRequest = NULL; +} + + +void LLAppCoreHttp::init() +{ + LLCore::HttpStatus status = LLCore::HttpRequest::createService(); + if (! status) + { + LL_ERRS("Init") << "Failed to initialize HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } + + // Point to our certs or SSH/https: will fail on connect + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE, + gDirUtilp->getCAFile()); + if (! status) + { + LL_ERRS("Init") << "Failed to set CA File for HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } + + // Establish HTTP Proxy. "LLProxy" is a special string which directs + // the code to use LLProxy::applyProxySettings() to establish any + // HTTP or SOCKS proxy for http operations. + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_LLPROXY, 1); + if (! status) + { + LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } + + // Tracing levels for library & libcurl (note that 2 & 3 are beyond spammy): + // 0 - None + // 1 - Basic start, stop simple transitions + // 2 - libcurl CURLOPT_VERBOSE mode with brief lines + // 3 - with partial data content + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 0); + + // Setup default policy and constrain if directed to + mPolicyDefault = LLCore::HttpRequest::DEFAULT_POLICY_ID; + static const std::string texture_concur("TextureFetchConcurrency"); + if (gSavedSettings.controlExists(texture_concur)) + { + U32 concur(llmin(gSavedSettings.getU32(texture_concur), U32(12))); + + if (concur > 0) + { + LLCore::HttpStatus status; + status = LLCore::HttpRequest::setPolicyClassOption(mPolicyDefault, + LLCore::HttpRequest::CP_CONNECTION_LIMIT, + concur); + if (! status) + { + LL_WARNS("Init") << "Unable to set texture fetch concurrency. Reason: " + << status.toString() + << LL_ENDL; + } + else + { + LL_INFOS("Init") << "Application settings overriding default texture fetch concurrency. New value: " + << concur + << LL_ENDL; + } + } + } + + // Kick the thread + status = LLCore::HttpRequest::startThread(); + if (! status) + { + LL_ERRS("Init") << "Failed to start HTTP servicing thread. Reason: " + << status.toString() + << LL_ENDL; + } + + mRequest = new LLCore::HttpRequest; +} + + +void LLAppCoreHttp::requestStop() +{ + llassert_always(mRequest); + + mStopHandle = mRequest->requestStopThread(this); + if (LLCORE_HTTP_HANDLE_INVALID != mStopHandle) + { + mStopRequested = LLTimer::getTotalSeconds(); + } +} + + +void LLAppCoreHttp::cleanup() +{ + if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle) + { + // Should have been started already... + requestStop(); + } + + if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle) + { + LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services without thread shutdown" + << LL_ENDL; + } + else + { + while (! mStopped && LLTimer::getTotalSeconds() < (mStopRequested + MAX_THREAD_WAIT_TIME)) + { + mRequest->update(200000); + ms_sleep(50); + } + if (! mStopped) + { + LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services with thread shutdown incomplete" + << LL_ENDL; + } + } + + delete mRequest; + mRequest = NULL; + + LLCore::HttpStatus status = LLCore::HttpRequest::destroyService(); + if (! status) + { + LL_WARNS("Cleanup") << "Failed to shutdown HTTP services, continuing. Reason: " + << status.toString() + << LL_ENDL; + } +} + + +void LLAppCoreHttp::onCompleted(LLCore::HttpHandle, LLCore::HttpResponse *) +{ + mStopped = true; +} diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h new file mode 100644 index 0000000000..241d73ad52 --- /dev/null +++ b/indra/newview/llappcorehttp.h @@ -0,0 +1,86 @@ +/** + * @file llappcorehttp.h + * @brief Singleton initialization/shutdown class for llcorehttp library + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LL_APP_COREHTTP_H_ +#define _LL_APP_COREHTTP_H_ + + +#include "httprequest.h" +#include "httphandler.h" +#include "httpresponse.h" + + +// This class manages the lifecyle of the core http library. +// Slightly different style than traditional code but reflects +// the use of handler classes and light-weight interface +// object instances of the new libraries. To be used +// as a singleton and static construction is fine. +class LLAppCoreHttp : public LLCore::HttpHandler +{ +public: + LLAppCoreHttp(); + ~LLAppCoreHttp(); + + // Initialize the LLCore::HTTP library creating service classes + // and starting the servicing thread. Caller is expected to do + // other initializations (SSL mutex, thread hash function) appropriate + // for the application. + void init(); + + // Request that the servicing thread stop servicing requests, + // release resource references and stop. Request is asynchronous + // and @see cleanup() will perform a limited wait loop for this + // request to stop the thread. + void requestStop(); + + // Terminate LLCore::HTTP library services. Caller is expected + // to have made a best-effort to shutdown the servicing thread + // by issuing a requestThreadStop() and waiting for completion + // notification that the stop has completed. + void cleanup(); + + // Notification when the stop request is complete. + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + + // Retrieve the policy class for default operations. + int getPolicyDefault() const + { + return mPolicyDefault; + } + +private: + static const F64 MAX_THREAD_WAIT_TIME; + +private: + LLCore::HttpRequest * mRequest; + LLCore::HttpHandle mStopHandle; + F64 mStopRequested; + bool mStopped; + int mPolicyDefault; +}; + + +#endif // _LL_APP_COREHTTP_H_ diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 0549a972e1..f8ee1a477f 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -215,10 +215,6 @@ #include "llmachineid.h" #include "llmainlooprepeater.h" -// LLCore::HTTP -#include "httpcommon.h" -#include "httprequest.h" -#include "httphandler.h" // *FIX: These extern globals should be cleaned up. // The globals either represent state/config/resource-storage of either @@ -334,53 +330,6 @@ static std::string gLaunchFileOnQuit; // Used on Win32 for other apps to identify our window (eg, win_setup) const char* const VIEWER_WINDOW_CLASSNAME = "Second Life"; -namespace -{ - -// This class manages the lifecyle of the core http library. -// Slightly different style than traditional code but reflects -// the use of handler classes and light-weight interface -// object instances of the new libraries. To be used -// as a singleton and static construction is fine. -class CoreHttp : public LLCore::HttpHandler -{ -public: - CoreHttp(); - ~CoreHttp(); - - // Initialize the LLCore::HTTP library creating service classes - // and starting the servicing thread. Caller is expected to do - // other initializations (SSL mutex, thread hash function) appropriate - // for the application. - void init(); - - // Request that the servicing thread stop servicing requests, - // release resource references and stop. - void requestStop(); - - // Terminate LLCore::HTTP library services. Caller is expected - // to have made a best-effort to shutdown the servicing thread - // by issuing a requestThreadStop() and waiting for completion - // notification that the stop has completed. - void cleanup(); - - // Notification when the stop request is complete. - virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); - -private: - static const F64 MAX_THREAD_WAIT_TIME; - -private: - LLCore::HttpRequest * mRequest; - LLCore::HttpHandle mStopHandle; - F64 mStopRequested; - bool mStopped; -}; - -CoreHttp coreHttpLib; - -} // end anonymous namespace - //-- LLDeferredTaskList ------------------------------------------------------ /** @@ -775,8 +724,9 @@ bool LLAppViewer::init() LLViewerStatsRecorder::initClass(); #endif - // Initialize the non-LLCurl libcurl library - coreHttpLib.init(); + // Initialize the non-LLCurl libcurl library. Should be called + // before consumers (LLTextureFetch). + mAppCoreHttp.init(); // *NOTE:Mani - LLCurl::initClass is not thread safe. // Called before threads are created. @@ -1915,7 +1865,7 @@ bool LLAppViewer::cleanup() // Delete workers first // shotdown all worker threads before deleting them in case of co-dependencies - coreHttpLib.requestStop(); + mAppCoreHttp.requestStop(); sTextureFetch->shutdown(); sTextureCache->shutdown(); sImageDecodeThread->shutdown(); @@ -2000,7 +1950,7 @@ bool LLAppViewer::cleanup() LLCurl::cleanupClass(); // Non-LLCurl libcurl library - coreHttpLib.cleanup(); + mAppCoreHttp.cleanup(); // If we're exiting to launch an URL, do that here so the screen // is at the right resolution before we launch IE. @@ -5298,132 +5248,3 @@ void LLAppViewer::metricsSend(bool enable_reporting) gViewerAssetStatsMain->reset(); } -namespace -{ - -const F64 CoreHttp::MAX_THREAD_WAIT_TIME(10.0); - -CoreHttp::CoreHttp() - : mRequest(NULL), - mStopHandle(LLCORE_HTTP_HANDLE_INVALID), - mStopRequested(0.0), - mStopped(false) -{} - - -CoreHttp::~CoreHttp() -{ - delete mRequest; - mRequest = NULL; -} - - -void CoreHttp::init() -{ - LLCore::HttpStatus status = LLCore::HttpRequest::createService(); - if (! status) - { - LL_ERRS("Init") << "Failed to initialize HTTP services. Reason: " - << status.toString() - << LL_ENDL; - } - - // Point to our certs or SSH/https: will fail on connect - status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE, - gDirUtilp->getCAFile()); - if (! status) - { - LL_ERRS("Init") << "Failed to set CA File for HTTP services. Reason: " - << status.toString() - << LL_ENDL; - } - - // Establish HTTP Proxy. "LLProxy" is a special string which directs - // the code to use LLProxy::applyProxySettings() to establish any - // HTTP or SOCKS proxy for http operations. - status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_LLPROXY, 1); - if (! status) - { - LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services. Reason: " - << status.toString() - << LL_ENDL; - } - - // Tracing levels for library & libcurl (note that 2 & 3 are beyond spammy): - // 0 - None - // 1 - Basic start, stop simple transitions - // 2 - libcurl CURLOPT_VERBOSE mode with brief lines - // 3 - with partial data content - status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 0); - - // Kick the thread - status = LLCore::HttpRequest::startThread(); - if (! status) - { - LL_ERRS("Init") << "Failed to start HTTP servicing thread. Reason: " - << status.toString() - << LL_ENDL; - } - - mRequest = new LLCore::HttpRequest; -} - - -void CoreHttp::requestStop() -{ - llassert_always(mRequest); - - mStopHandle = mRequest->requestStopThread(this); - if (LLCORE_HTTP_HANDLE_INVALID != mStopHandle) - { - mStopRequested = LLTimer::getTotalSeconds(); - } -} - - -void CoreHttp::cleanup() -{ - if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle) - { - // Should have been started already... - requestStop(); - } - - if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle) - { - LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services without thread shutdown" - << LL_ENDL; - } - else - { - while (! mStopped && LLTimer::getTotalSeconds() < (mStopRequested + MAX_THREAD_WAIT_TIME)) - { - mRequest->update(200000); - ms_sleep(50); - } - if (! mStopped) - { - LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services with thread shutdown incomplete" - << LL_ENDL; - } - } - - delete mRequest; - mRequest = NULL; - - LLCore::HttpStatus status = LLCore::HttpRequest::destroyService(); - if (! status) - { - LL_WARNS("Cleanup") << "Failed to shutdown HTTP services, continuing. Reason: " - << status.toString() - << LL_ENDL; - } -} - - -void CoreHttp::onCompleted(LLCore::HttpHandle, LLCore::HttpResponse *) -{ - mStopped = true; -} - -} // end anonymous namespace diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index f7d019ccba..a694ea9fe6 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -31,6 +31,7 @@ #include "llcontrol.h" #include "llsys.h" // for LLOSInfo #include "lltimer.h" +#include "llappcorehttp.h" class LLCommandLineParser; class LLFrameTimer; @@ -173,6 +174,9 @@ public: // Metrics policy helper statics. static void metricsUpdateRegion(U64 region_handle); static void metricsSend(bool enable_reporting); + + // llcorehttp init/shutdown/config information. + LLAppCoreHttp & getAppCoreHttp() { return mAppCoreHttp; } protected: virtual bool initWindow(); // Initialize the viewer's window. @@ -270,6 +274,9 @@ private: boost::scoped_ptr mUpdater; + // llcorehttp library init/shutdown helper + LLAppCoreHttp mAppCoreHttp; + //--------------------------------------------- //*NOTE: Mani - legacy updater stuff // Still useable? diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 51d57ccf68..b5586d1250 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -868,7 +868,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mMetricsStartTime(0), mHttpHandle(LLCORE_HTTP_HANDLE_INVALID), mHttpBufferArray(NULL), - mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mHttpPolicyClass(mFetcher->mHttpPolicyClass), mHttpActive(false), mHttpReplySize(0U), mHttpReplyOffset(0U), @@ -2302,6 +2302,7 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mHttpOptions(NULL), mHttpHeaders(NULL), mHttpMetricsHeaders(NULL), + mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), mHttpSemaphore(HTTP_REQUESTS_IN_QUEUE_HIGH_WATER), mTotalCacheReadCount(0U), mTotalCacheWriteCount(0U), @@ -2324,6 +2325,7 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c"); mHttpMetricsHeaders = new LLCore::HttpHeaders; mHttpMetricsHeaders->mHeaders.push_back("Content-Type: application/llsd+xml"); + mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicyDefault(); } LLTextureFetch::~LLTextureFetch() @@ -3631,7 +3633,6 @@ bool TFReqSendMetrics::doWork(LLTextureFetch * fetcher) { static const U32 report_priority(1); - static const int report_policy_class(LLCore::HttpRequest::DEFAULT_POLICY_ID); static LLCore::HttpHandler * const handler(fetcher->isQAMode() || true ? &stats_handler : NULL); if (! gViewerAssetStatsThread1) @@ -3671,7 +3672,7 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) LLCore::BufferArrayStream bas(ba); LLSDSerialize::toXML(merged_llsd, bas); - fetcher->getHttpRequest().requestPost(report_policy_class, + fetcher->getHttpRequest().requestPost(fetcher->getPolicyClass(), report_priority, mCapsURL, ba, @@ -3797,7 +3798,7 @@ LLTextureFetchDebugger::LLTextureFetchDebugger(LLTextureFetch* fetcher, LLTextur mTextureCache(cache), mImageDecodeThread(imagedecodethread), mHttpHeaders(NULL), - mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID) + mHttpPolicyClass(fetcher->getPolicyClass()) { init(); } diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 54ffeda8df..115e471bc9 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -164,6 +164,9 @@ public: // Threads: T* LLCore::HttpRequest & getHttpRequest() { return *mHttpRequest; } + // Threads: T* + LLCore::HttpRequest::policy_t getPolicyClass() const { return mHttpPolicyClass; } + // Return a pointer to the shared metrics headers definition. // Does not increment the reference count, caller is required // to do that to hold a reference for any length of time. @@ -339,10 +342,11 @@ private: // Interfaces and objects into the core http library used // to make our HTTP requests. These replace the various // LLCurl interfaces used in the past. - LLCore::HttpRequest * mHttpRequest; // Ttf - LLCore::HttpOptions * mHttpOptions; // Ttf - LLCore::HttpHeaders * mHttpHeaders; // Ttf - LLCore::HttpHeaders * mHttpMetricsHeaders; // Ttf + LLCore::HttpRequest * mHttpRequest; // Ttf + LLCore::HttpOptions * mHttpOptions; // Ttf + LLCore::HttpHeaders * mHttpHeaders; // Ttf + LLCore::HttpHeaders * mHttpMetricsHeaders; // Ttf + LLCore::HttpRequest::policy_t mHttpPolicyClass; // T* // We use a resource semaphore to keep HTTP requests in // WAIT_HTTP_RESOURCE2 if there aren't sufficient slots in the -- cgit v1.2.3 From 334ce2556f0d51c38a76d655084ae1d4671f6aec Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 23 Jul 2012 17:00:11 -0400 Subject: Cleaning up comments, names, miscellany. --- indra/llcorehttp/_httpinternal.h | 4 ++-- indra/llcorehttp/_httpreadyqueue.h | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h index 5f966500c9..97ec5ee1d6 100644 --- a/indra/llcorehttp/_httpinternal.h +++ b/indra/llcorehttp/_httpinternal.h @@ -1,5 +1,5 @@ /** - * @file httpinternal.h + * @file _httpinternal.h * @brief Implementation constants and magic numbers * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ @@ -44,7 +44,7 @@ namespace LLCore { // Maxium number of policy classes that can be defined. -// *FIXME: Currently limited to the default class, extend. +// *TODO: Currently limited to the default class, extend. const int POLICY_CLASS_LIMIT = 1; // Debug/informational tracing. Used both diff --git a/indra/llcorehttp/_httpreadyqueue.h b/indra/llcorehttp/_httpreadyqueue.h index 8462b174b5..9cf4b059a1 100644 --- a/indra/llcorehttp/_httpreadyqueue.h +++ b/indra/llcorehttp/_httpreadyqueue.h @@ -45,6 +45,12 @@ namespace LLCore /// important of those rules is that any iterator becomes invalid /// on element erasure. So pay attention. /// +/// If LLCORE_READY_QUEUE_IGNORES_PRIORITY tests true, the class +/// implements a std::priority_queue interface but on std::deque +/// behavior to eliminate sensitivity to priority. In the future, +/// this will likely become the only behavior or it may become +/// a run-time election. +/// /// Threading: not thread-safe. Expected to be used entirely by /// a single thread, typically a worker thread of some sort. -- cgit v1.2.3 From 85e69b043b098dbe5a09f2eac6ff541123089f13 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 23 Jul 2012 23:40:07 +0000 Subject: Big comment and naming cleanup. Ready for prime-time. Add to-do list to _httpinternal.h to guide anyone who wants to pitch in and help. --- indra/llcorehttp/_httpinternal.h | 93 ++++++++++++++++++++++++++-------- indra/llcorehttp/_httplibcurl.cpp | 13 ++--- indra/llcorehttp/_httplibcurl.h | 27 ++++++++-- indra/llcorehttp/_httpopcancel.cpp | 7 --- indra/llcorehttp/_httpopcancel.h | 5 +- indra/llcorehttp/_httpoperation.cpp | 8 +-- indra/llcorehttp/_httpoperation.h | 77 +++++++++++++++++++++++++--- indra/llcorehttp/_httpoprequest.cpp | 47 +++++++---------- indra/llcorehttp/_httpoprequest.h | 26 ++++++++-- indra/llcorehttp/_httpopsetget.cpp | 8 --- indra/llcorehttp/_httpopsetget.h | 3 ++ indra/llcorehttp/_httppolicy.cpp | 12 +++-- indra/llcorehttp/_httppolicy.h | 39 ++++++++++++-- indra/llcorehttp/_httppolicyclass.cpp | 8 +-- indra/llcorehttp/_httppolicyglobal.cpp | 8 +-- indra/llcorehttp/_httpreadyqueue.h | 10 ++-- indra/llcorehttp/_httpservice.cpp | 24 ++++----- indra/llcorehttp/_httpservice.h | 6 +-- indra/llcorehttp/httpcommon.h | 18 +++++-- indra/llcorehttp/httpoptions.cpp | 6 +-- 20 files changed, 313 insertions(+), 132 deletions(-) diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h index 97ec5ee1d6..465e2036b3 100644 --- a/indra/llcorehttp/_httpinternal.h +++ b/indra/llcorehttp/_httpinternal.h @@ -32,12 +32,65 @@ // something wrong is probably happening. +// -------------------------------------------------------------------- +// General library to-do list +// +// - Implement policy classes. Structure is mostly there just didn't +// need it for the first consumer. +// - Consider Removing 'priority' from the request interface. Its use +// in an always active class can lead to starvation of low-priority +// requests. Requires coodination of priority values across all +// components that share a class. Changing priority across threads +// is slightly expensive (relative to gain) and hasn't been completely +// implemented. And the major user of priority, texture fetches, +// may not really need it. +// - Set/get for global policy and policy classes is clumsy. Rework +// it heading in a direction that allows for more dynamic behavior. +// - Move HttpOpRequest::prepareRequest() to HttpLibcurl for the +// pedantic. +// - Update downloader and other long-duration services are going to +// need a progress notification. Initial idea is to introduce a +// 'repeating request' which can piggyback on another request and +// persist until canceled or carrier completes. Current queue +// structures allow an HttpOperation object to be enqueued +// repeatedly, so... +// - Investigate making c-ares' re-implementation of a resolver library +// more resilient or more intelligent on Mac. Part of the DNS failure +// lies in here. The mechanism also looks a little less dynamic +// than needed in an environments where networking is changing. +// - Global optimizations: 'borrowing' connections from other classes, +// HTTP pipelining. +// - Dynamic/control system stuff: detect problems and self-adjust. +// This won't help in the face of the router problems we've looked +// at, however. Detect starvation due to UDP activity and provide +// feedback to it. +// +// Integration to-do list +// - LLTextureFetch still needs a major refactor. The use of +// LLQueuedThread makes it hard to inspect workers and do the +// resource waiting we're now doing. Rebuild along simpler lines +// some of which are suggested in new commentary at the top of +// the main source file. +// - Expand areas of usage eventually leading to the removal of LLCurl. +// Rough order of expansion: +// . Mesh fetch +// . Avatar names +// . Group membership lists +// . Caps access in general +// . 'The rest' +// - Adapt texture cache, image decode and other image consumers to +// the BufferArray model to reduce data copying. Alternatively, +// adapt this library to something else. +// +// -------------------------------------------------------------------- + + // If '1', internal ready queues will not order ready // requests by priority, instead it's first-come-first-served. // Reprioritization requests have the side-effect of then // putting the modified request at the back of the ready queue. -#define LLCORE_READY_QUEUE_IGNORES_PRIORITY 1 +#define LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY 1 namespace LLCore @@ -45,48 +98,48 @@ namespace LLCore // Maxium number of policy classes that can be defined. // *TODO: Currently limited to the default class, extend. -const int POLICY_CLASS_LIMIT = 1; +const int HTTP_POLICY_CLASS_LIMIT = 1; // Debug/informational tracing. Used both // as a global option and in per-request traces. -const int TRACE_OFF = 0; -const int TRACE_LOW = 1; -const int TRACE_CURL_HEADERS = 2; -const int TRACE_CURL_BODIES = 3; +const int HTTP_TRACE_OFF = 0; +const int HTTP_TRACE_LOW = 1; +const int HTTP_TRACE_CURL_HEADERS = 2; +const int HTTP_TRACE_CURL_BODIES = 3; -const int TRACE_MIN = TRACE_OFF; -const int TRACE_MAX = TRACE_CURL_BODIES; +const int HTTP_TRACE_MIN = HTTP_TRACE_OFF; +const int HTTP_TRACE_MAX = HTTP_TRACE_CURL_BODIES; // Request retry limits -const int DEFAULT_RETRY_COUNT = 5; -const int LIMIT_RETRY_MIN = 0; -const int LIMIT_RETRY_MAX = 100; +const int HTTP_RETRY_COUNT_DEFAULT = 5; +const int HTTP_RETRY_COUNT_MIN = 0; +const int HTTP_RETRY_COUNT_MAX = 100; -const int DEFAULT_HTTP_REDIRECTS = 10; +const int HTTP_REDIRECTS_DEFAULT = 10; // Timeout value used for both connect and protocol exchange. // Retries and time-on-queue are not included and aren't // accounted for. -const long DEFAULT_TIMEOUT = 30L; -const long LIMIT_TIMEOUT_MIN = 0L; -const long LIMIT_TIMEOUT_MAX = 3600L; +const long HTTP_REQUEST_TIMEOUT_DEFAULT = 30L; +const long HTTP_REQUEST_TIMEOUT_MIN = 0L; +const long HTTP_REQUEST_TIMEOUT_MAX = 3600L; // Limits on connection counts -const int DEFAULT_CONNECTIONS = 8; -const int LIMIT_CONNECTIONS_MIN = 1; -const int LIMIT_CONNECTIONS_MAX = 256; +const int HTTP_CONNECTION_LIMIT_DEFAULT = 8; +const int HTTP_CONNECTION_LIMIT_MIN = 1; +const int HTTP_CONNECTION_LIMIT_MAX = 256; // Tuning parameters // Time worker thread sleeps after a pass through the // request, ready and active queues. -const int LOOP_SLEEP_NORMAL_MS = 2; +const int HTTP_SERVICE_LOOP_SLEEP_NORMAL_MS = 2; // Block allocation size (a tuning parameter) is found // in bufferarray.h. // Compatibility controls -const bool ENABLE_LINKSYS_WRT54G_V5_DNS_FIX = true; +const bool HTTP_ENABLE_LINKSYS_WRT54G_V5_DNS_FIX = true; } // end namespace LLCore diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index e031efbc91..4e2e3f0e0e 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -85,7 +85,7 @@ void HttpLibcurl::shutdown() void HttpLibcurl::start(int policy_count) { - llassert_always(policy_count <= POLICY_CLASS_LIMIT); + llassert_always(policy_count <= HTTP_POLICY_CLASS_LIMIT); llassert_always(! mMultiHandles); // One-time call only mPolicyCount = policy_count; @@ -156,6 +156,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() } +// Caller has provided us with a ref count on op. void HttpLibcurl::addOp(HttpOpRequest * op) { llassert_always(op->mReqPolicy < mPolicyCount); @@ -165,7 +166,7 @@ void HttpLibcurl::addOp(HttpOpRequest * op) if (! op->prepareRequest(mService)) { // Couldn't issue request, fail with notification - // *FIXME: Need failure path + // *TODO: Need failure path return; } @@ -173,7 +174,7 @@ void HttpLibcurl::addOp(HttpOpRequest * op) curl_multi_add_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle); op->mCurlActive = true; - if (op->mTracing > TRACE_OFF) + if (op->mTracing > HTTP_TRACE_OFF) { HttpPolicy & policy(mService->getPolicy()); @@ -215,7 +216,7 @@ bool HttpLibcurl::cancel(HttpHandle handle) // *NOTE: cancelRequest logic parallels completeRequest logic. // Keep them synchronized as necessary. Caller is expected to -// remove to op from the active list and release the op *after* +// remove the op from the active list and release the op *after* // calling this method. It must be called first to deliver the // op to the reply queue with refcount intact. void HttpLibcurl::cancelRequest(HttpOpRequest * op) @@ -229,7 +230,7 @@ void HttpLibcurl::cancelRequest(HttpOpRequest * op) op->mCurlHandle = NULL; // Tracing - if (op->mTracing > TRACE_OFF) + if (op->mTracing > HTTP_TRACE_OFF) { LL_INFOS("CoreHttp") << "TRACE, RequestCanceled, Handle: " << static_cast(op) @@ -305,7 +306,7 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode op->mCurlHandle = NULL; // Tracing - if (op->mTracing > TRACE_OFF) + if (op->mTracing > HTTP_TRACE_OFF) { LL_INFOS("CoreHttp") << "TRACE, RequestComplete, Handle: " << static_cast(op) diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 53972b1ffa..611f029ef5 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -68,24 +68,41 @@ public: /// Give cycles to libcurl to run active requests. Completed /// operations (successful or failed) will be retried or handed /// over to the reply queue as final responses. + /// + /// @return Indication of how long this method is + /// willing to wait for next service call. HttpService::ELoopSpeed processTransport(); /// Add request to the active list. Caller is expected to have - /// provided us with a reference count to hold the request. (No - /// additional references will be added.) + /// provided us with a reference count on the op to hold the + /// request. (No additional references will be added.) void addOp(HttpOpRequest * op); /// One-time call to set the number of policy classes to be /// serviced and to create the resources for each. Value /// must agree with HttpPolicy::setPolicies() call. void start(int policy_count); - + + /// Synchronously stop libcurl operations. All active requests + /// are canceled and removed from libcurl's handling. Easy + /// handles are detached from their multi handles and released. + /// Multi handles are also released. Canceled requests are + /// completed with canceled status and made available on their + /// respective reply queues. + /// + /// Can be restarted with a start() call. void shutdown(); - + + /// Return global and per-class counts of active requests. int getActiveCount() const; int getActiveCountInClass(int policy_class) const; - // Shadows HttpService's method + /// Attempt to cancel a request identified by handle. + /// + /// Interface shadows HttpService's method. + /// + /// @return True if handle was found and operation canceled. + /// bool cancel(HttpHandle handle); protected: diff --git a/indra/llcorehttp/_httpopcancel.cpp b/indra/llcorehttp/_httpopcancel.cpp index 8e1105dc81..c1912eb3db 100644 --- a/indra/llcorehttp/_httpopcancel.cpp +++ b/indra/llcorehttp/_httpopcancel.cpp @@ -26,18 +26,11 @@ #include "_httpopcancel.h" -#include -#include - #include "httpcommon.h" #include "httphandler.h" #include "httpresponse.h" -#include "_httprequestqueue.h" -#include "_httpreplyqueue.h" #include "_httpservice.h" -#include "_httppolicy.h" -#include "_httplibcurl.h" namespace LLCore diff --git a/indra/llcorehttp/_httpopcancel.h b/indra/llcorehttp/_httpopcancel.h index 659d28955f..336dfdc573 100644 --- a/indra/llcorehttp/_httpopcancel.h +++ b/indra/llcorehttp/_httpopcancel.h @@ -46,11 +46,14 @@ namespace LLCore /// be canceled, if possible. This includes active requests /// that may be in the middle of an HTTP transaction. Any /// completed request will not be canceled and will return -/// its final status unchanged. +/// its final status unchanged and *this* request will complete +/// with an HE_HANDLE_NOT_FOUND error status. class HttpOpCancel : public HttpOperation { public: + /// @param handle Handle of previously-issued request to + /// be canceled. HttpOpCancel(HttpHandle handle); protected: diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index 910dbf1f2f..5cf5bc5930 100644 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -94,7 +94,7 @@ void HttpOperation::stageFromRequest(HttpService *) // Default implementation should never be called. This // indicates an operation making a transition that isn't // defined. - LL_ERRS("HttpCore") << "Default stateFromRequest method may not be called." + LL_ERRS("HttpCore") << "Default stageFromRequest method may not be called." << LL_ENDL; } @@ -104,7 +104,7 @@ void HttpOperation::stageFromReady(HttpService *) // Default implementation should never be called. This // indicates an operation making a transition that isn't // defined. - LL_ERRS("HttpCore") << "Default stateFromReady method may not be called." + LL_ERRS("HttpCore") << "Default stageFromReady method may not be called." << LL_ENDL; } @@ -114,7 +114,7 @@ void HttpOperation::stageFromActive(HttpService *) // Default implementation should never be called. This // indicates an operation making a transition that isn't // defined. - LL_ERRS("HttpCore") << "Default stateFromActive method may not be called." + LL_ERRS("HttpCore") << "Default stageFromActive method may not be called." << LL_ENDL; } @@ -143,7 +143,7 @@ HttpStatus HttpOperation::cancel() void HttpOperation::addAsReply() { - if (mTracing > TRACE_OFF) + if (mTracing > HTTP_TRACE_OFF) { LL_INFOS("CoreHttp") << "TRACE, ToReplyQueue, Handle: " << static_cast(this) diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index 717a9b0d72..914627fad0 100644 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -72,9 +72,11 @@ class HttpService; class HttpOperation : public LLCoreInt::RefCounted { public: + /// Threading: called by a consumer/application thread. HttpOperation(); protected: + /// Threading: called by any thread. virtual ~HttpOperation(); // Use release() private: @@ -82,28 +84,87 @@ private: void operator=(const HttpOperation &); // Not defined public: + /// Register a reply queue and a handler for completion notifications. + /// + /// Invokers of operations that want to receive notification that an + /// operation has been completed do so by binding a reply queue and + /// a handler object to the request. + /// + /// @param reply_queue Pointer to the reply queue where completion + /// notifications are to be queued (typically + /// by addAsReply()). This will typically be + /// the reply queue referenced by the request + /// object. This method will increment the + /// refcount on the queue holding the queue + /// until delivery is complete. Using a reply_queue + /// even if the handler is NULL has some benefits + /// for memory deallocation by keeping it in the + /// originating thread. + /// + /// @param handler Possibly NULL pointer to a non-refcounted + //// handler object to be invoked (onCompleted) + /// when the operation is finished. Note that + /// the handler object is never dereferenced + /// by the worker thread. This is passible data + /// until notification is performed. + /// + /// Threading: called by application thread. + /// void setReplyPath(HttpReplyQueue * reply_queue, HttpHandler * handler); - HttpHandler * getUserHandler() const - { - return mUserHandler; - } - + /// The three possible staging steps in an operation's lifecycle. + /// Asynchronous requests like HTTP operations move from the + /// request queue to the ready queue via stageFromRequest. Then + /// from the ready queue to the active queue by stageFromReady. And + /// when complete, to the reply queue via stageFromActive and the + /// addAsReply utility. + /// + /// Immediate mode operations (everything else) move from the + /// request queue to the reply queue directly via stageFromRequest + /// and addAsReply with no existence on the ready or active queues. + /// + /// These methods will take out a reference count on the request, + /// caller only needs to dispose of its reference when done with + /// the request. + /// + /// Threading: called by worker thread. + /// virtual void stageFromRequest(HttpService *); virtual void stageFromReady(HttpService *); virtual void stageFromActive(HttpService *); + /// Delivers a notification to a handler object on completion. + /// + /// Once a request is complete and it has been removed from its + /// reply queue, a handler notification may be delivered by a + /// call to HttpRequest::update(). This method does the necessary + /// dispatching. + /// + /// Threading: called by application thread. + /// virtual void visitNotifier(HttpRequest *); - + + /// Cancels the operation whether queued or active. + /// Final status of the request becomes canceled (an error) and + /// that will be delivered to caller via notification scheme. + /// + /// Threading: called by worker thread. + /// virtual HttpStatus cancel(); protected: + /// Delivers request to reply queue on completion. After this + /// call, worker thread no longer accesses the object and it + /// is owned by the reply queue. + /// + /// Threading: called by worker thread. + /// void addAsReply(); protected: HttpReplyQueue * mReplyQueue; // Have refcount - HttpHandler * mUserHandler; + HttpHandler * mUserHandler; // Naked pointer public: // Request Data @@ -172,7 +233,7 @@ public: /// HttpOpSpin is a test-only request that puts the worker /// thread into a cpu spin. Used for unit tests and cleanup -/// evaluation. You do not want to use this. +/// evaluation. You do not want to use this in production. class HttpOpSpin : public HttpOperation { public: diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index a18a164f0d..7db19b1841 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -111,10 +111,10 @@ HttpOpRequest::HttpOpRequest() mReplyHeaders(NULL), mPolicyRetries(0), mPolicyRetryAt(HttpTime(0)), - mPolicyRetryLimit(DEFAULT_RETRY_COUNT) + mPolicyRetryLimit(HTTP_RETRY_COUNT_DEFAULT) { // *NOTE: As members are added, retry initialization/cleanup - // may need to be extended in @prepareRequest(). + // may need to be extended in @see prepareRequest(). } @@ -153,9 +153,6 @@ HttpOpRequest::~HttpOpRequest() mCurlHeaders = NULL; } - mReplyOffset = 0; - mReplyLength = 0; - mReplyFullLength = 0; if (mReplyBody) { mReplyBody->release(); @@ -215,8 +212,6 @@ void HttpOpRequest::stageFromActive(HttpService * service) void HttpOpRequest::visitNotifier(HttpRequest * request) { - static const HttpStatus partial_content(HTTP_PARTIAL_CONTENT, HE_SUCCESS); - if (mUserHandler) { HttpResponse * response = new HttpResponse(); @@ -339,8 +334,8 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id, mProcFlags |= PF_SAVE_HEADERS; } mPolicyRetryLimit = options->getRetries(); - mPolicyRetryLimit = llclamp(mPolicyRetryLimit, LIMIT_RETRY_MIN, LIMIT_RETRY_MAX); - mTracing = (std::max)(mTracing, llclamp(options->getTrace(), TRACE_MIN, TRACE_MAX)); + mPolicyRetryLimit = llclamp(mPolicyRetryLimit, HTTP_RETRY_COUNT_MIN, HTTP_RETRY_COUNT_MAX); + mTracing = (std::max)(mTracing, llclamp(options->getTrace(), HTTP_TRACE_MIN, HTTP_TRACE_MAX)); } } @@ -394,7 +389,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this); curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); - if (ENABLE_LINKSYS_WRT54G_V5_DNS_FIX) + if (HTTP_ENABLE_LINKSYS_WRT54G_V5_DNS_FIX) { // The Linksys WRT54G V5 router has an issue with frequent // DNS lookups from LAN machines. If they happen too often, @@ -402,7 +397,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) // about 700 or so requests and starts issuing TCP RSTs to // new connections. Reuse the DNS lookups for even a few // seconds and no RSTs. - curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 10); + curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15); } else { @@ -414,7 +409,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1); curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, DEFAULT_HTTP_REDIRECTS); + curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, HTTP_REDIRECTS_DEFAULT); curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback); curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, this); curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback); @@ -434,7 +429,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } else if (policy.get(HttpRequest::GP_HTTP_PROXY, &opt_value)) { - // *TODO: This is fine for now but get fuller socks/ + // *TODO: This is fine for now but get fuller socks5/ // authentication thing going later.... curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value->c_str()); curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); @@ -497,7 +492,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } // Tracing - if (mTracing >= TRACE_CURL_HEADERS) + if (mTracing >= HTTP_TRACE_CURL_HEADERS) { curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1); curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, this); @@ -528,11 +523,11 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); // Request options - long timeout(DEFAULT_TIMEOUT); + long timeout(HTTP_REQUEST_TIMEOUT_DEFAULT); if (mReqOptions) { timeout = mReqOptions->getTimeout(); - timeout = llclamp(timeout, LIMIT_TIMEOUT_MIN, LIMIT_TIMEOUT_MAX); + timeout = llclamp(timeout, HTTP_REQUEST_TIMEOUT_MIN, HTTP_REQUEST_TIMEOUT_MAX); } curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, timeout); curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout); @@ -605,12 +600,6 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi static const char con_ran_line[] = "content-range:"; static const size_t con_ran_line_len = sizeof(con_ran_line) - 1; - static const char con_type_line[] = "content-type:"; - static const size_t con_type_line_len = sizeof(con_type_line) - 1; - - static const char con_enc_line[] = "content-encoding:"; - static const size_t con_enc_line_len = sizeof(con_enc_line) - 1; - HttpOpRequest * op(static_cast(userdata)); const size_t hdr_size(size * nmemb); @@ -705,7 +694,7 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe switch (info) { case CURLINFO_TEXT: - if (op->mTracing >= TRACE_CURL_HEADERS) + if (op->mTracing >= HTTP_TRACE_CURL_HEADERS) { tag = "TEXT"; escape_libcurl_debug_data(buffer, len, true, safe_line); @@ -714,7 +703,7 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe break; case CURLINFO_HEADER_IN: - if (op->mTracing >= TRACE_CURL_HEADERS) + if (op->mTracing >= HTTP_TRACE_CURL_HEADERS) { tag = "HEADERIN"; escape_libcurl_debug_data(buffer, len, true, safe_line); @@ -723,7 +712,7 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe break; case CURLINFO_HEADER_OUT: - if (op->mTracing >= TRACE_CURL_HEADERS) + if (op->mTracing >= HTTP_TRACE_CURL_HEADERS) { tag = "HEADEROUT"; escape_libcurl_debug_data(buffer, 2 * len, true, safe_line); // Goes out as one line @@ -732,11 +721,11 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe break; case CURLINFO_DATA_IN: - if (op->mTracing >= TRACE_CURL_HEADERS) + if (op->mTracing >= HTTP_TRACE_CURL_HEADERS) { tag = "DATAIN"; logit = true; - if (op->mTracing >= TRACE_CURL_BODIES) + if (op->mTracing >= HTTP_TRACE_CURL_BODIES) { escape_libcurl_debug_data(buffer, len, false, safe_line); } @@ -750,11 +739,11 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe break; case CURLINFO_DATA_OUT: - if (op->mTracing >= TRACE_CURL_HEADERS) + if (op->mTracing >= HTTP_TRACE_CURL_HEADERS) { tag = "DATAOUT"; logit = true; - if (op->mTracing >= TRACE_CURL_BODIES) + if (op->mTracing >= HTTP_TRACE_CURL_BODIES) { escape_libcurl_debug_data(buffer, len, false, safe_line); } diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 36dc5dc876..7b65d17783 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -88,7 +88,14 @@ public: virtual void visitNotifier(HttpRequest * request); public: - // Setup Methods + /// Setup Methods + /// + /// Basically an RPC setup for each type of HTTP method + /// invocation with one per method type. These are + /// generally invoked right after construction. + /// + /// Threading: called by application thread + /// HttpStatus setupGet(HttpRequest::policy_t policy_id, HttpRequest::priority_t priority, const std::string & url, @@ -116,19 +123,32 @@ public: BufferArray * body, HttpOptions * options, HttpHeaders * headers); - + + // Internal method used to setup the libcurl options for a request. + // Does all the libcurl handle setup in one place. + // + // Threading: called by worker thread + // HttpStatus prepareRequest(HttpService * service); virtual HttpStatus cancel(); protected: + // Common setup for all the request methods. + // + // Threading: called by application thread + // void setupCommon(HttpRequest::policy_t policy_id, HttpRequest::priority_t priority, const std::string & url, BufferArray * body, HttpOptions * options, HttpHeaders * headers); - + + // libcurl operational callbacks + // + // Threading: called by worker thread + // static size_t writeCallback(void * data, size_t size, size_t nmemb, void * userdata); static size_t readCallback(void * data, size_t size, size_t nmemb, void * userdata); static size_t headerCallback(void * data, size_t size, size_t nmemb, void * userdata); diff --git a/indra/llcorehttp/_httpopsetget.cpp b/indra/llcorehttp/_httpopsetget.cpp index c1357f9ae5..8198528a9b 100644 --- a/indra/llcorehttp/_httpopsetget.cpp +++ b/indra/llcorehttp/_httpopsetget.cpp @@ -26,18 +26,10 @@ #include "_httpopsetget.h" -#include -#include - #include "httpcommon.h" -#include "httphandler.h" -#include "httpresponse.h" -#include "_httprequestqueue.h" -#include "_httpreplyqueue.h" #include "_httpservice.h" #include "_httppolicy.h" -#include "_httplibcurl.h" namespace LLCore diff --git a/indra/llcorehttp/_httpopsetget.h b/indra/llcorehttp/_httpopsetget.h index efb24855c5..6966b9d94e 100644 --- a/indra/llcorehttp/_httpopsetget.h +++ b/indra/llcorehttp/_httpopsetget.h @@ -44,6 +44,8 @@ namespace LLCore /// HttpOpSetGet requests dynamic changes to policy and /// configuration settings. +/// +/// *NOTE: Expect this to change. Don't really like it yet. class HttpOpSetGet : public HttpOperation { @@ -58,6 +60,7 @@ private: void operator=(const HttpOpSetGet &); // Not defined public: + /// Threading: called by application thread void setupGet(HttpRequest::EGlobalPolicy setting); void setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value); diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 1e64924198..c7a69ad133 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -40,12 +40,17 @@ namespace LLCore { +// Per-policy-class data for a running system. +// Collection of queues, parameters, history, metrics, etc. +// for a single policy class. +// +// Threading: accessed only by worker thread struct HttpPolicy::State { public: State() - : mConnMax(DEFAULT_CONNECTIONS), - mConnAt(DEFAULT_CONNECTIONS), + : mConnMax(HTTP_CONNECTION_LIMIT_DEFAULT), + mConnAt(HTTP_CONNECTION_LIMIT_DEFAULT), mConnMin(1), mNextSample(0), mErrorCount(0), @@ -298,6 +303,7 @@ bool HttpPolicy::cancel(HttpHandle handle) return false; } + bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) { static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); @@ -345,7 +351,7 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) } -int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) +int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) const { if (policy_class < mActiveClasses) { diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index a02bf084c1..03d92c0b8e 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -63,22 +63,37 @@ public: /// Cancel all ready and retry requests sending them to /// their notification queues. Release state resources /// making further request handling impossible. + /// + /// Threading: called by worker thread void shutdown(); /// Deliver policy definitions and enable handling of /// requests. One-time call invoked before starting /// the worker thread. + /// + /// Threading: called by application thread void start(const HttpPolicyGlobal & global, const std::vector & classes); /// Give the policy layer some cycles to scan the ready /// queue promoting higher-priority requests to active /// as permited. + /// + /// @return Indication of how soon this method + /// should be called again. + /// + /// Threading: called by worker thread HttpService::ELoopSpeed processReadyQueue(); /// Add request to a ready queue. Caller is expected to have /// provided us with a reference count to hold the request. (No /// additional references will be added.) + /// + /// OpRequest is owned by the request queue after this call + /// and should not be modified by anyone until retrieved + /// from queue. + /// + /// Threading: called by any thread void addOp(HttpOpRequest *); /// Similar to addOp, used when a caller wants to retry a @@ -87,12 +102,20 @@ public: /// handling is the same and retried operations are considered /// before new ones but that doesn't guarantee completion /// order. + /// + /// Threading: called by worker thread void retryOp(HttpOpRequest *); - // Shadows HttpService's method + /// Attempt to change the priority of an earlier request. + /// Request that Shadows HttpService's method + /// + /// Threading: called by worker thread bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); - // Shadows HttpService's method as well + /// Attempt to cancel a previous request. + /// Shadows HttpService's method as well + /// + /// Threading: called by worker thread bool cancel(HttpHandle handle); /// When transport is finished with an op and takes it off the @@ -103,17 +126,25 @@ public: /// @return Returns true of the request is still active /// or ready after staging, false if has been /// sent on to the reply queue. + /// + /// Threading: called by worker thread bool stageAfterCompletion(HttpOpRequest * op); // Get pointer to global policy options. Caller is expected // to do context checks like no setting once running. + /// + /// Threading: called by any thread *but* the object may + /// only be modified by the worker thread once running. + /// HttpPolicyGlobal & getGlobalOptions() { return mGlobalOptions; } - // Get ready counts for a particular class - int getReadyCount(HttpRequest::policy_t policy_class); + /// Get ready counts for a particular policy class + /// + /// Threading: called by worker thread + int getReadyCount(HttpRequest::policy_t policy_class) const; protected: struct State; diff --git a/indra/llcorehttp/_httppolicyclass.cpp b/indra/llcorehttp/_httppolicyclass.cpp index 8007468d3c..a23b81322c 100644 --- a/indra/llcorehttp/_httppolicyclass.cpp +++ b/indra/llcorehttp/_httppolicyclass.cpp @@ -35,8 +35,8 @@ namespace LLCore HttpPolicyClass::HttpPolicyClass() : mSetMask(0UL), - mConnectionLimit(DEFAULT_CONNECTIONS), - mPerHostConnectionLimit(DEFAULT_CONNECTIONS), + mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT), + mPerHostConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT), mPipelining(0) {} @@ -71,11 +71,11 @@ HttpStatus HttpPolicyClass::set(HttpRequest::EClassPolicy opt, long value) switch (opt) { case HttpRequest::CP_CONNECTION_LIMIT: - mConnectionLimit = llclamp(value, long(LIMIT_CONNECTIONS_MIN), long(LIMIT_CONNECTIONS_MAX)); + mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX)); break; case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT: - mPerHostConnectionLimit = llclamp(value, long(LIMIT_CONNECTIONS_MIN), mConnectionLimit); + mPerHostConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), mConnectionLimit); break; case HttpRequest::CP_ENABLE_PIPELINING: diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp index ca04839eaf..72f409d3b1 100644 --- a/indra/llcorehttp/_httppolicyglobal.cpp +++ b/indra/llcorehttp/_httppolicyglobal.cpp @@ -35,8 +35,8 @@ namespace LLCore HttpPolicyGlobal::HttpPolicyGlobal() : mSetMask(0UL), - mConnectionLimit(DEFAULT_CONNECTIONS), - mTrace(TRACE_OFF), + mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT), + mTrace(HTTP_TRACE_OFF), mUseLLProxy(0) {} @@ -66,11 +66,11 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value) switch (opt) { case HttpRequest::GP_CONNECTION_LIMIT: - mConnectionLimit = llclamp(value, long(LIMIT_CONNECTIONS_MIN), long(LIMIT_CONNECTIONS_MAX)); + mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX)); break; case HttpRequest::GP_TRACE: - mTrace = llclamp(value, long(TRACE_MIN), long(TRACE_MAX)); + mTrace = llclamp(value, long(HTTP_TRACE_MIN), long(HTTP_TRACE_MAX)); break; case HttpRequest::GP_LLPROXY: diff --git a/indra/llcorehttp/_httpreadyqueue.h b/indra/llcorehttp/_httpreadyqueue.h index 9cf4b059a1..5f19a9c5f9 100644 --- a/indra/llcorehttp/_httpreadyqueue.h +++ b/indra/llcorehttp/_httpreadyqueue.h @@ -45,7 +45,7 @@ namespace LLCore /// important of those rules is that any iterator becomes invalid /// on element erasure. So pay attention. /// -/// If LLCORE_READY_QUEUE_IGNORES_PRIORITY tests true, the class +/// If LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY tests true, the class /// implements a std::priority_queue interface but on std::deque /// behavior to eliminate sensitivity to priority. In the future, /// this will likely become the only behavior or it may become @@ -54,7 +54,7 @@ namespace LLCore /// Threading: not thread-safe. Expected to be used entirely by /// a single thread, typically a worker thread of some sort. -#if LLCORE_READY_QUEUE_IGNORES_PRIORITY +#if LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY typedef std::deque HttpReadyQueueBase; @@ -64,7 +64,7 @@ typedef std::priority_queue, LLCore::HttpOpRequestCompare> HttpReadyQueueBase; -#endif // LLCORE_READY_QUEUE_IGNORES_PRIORITY +#endif // LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY class HttpReadyQueue : public HttpReadyQueueBase { @@ -82,7 +82,7 @@ protected: public: -#if LLCORE_READY_QUEUE_IGNORES_PRIORITY +#if LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY // Types and methods needed to make a std::deque look // more like a std::priority_queue, at least for our // purposes. @@ -103,7 +103,7 @@ public: push_back(v); } -#endif // LLCORE_READY_QUEUE_IGNORES_PRIORITY +#endif // LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY const container_type & get_container() const { diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index f7d9813db0..0825888d0f 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -55,8 +55,8 @@ HttpService::HttpService() { // Create the default policy class HttpPolicyClass pol_class; - pol_class.set(HttpRequest::CP_CONNECTION_LIMIT, DEFAULT_CONNECTIONS); - pol_class.set(HttpRequest::CP_PER_HOST_CONNECTION_LIMIT, DEFAULT_CONNECTIONS); + pol_class.set(HttpRequest::CP_CONNECTION_LIMIT, HTTP_CONNECTION_LIMIT_DEFAULT); + pol_class.set(HttpRequest::CP_PER_HOST_CONNECTION_LIMIT, HTTP_CONNECTION_LIMIT_DEFAULT); pol_class.set(HttpRequest::CP_ENABLE_PIPELINING, 0L); mPolicyClasses.push_back(pol_class); } @@ -150,7 +150,7 @@ void HttpService::term() HttpRequest::policy_t HttpService::createPolicyClass() { const HttpRequest::policy_t policy_class(mPolicyClasses.size()); - if (policy_class >= POLICY_CLASS_LIMIT) + if (policy_class >= HTTP_POLICY_CLASS_LIMIT) { return 0; } @@ -219,12 +219,12 @@ bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t prio } - /// Try to find the given request handle on any of the request - /// queues and cancel the operation. - /// - /// @return True if the request was canceled. - /// - /// Threading: callable by worker thread. +/// Try to find the given request handle on any of the request +/// queues and cancel the operation. +/// +/// @return True if the request was canceled. +/// +/// Threading: callable by worker thread. bool HttpService::cancel(HttpHandle handle) { bool canceled(false); @@ -297,7 +297,7 @@ void HttpService::threadRun(LLCoreInt::HttpThread * thread) // Determine whether to spin, sleep briefly or sleep for next request if (REQUEST_SLEEP != loop) { - ms_sleep(LOOP_SLEEP_NORMAL_MS); + ms_sleep(HTTP_SERVICE_LOOP_SLEEP_NORMAL_MS); } } @@ -321,11 +321,11 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop) if (! mExitRequested) { // Setup for subsequent tracing - long tracing(TRACE_OFF); + long tracing(HTTP_TRACE_OFF); mPolicy->getGlobalOptions().get(HttpRequest::GP_TRACE, &tracing); op->mTracing = (std::max)(op->mTracing, int(tracing)); - if (op->mTracing > TRACE_OFF) + if (op->mTracing > HTTP_TRACE_OFF) { LL_INFOS("CoreHttp") << "TRACE, FromRequestQueue, Handle: " << static_cast(op) diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index d24c497ca9..ffe0349d4d 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -55,7 +55,7 @@ class HttpPolicy; class HttpLibcurl; -/// The HttpService class does the work behind the request queue. It +/// The HttpService class does the work behind the request queue. It /// oversees the HTTP workflow carrying out a number of tasks: /// - Pulling requests from the global request queue /// - Executing 'immediate' requests directly @@ -76,7 +76,7 @@ class HttpLibcurl; /// HttpPolicy and HttpLibcurl (transport). These always exist in a /// 1:1:1 relationship with HttpService managing instances of the other /// two. So, these classes do not use reference counting to refer -/// to one-another, their lifecycles are always managed together. +/// to one another, their lifecycles are always managed together. class HttpService { @@ -206,7 +206,7 @@ protected: // === shared data === static volatile EState sState; - HttpRequestQueue * mRequestQueue; + HttpRequestQueue * mRequestQueue; // Refcounted LLAtomicU32 mExitRequested; LLCoreInt::HttpThread * mThread; diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index dd5798edf9..c0d4ec5aad 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -60,8 +60,10 @@ /// Using the library is fairly easy. Global setup needs a few /// steps: /// -/// - libcurl initialization with thread-safely callbacks for c-ares -/// DNS lookups. +/// - libcurl initialization including thread-safely callbacks for SSL: +/// . curl_global_init(...) +/// . CRYPTO_set_locking_callback(...) +/// . CRYPTO_set_id_callback(...) /// - HttpRequest::createService() called to instantiate singletons /// and support objects. /// @@ -90,8 +92,18 @@ /// - Do completion processing in your onCompletion() method. /// /// Code fragments: -/// +/// Rather than a poorly-maintained example in comments, look in the +/// example subdirectory which is a minimal yet functional tool to do +/// GET request performance testing. With four calls: /// +/// init_curl(); +/// LLCore::HttpRequest::createService(); +/// LLCore::HttpRequest::startThread(); +/// LLCore::HttpRequest * hr = new LLCore::HttpRequest(); +/// +/// the program is basically ready to issue requests. +/// + #include "linden_common.h" // Modifies curl/curl.h interfaces diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp index 68f7277ed3..1699d19f8d 100644 --- a/indra/llcorehttp/httpoptions.cpp +++ b/indra/llcorehttp/httpoptions.cpp @@ -36,9 +36,9 @@ namespace LLCore HttpOptions::HttpOptions() : RefCounted(true), mWantHeaders(false), - mTracing(TRACE_OFF), - mTimeout(DEFAULT_TIMEOUT), - mRetries(DEFAULT_RETRY_COUNT) + mTracing(HTTP_TRACE_OFF), + mTimeout(HTTP_REQUEST_TIMEOUT_DEFAULT), + mRetries(HTTP_RETRY_COUNT_DEFAULT) {} -- cgit v1.2.3 From 052f985e7546aff2203773cf43c9a688c9c340ba Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 24 Jul 2012 15:21:28 -0400 Subject: Trivial change to tickle build. --- indra/llcorehttp/bufferarray.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index 5eb8f84c34..8eaaeed710 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -350,5 +350,3 @@ BufferArray::Block * BufferArray::Block::alloc(size_t len) } // end namespace LLCore - - -- cgit v1.2.3 From 1d5490e752deeff316658f4850aac5fc96a91866 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 25 Jul 2012 22:29:53 +0000 Subject: SH-3304 drano-http viewer fails to launch on linux libapr-1.so.0.4.5 and libaprutil-1.so.0.4.1 missing from manifest. --- indra/newview/viewer_manifest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index a0dfc19e9c..42691fb51b 100644 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1027,10 +1027,10 @@ class Linux_i686Manifest(LinuxManifest): if self.prefix("../packages/lib/release", dst="lib"): self.path("libapr-1.so") self.path("libapr-1.so.0") - self.path("libapr-1.so.0.4.2") + self.path("libapr-1.so.0.4.5") self.path("libaprutil-1.so") self.path("libaprutil-1.so.0") - self.path("libaprutil-1.so.0.3.10") + self.path("libaprutil-1.so.0.4.1") self.path("libboost_program_options-mt.so.1.48.0") self.path("libboost_regex-mt.so.1.48.0") self.path("libboost_thread-mt.so.1.48.0") -- cgit v1.2.3 From bf004be1023347bcabaae6baa1624b2ed78d69fd Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 1 Aug 2012 12:38:28 -0400 Subject: SH-3308 Beef up retry messaging. Reformatted messages around request retry. Successfully retried requests also message so you can see the cycle closed. Added additional retryable error codes (timeout, other libcurl failures). Commenting and removed some unnecessary std::min logic. --- indra/llcorehttp/_httplibcurl.cpp | 10 ++++++++-- indra/llcorehttp/_httppolicy.cpp | 34 ++++++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 4e2e3f0e0e..6fe0bfc7d1 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -97,6 +97,12 @@ void HttpLibcurl::start(int policy_count) } +// Give libcurl some cycles, invoke it's callbacks, process +// completed requests finalizing or issuing retries as needed. +// +// If active list goes empty *and* we didn't queue any +// requests for retry, we return a request for a hard +// sleep otherwise ask for a normal polling interval. HttpService::ELoopSpeed HttpLibcurl::processTransport() { HttpService::ELoopSpeed ret(HttpService::REQUEST_SLEEP); @@ -129,7 +135,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() if (completeRequest(mMultiHandles[policy_class], handle, result)) { // Request is still active, don't get too sleepy - ret = (std::min)(ret, HttpService::NORMAL); + ret = HttpService::NORMAL; } handle = NULL; // No longer valid on return } @@ -150,7 +156,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() if (! mActiveOps.empty()) { - ret = (std::min)(ret, HttpService::NORMAL); + ret = HttpService::NORMAL; } return ret; } diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index c7a69ad133..76c1e22431 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -162,8 +162,10 @@ void HttpPolicy::retryOp(HttpOpRequest * op) const HttpTime delta(retry_deltas[llclamp(op->mPolicyRetries, 0, delta_max)]); op->mPolicyRetryAt = now + delta; ++op->mPolicyRetries; - LL_WARNS("CoreHttp") << "URL op retry #" << op->mPolicyRetries - << " being scheduled for " << delta << " uSecs from now." + LL_WARNS("CoreHttp") << "HTTP request " << static_cast(op) + << " retry " << op->mPolicyRetries + << " scheduled for +" << (delta / HttpTime(1000)) + << " mS. Status: " << op->mStatus.toHex() << LL_ENDL; if (op->mTracing > 0) { @@ -175,6 +177,17 @@ void HttpPolicy::retryOp(HttpOpRequest * op) } +// Attempt to deliver requests to the transport layer. +// +// Tries to find HTTP requests for each policy class with +// available capacity. Starts with the retry queue first +// looking for requests that have waited long enough then +// moves on to the ready queue. +// +// If all queues are empty, will return an indication that +// the worker thread may sleep hard otherwise will ask for +// normal polling frequency. +// HttpService::ELoopSpeed HttpPolicy::processReadyQueue() { const HttpTime now(totalTime()); @@ -311,6 +324,9 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) static const HttpStatus cant_res_host(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST); static const HttpStatus send_error(HttpStatus::EXT_CURL_EASY, CURLE_SEND_ERROR); static const HttpStatus recv_error(HttpStatus::EXT_CURL_EASY, CURLE_RECV_ERROR); + static const HttpStatus upload_failed(HttpStatus::EXT_CURL_EASY, CURLE_UPLOAD_FAILED); + static const HttpStatus op_timedout(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT); + static const HttpStatus post_error(HttpStatus::EXT_CURL_EASY, CURLE_HTTP_POST_ERROR); // Retry or finalize if (! op->mStatus) @@ -323,7 +339,10 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) cant_res_proxy == op->mStatus || cant_res_host == op->mStatus || send_error == op->mStatus || - recv_error == op->mStatus)) + recv_error == op->mStatus || + upload_failed == op->mStatus || + op_timedout == op->mStatus || + post_error == op->mStatus)) { // Okay, worth a retry. We include 499 in this test as // it's the old 'who knows?' error from many grid services... @@ -335,14 +354,17 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) // This op is done, finalize it delivering it to the reply queue... if (! op->mStatus) { - LL_WARNS("CoreHttp") << "URL op failed after " << op->mPolicyRetries + LL_WARNS("CoreHttp") << "HTTP request " << static_cast(op) + << " failed after " << op->mPolicyRetries << " retries. Reason: " << op->mStatus.toString() + << " (" << op->mStatus.toHex() << ")" << LL_ENDL; } else if (op->mPolicyRetries) { - LL_DEBUGS("CoreHttp") << "URL op succeeded after " << op->mPolicyRetries << " retries." - << LL_ENDL; + LL_WARNS("CoreHttp") << "HTTP request " << static_cast(op) + << " succeeded on retry " << op->mPolicyRetries << "." + << LL_ENDL; } op->stageFromActive(mService); -- cgit v1.2.3 From 7d98d1afb036ce4b221e101e6de153a3d978ba9f Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 3 Aug 2012 14:02:36 -0400 Subject: Uniform gray background for texture console window. --- indra/newview/lltextureview.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index f7636b2473..29925657b5 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -524,8 +524,8 @@ void LLGLTexMemBar::draw() // Gray background using completely magic numbers gGL.color4f(0.f, 0.f, 0.f, 0.25f); - const LLRect & rect(getRect()); - gl_rect_2d(-4, v_offset, rect.mRight - rect.mLeft + 2, v_offset + line_height*4); + // const LLRect & rect(getRect()); + // gl_rect_2d(-4, v_offset, rect.mRight - rect.mLeft + 2, v_offset + line_height*4); std::string text = ""; LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*6, -- cgit v1.2.3 From 9a6881bdedf530872c442bddbe0335ca475c736e Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 17 Aug 2012 12:01:22 -0400 Subject: SH-3328 Permanently add 'QAModeHttpTrace' setting to control llcorehttp trace functionality llcorehttp implements a nice trace facility but it hasn't been attached to an external control to date. This hands over control to a *non-persistent* setting that can be used for QA or field diagnostics. --- indra/newview/llappcorehttp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index e89c8d5ac2..a3cd0638d5 100644 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -85,7 +85,9 @@ void LLAppCoreHttp::init() // 1 - Basic start, stop simple transitions // 2 - libcurl CURLOPT_VERBOSE mode with brief lines // 3 - with partial data content - status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 0); + long trace_level(0L); + trace_level = long(gSavedSettings.getU32("QAModeHttpTrace")); + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, trace_level); // Setup default policy and constrain if directed to mPolicyDefault = LLCore::HttpRequest::DEFAULT_POLICY_ID; -- cgit v1.2.3 From 9de14fe45b6fc3b97e8c1ca91161e1fd5a9aab65 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 17 Aug 2012 12:33:12 -0400 Subject: SH-3327 HTTP transfers not showing up in texture console 'net tot tex' value. Dropped an argument during integration which made the total byte count read lower than expected. Everything else is fine, however. --- indra/newview/lltexturefetch.cpp | 4 ++-- indra/newview/lltexturefetch.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index efaeafbe78..718b15825d 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -924,7 +924,7 @@ LLTextureFetchWorker::~LLTextureFetchWorker() mHttpBufferArray = NULL; } unlockWorkMutex(); // -Mw - mFetcher->removeFromHTTPQueue(mID); + mFetcher->removeFromHTTPQueue(mID, 0); mFetcher->removeHttpWaiter(mID); mFetcher->updateStateStats(mCacheReadCount, mCacheWriteCount, mResourceWaitCount); } @@ -1875,7 +1875,7 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe } } - mFetcher->removeFromHTTPQueue(mID); + mFetcher->removeFromHTTPQueue(mID, data_size); recordTextureDone(true); } // -Mw diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 115e471bc9..95ec8c65c0 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -226,7 +226,7 @@ protected: void addToHTTPQueue(const LLUUID& id); // Threads: T* - void removeFromHTTPQueue(const LLUUID& id, S32 received_size = 0); + void removeFromHTTPQueue(const LLUUID& id, S32 received_size); // Identical to @deleteRequest but with different arguments // (caller already has the worker pointer). -- cgit v1.2.3 From d06739facfc13f5e7081783f85c1dd3576c3c155 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 17 Aug 2012 13:16:22 -0400 Subject: SH-3328 llcorehttp tracing control needed defensive logic for missing setting Guard for the typical case of a missing setting with a bracketing gSavedSettings.controlExists() call. --- indra/newview/llappcorehttp.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index a3cd0638d5..0d7d41304d 100644 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -85,10 +85,14 @@ void LLAppCoreHttp::init() // 1 - Basic start, stop simple transitions // 2 - libcurl CURLOPT_VERBOSE mode with brief lines // 3 - with partial data content - long trace_level(0L); - trace_level = long(gSavedSettings.getU32("QAModeHttpTrace")); - status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, trace_level); - + static const std::string http_trace("QAModeHttpTrace"); + if (gSavedSettings.controlExists(http_trace)) + { + long trace_level(0L); + trace_level = long(gSavedSettings.getU32(http_trace)); + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, trace_level); + } + // Setup default policy and constrain if directed to mPolicyDefault = LLCore::HttpRequest::DEFAULT_POLICY_ID; static const std::string texture_concur("TextureFetchConcurrency"); -- cgit v1.2.3 From 4b86f8983ad343b675e3f4960e91f0d4cb876dea Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Sun, 19 Aug 2012 18:51:21 -0400 Subject: SH-3329 Cached scene loads slower than same scene with cleared cache. A/B comparison with original code showed the newer issuing lower-priority requests of the cache reader and some other minor changes. Brought them into agreement (this is cargo-cult programming). Made the HTTP resource semaphore an atomic int for rigorous correctness across threads. I swear I'm going to tear down this code someday. --- indra/newview/lltexturefetch.cpp | 47 ++++++++++++++++++++++++++++------------ indra/newview/lltexturefetch.h | 4 ++-- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 718b15825d..6ae20edb9f 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -439,20 +439,27 @@ private: void lockWorkMutex() { mWorkMutex.lock(); } void unlockWorkMutex() { mWorkMutex.unlock(); } + // Threads: Ttf // Locks: Mw - void acquireHttpSemaphore() + bool acquireHttpSemaphore() { llassert(! mHttpHasResource); + if (mFetcher->mHttpSemaphore <= 0) + { + return false; + } mHttpHasResource = true; - --mFetcher->mHttpSemaphore; + mFetcher->mHttpSemaphore--; + return true; } + // Threads: Ttf // Locks: Mw void releaseHttpSemaphore() { llassert(mHttpHasResource); mHttpHasResource = false; - ++mFetcher->mHttpSemaphore; + mFetcher->mHttpSemaphore++; } private: @@ -901,6 +908,9 @@ LLTextureFetchWorker::~LLTextureFetchWorker() lockWorkMutex(); // +Mw (should be useless) if (mHttpHasResource) { + // Last-chance catchall to recover the resource. Using an + // atomic datatype solely because this can be running in + // another thread. releaseHttpSemaphore(); } if (mHttpActive) @@ -1351,7 +1361,7 @@ bool LLTextureFetchWorker::doWork(S32 param) // // If it looks like we're busy, keep this request here. // Otherwise, advance into the HTTP states. - if (mFetcher->mHttpSemaphore <= 0 || mFetcher->getHttpWaitersCount()) + if (mFetcher->getHttpWaitersCount() || ! acquireHttpSemaphore()) { mState = WAIT_HTTP_RESOURCE2; setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); @@ -1359,9 +1369,8 @@ bool LLTextureFetchWorker::doWork(S32 param) ++mResourceWaitCount; return false; } + mState = SEND_HTTP_REQ; - acquireHttpSemaphore(); - // *NOTE: You must invoke releaseHttpSemaphore() if you transition // to a state other than SEND_HTTP_REQ or WAIT_HTTP_REQ or abort // the request. @@ -1464,6 +1473,8 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == WAIT_HTTP_REQ) { + // *NOTE: As stated above, all transitions out of this state should + // call releaseHttpSemaphore(). if (mLoaded) { S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; @@ -1474,6 +1485,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if(mWriteToCacheState == NOT_WRITE) //map tiles { mState = DONE; + releaseHttpSemaphore(); return true; // failed, means no map tile on the empty region. } @@ -1678,7 +1690,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mAuxImage = NULL; llassert_always(mFormattedImage.notNull()); S32 discard = mHaveAllData ? 0 : mLoadedDiscard; - U32 image_priority = LLWorkerThread::PRIORITY_LOW | mWorkPriority; + U32 image_priority = LLWorkerThread::PRIORITY_NORMAL | mWorkPriority; mDecoded = FALSE; mState = DECODE_IMAGE_UPDATE; LL_DEBUGS("Texture") << mID << ": Decoding. Bytes: " << mFormattedImage->getDataSize() << " Discard: " << discard @@ -1789,7 +1801,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == DONE) { - if (mDecodedDiscard > 0 && mDesiredDiscard < mDecodedDiscard) + if (mDecodedDiscard >= 0 && mDesiredDiscard < mDecodedDiscard) { // More data was requested, return to INIT mState = INIT; @@ -3373,7 +3385,9 @@ void LLTextureFetch::removeHttpWaiter(const LLUUID & tid) // Locks: -Mw (must not hold any worker when called) void LLTextureFetch::releaseHttpWaiters() { - if (mHttpSemaphore < HTTP_REQUESTS_IN_QUEUE_LOW_WATER) + // Use mHttpSemaphore rather than mHTTPTextureQueue.size() + // to avoid a lock. + if (mHttpSemaphore < (HTTP_REQUESTS_IN_QUEUE_HIGH_WATER - HTTP_REQUESTS_IN_QUEUE_LOW_WATER)) return; // Quickly make a copy of all the LLUIDs. Get off the @@ -3425,22 +3439,27 @@ void LLTextureFetch::releaseHttpWaiters() // with other callers. Do defensive things like getting // refreshed counts of requests and checking if someone else // has moved any worker state around.... - for (worker_list_t::iterator iter2(tids2.begin()); - tids2.end() != iter2 && mHttpSemaphore > 0; - ++iter2) + for (worker_list_t::iterator iter2(tids2.begin()); tids2.end() != iter2; ++iter2) { LLTextureFetchWorker * worker(* iter2); worker->lockWorkMutex(); // +Mw if (LLTextureFetchWorker::WAIT_HTTP_RESOURCE2 != worker->mState) { + // Not in expected state, try the next one worker->unlockWorkMutex(); // -Mw continue; } + if (! worker->acquireHttpSemaphore()) + { + // Out of active slots, quit + worker->unlockWorkMutex(); // -Mw + break; + } + worker->mState = LLTextureFetchWorker::SEND_HTTP_REQ; worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); - worker->acquireHttpSemaphore(); worker->unlockWorkMutex(); // -Mw removeHttpWaiter(worker->mID); @@ -4154,7 +4173,7 @@ S32 LLTextureFetchDebugger::fillCurlQueue() mFetchingHistory[i].mCurlState = FetchEntry::CURL_IN_PROGRESS; mNbCurlRequests++; // Hack - if (mNbCurlRequests == 40) + if (mNbCurlRequests == HTTP_REQUESTS_IN_QUEUE_HIGH_WATER) // emulate normal pipeline break; } else diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 95ec8c65c0..4294209f04 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -355,10 +355,10 @@ private: // where it's more expensive to get at them. Requests in either // SEND_HTTP_REQ or WAIT_HTTP_REQ charge against the semaphore // and tracking state transitions is critical to liveness. - int mHttpSemaphore; // Ttf + LLAtomicS32 mHttpSemaphore; // Ttf + Tmain typedef std::set wait_http_res_queue_t; - wait_http_res_queue_t mHttpWaitResource; // Mfnq + wait_http_res_queue_t mHttpWaitResource; // Mfnq // Cumulative stats on the states/requests issued by // textures running through here. -- cgit v1.2.3 From 7bee4b58ff1e36ca39abc090991833c43c8903cc Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 21 Aug 2012 12:28:51 -0400 Subject: SH-3325 texture load slow on some machines This doesn't really address 3325 directly but it is the result of research done while hunting it down. First, this is a thread safety improvement for canceled requests that have gone into resource wait state. I don't think we've seen a failure there but there was a window. It also cleans the resource wait queue earlier which lets us do less work and get requests more quickly into llcorehttp by bypassing the resource wait state. With this, I finally feel comfortable about rundown of requests. --- indra/newview/lltexturefetch.cpp | 39 +++++++++++++++++++++++++++++++++------ indra/newview/lltexturefetch.h | 3 +++ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 6ae20edb9f..faaa9ed86b 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1945,10 +1945,14 @@ bool LLTextureFetchWorker::deleteOK() if (WAIT_HTTP_RESOURCE2 == mState) { - // Don't delete the worker out from under the - // releaseHttpWaiters() method. Keep the pointers - // valid, clean up after transition. - delete_ok = false; + if (mFetcher->isHttpWaiter(mID)) + { + // Don't delete the worker out from under the releaseHttpWaiters() + // method. Keep the pointers valid, clean up after that method + // has recognized the cancelation and removed the UUID from the + // waiter list. + delete_ok = false; + } } // Allow any pending reads or writes to complete @@ -2551,7 +2555,6 @@ void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) unlockQueue(); // -Mfq llassert_always(erased_1 > 0) ; - removeFromNetworkQueue(worker, cancel); llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ; @@ -3370,6 +3373,16 @@ void LLTextureFetch::removeHttpWaiter(const LLUUID & tid) mNetworkQueueMutex.unlock(); // -Mfnq } +// Threads: T* +bool LLTextureFetch::isHttpWaiter(const LLUUID & tid) +{ + mNetworkQueueMutex.lock(); // +Mfnq + wait_http_res_queue_t::iterator iter(mHttpWaitResource.find(tid)); + const bool ret(mHttpWaitResource.end() != iter); + mNetworkQueueMutex.unlock(); // -Mfnq + return ret; +} + // Release as many requests as permitted from the WAIT_HTTP_RESOURCE2 // state to the SEND_HTTP_REQ state based on their current priority. // @@ -3424,6 +3437,15 @@ void LLTextureFetch::releaseHttpWaiters() { tids2.push_back(worker); } + else + { + // If worker isn't found, this should be due to a request + // for deletion. We signal our recognition that this + // uuid shouldn't be used for resource waiting anymore by + // erasing it from the resource waiter list. That allows + // deleteOK to do final deletion on the worker. + removeHttpWaiter(* iter); + } } tids.clear(); @@ -3446,8 +3468,13 @@ void LLTextureFetch::releaseHttpWaiters() worker->lockWorkMutex(); // +Mw if (LLTextureFetchWorker::WAIT_HTTP_RESOURCE2 != worker->mState) { - // Not in expected state, try the next one + // Not in expected state, remove it, try the next one worker->unlockWorkMutex(); // -Mw + LL_WARNS("Texture") << "Resource-waited texture " << worker->mID + << " in unexpected state: " << worker->mState + << ". Removing from wait list." + << LL_ENDL; + removeHttpWaiter(worker->mID); continue; } diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 4294209f04..fd2223ecb0 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -185,6 +185,9 @@ public: // Threads: T* void removeHttpWaiter(const LLUUID & tid); + // Threads: T* + bool isHttpWaiter(const LLUUID & tid); + // If there are slots, release one or more LLTextureFetchWorker // requests from resource wait state (WAIT_HTTP_RESOURCE) to // active (SEND_HTTP_REQ). -- cgit v1.2.3 From 67553c99f6e0a3146741c448f6d20c90488b8d5b Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 21 Aug 2012 14:02:53 -0400 Subject: Pointless edit to tickle TC/codeticket. --- indra/newview/lltexturefetch.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index faaa9ed86b..422655879c 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -232,6 +232,7 @@ LLStat LLTextureFetch::sCacheReadLatency("texture_cache_read_latency", 128); // experiencial benefits promised. // + ////////////////////////////////////////////////////////////////////////////// // Tuning/Parameterization Constants -- cgit v1.2.3 From b974422a07a4a7c689fb4096c0e70fbfb4342785 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 22 Aug 2012 18:20:56 -0400 Subject: MAINT-1444: Make bottom-right corner of new login panel overlap. MAINT-1444 complains that with all optional login-panel controls enabled, the login panel is wider than 1024 pixels: at that width, the new "Start now" button is pushed completely off the right edge of the window. Richard told me how to tweak the layout so that the controls can squeeze together in an overlapping way, enabling all controls to be visible even at 1024 width. --- indra/newview/skins/default/xui/en/panel_login.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/indra/newview/skins/default/xui/en/panel_login.xml b/indra/newview/skins/default/xui/en/panel_login.xml index 9c96143aa3..6c4cbd4627 100644 --- a/indra/newview/skins/default/xui/en/panel_login.xml +++ b/indra/newview/skins/default/xui/en/panel_login.xml @@ -31,6 +31,7 @@ width="996"/> Date: Tue, 28 Aug 2012 13:52:14 -0500 Subject: MAINT-1491 Integration of statistically generated GPU table -- enable shadows by default where appropriate. --- indra/newview/featuretable.txt | 54 +- indra/newview/featuretable_linux.txt | 40 +- indra/newview/featuretable_mac.txt | 41 +- indra/newview/featuretable_xp.txt | 41 +- indra/newview/gpu_table.txt | 1012 +++++++++++++++++----------------- indra/newview/llfeaturemanager.cpp | 46 +- indra/newview/llfeaturemanager.h | 4 +- indra/newview/llviewerstats.cpp | 13 +- 8 files changed, 715 insertions(+), 536 deletions(-) diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt index eeb632acaf..e877e15053 100644 --- a/indra/newview/featuretable.txt +++ b/indra/newview/featuretable.txt @@ -1,4 +1,4 @@ -version 32 +version 33 // The version number above should be implemented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -98,9 +98,6 @@ RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 0 WindLightUseAtmosShaders 1 0 WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 -RenderShadowDetail 1 0 RenderFSAASamples 1 0 @@ -130,9 +127,6 @@ RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 0 WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 -RenderShadowDetail 1 0 RenderFSAASamples 1 0 // @@ -160,9 +154,6 @@ RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 0 WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 -RenderShadowDetail 1 0 RenderFSAASamples 1 0 // @@ -190,9 +181,6 @@ RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 1 WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 -RenderShadowDetail 1 0 RenderFSAASamples 1 2 // @@ -230,30 +218,66 @@ RenderFSAASamples 1 2 // list Unknown RenderVBOEnable 1 0 +RenderShadowDetail 1 0 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 // // Class 0 Hardware (just old) // list Class0 RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 // // Class 1 Hardware // list Class1 RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 + // -// Class 2 Hardware (make it purty) +// Class 2 Hardware // list Class2 RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 + // -// Class 3 Hardware (make it purty) +// Class 3 Hardware (deferred enabled) // list Class3 RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 1 +RenderDeferredSSAO 1 0 + +// +// Class 4 Hardware (deferred + SSAO) +// +list Class4 +RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 1 +RenderDeferredSSAO 1 1 + +// +// Class 5 Hardware (deferred + SSAO + shadows) +// +list Class5 +RenderVBOEnable 1 1 +RenderShadowDetail 1 2 +RenderDeferred 1 1 +RenderDeferredSSAO 1 1 + // // VRAM > 512MB diff --git a/indra/newview/featuretable_linux.txt b/indra/newview/featuretable_linux.txt index 3a0e7e3697..378e9650cf 100644 --- a/indra/newview/featuretable_linux.txt +++ b/indra/newview/featuretable_linux.txt @@ -1,4 +1,4 @@ -version 27 +version 28 // The version number above should be implemented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -226,31 +226,65 @@ RenderFSAASamples 1 2 // list Unknown RenderVBOEnable 1 0 +RenderShadowDetail 1 0 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 // // Class 0 Hardware (just old) // list Class0 RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 // // Class 1 Hardware // list Class1 RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 + // -// Class 2 Hardware (make it purty) +// Class 2 Hardware // list Class2 RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 + // -// Class 3 Hardware (make it purty) +// Class 3 Hardware (deferred enabled) // list Class3 RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 1 +RenderDeferredSSAO 1 0 + +// +// Class 4 Hardware (deferred + SSAO) +// +list Class4 +RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 1 +RenderDeferredSSAO 1 1 +// +// Class 5 Hardware (deferred + SSAO + shadows) +// +list Class5 +RenderVBOEnable 1 1 +RenderShadowDetail 1 2 +RenderDeferred 1 1 +RenderDeferredSSAO 1 1 // // VRAM > 512MB // diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt index 96362ff4bb..0f12769b38 100644 --- a/indra/newview/featuretable_mac.txt +++ b/indra/newview/featuretable_mac.txt @@ -1,4 +1,4 @@ -version 32 +version 34 // The version number above should be implemented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -228,30 +228,65 @@ RenderFSAASamples 1 2 // list Unknown RenderVBOEnable 1 0 +RenderShadowDetail 1 0 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 // // Class 0 Hardware (just old) // list Class0 RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 // // Class 1 Hardware // list Class1 RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 + // -// Class 2 Hardware (make it purty) +// Class 2 Hardware // list Class2 RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 + // -// Class 3 Hardware (make it purty) +// Class 3 Hardware (deferred enabled) // list Class3 RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 1 +RenderDeferredSSAO 1 0 + +// +// Class 4 Hardware (deferred + SSAO) +// +list Class4 +RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 1 +RenderDeferredSSAO 1 1 + +// +// Class 5 Hardware (deferred + SSAO + shadows) +// +list Class5 +RenderVBOEnable 1 1 +RenderShadowDetail 1 2 +RenderDeferred 1 1 +RenderDeferredSSAO 1 1 // // No Pixel Shaders available diff --git a/indra/newview/featuretable_xp.txt b/indra/newview/featuretable_xp.txt index a945f7a693..8c1dd80d07 100644 --- a/indra/newview/featuretable_xp.txt +++ b/indra/newview/featuretable_xp.txt @@ -1,4 +1,4 @@ -version 31 +version 32 // The version number above should be implemented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -228,30 +228,65 @@ RenderFSAASamples 1 2 // list Unknown RenderVBOEnable 1 0 +RenderShadowDetail 1 0 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 // // Class 0 Hardware (just old) // list Class0 RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 // // Class 1 Hardware // list Class1 RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 + // -// Class 2 Hardware (make it purty) +// Class 2 Hardware // list Class2 RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 + // -// Class 3 Hardware (make it purty) +// Class 3 Hardware (deferred enabled) // list Class3 RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 1 +RenderDeferredSSAO 1 0 + +// +// Class 4 Hardware (deferred + SSAO) +// +list Class4 +RenderVBOEnable 1 1 +RenderShadowDetail 1 0 +RenderDeferred 1 1 +RenderDeferredSSAO 1 1 + +// +// Class 5 Hardware (deferred + SSAO + shadows) +// +list Class5 +RenderVBOEnable 1 1 +RenderShadowDetail 1 2 +RenderDeferred 1 1 +RenderDeferredSSAO 1 1 // // VRAM > 512MB diff --git a/indra/newview/gpu_table.txt b/indra/newview/gpu_table.txt index 777d54a5c3..2a47646a4e 100644 --- a/indra/newview/gpu_table.txt +++ b/indra/newview/gpu_table.txt @@ -23,514 +23,516 @@ // 0 - Defaults to low graphics settings. No shaders on by default // 1 - Defaults to mid graphics settings. Basic shaders on by default // 2 - Defaults to high graphics settings. Atmospherics on by default. -// 3 - Same as class 2 for now. +// 3 - Same as 2, but with lighting and shadows enabled. +// 4 - Same as 3, but with ambient occlusion enabled. +// 5 - Same as 4, but with shadows set to "Sun/Moon+Projectors." // // Supported Number: // 0 - We claim to not support this card. // 1 - We claim to support this card. // -3Dfx .*3Dfx.* 0 0 -3Dlabs .*3Dlabs.* 0 0 -ATI 3D-Analyze .*ATI.*3D-Analyze.* 0 0 -ATI All-in-Wonder 7500 .*ATI.*All-in-Wonder 75.* 0 1 -ATI All-in-Wonder 8500 .*ATI.*All-in-Wonder 85.* 0 1 -ATI All-in-Wonder 9200 .*ATI.*All-in-Wonder 92.* 0 1 -ATI All-in-Wonder 9xxx .*ATI.*All-in-Wonder 9.* 1 1 -ATI All-in-Wonder HD .*ATI.*All-in-Wonder HD.* 1 1 -ATI All-in-Wonder X600 .*ATI.*All-in-Wonder X6.* 1 1 -ATI All-in-Wonder X800 .*ATI.*All-in-Wonder X8.* 2 1 -ATI All-in-Wonder X1800 .*ATI.*All-in-Wonder X18.* 3 1 -ATI All-in-Wonder X1900 .*ATI.*All-in-Wonder X19.* 3 1 -ATI All-in-Wonder PCI-E .*ATI.*All-in-Wonder.*PCI-E.* 1 1 -ATI All-in-Wonder Radeon .*ATI.*All-in-Wonder Radeon.* 0 1 -ATI ASUS ARES .*ATI.*ASUS.*ARES.* 3 1 -ATI ASUS A9xxx .*ATI.*ASUS.*A9.* 1 1 -ATI ASUS AH24xx .*ATI.*ASUS.*AH24.* 1 1 -ATI ASUS AH26xx .*ATI.*ASUS.*AH26.* 3 1 -ATI ASUS AH34xx .*ATI.*ASUS.*AH34.* 1 1 -ATI ASUS AH36xx .*ATI.*ASUS.*AH36.* 3 1 -ATI ASUS AH46xx .*ATI.*ASUS.*AH46.* 3 1 -ATI ASUS AX3xx .*ATI.*ASUS.*AX3.* 1 1 -ATI ASUS AX5xx .*ATI.*ASUS.*AX5.* 1 1 -ATI ASUS AX8xx .*ATI.*ASUS.*AX8.* 2 1 -ATI ASUS EAH24xx .*ATI.*ASUS.*EAH24.* 2 1 -ATI ASUS EAH26xx .*ATI.*ASUS.*EAH26.* 3 1 -ATI ASUS EAH29xx .*ATI.*ASUS.*EAH29.* 3 1 -ATI ASUS EAH34xx .*ATI.*ASUS.*EAH34.* 1 1 -ATI ASUS EAH36xx .*ATI.*ASUS.*EAH36.* 3 1 -ATI ASUS EAH38xx .*ATI.*ASUS.*EAH38.* 3 1 -ATI ASUS EAH43xx .*ATI.*ASUS.*EAH43.* 1 1 -ATI ASUS EAH45xx .*ATI.*ASUS.*EAH45.* 1 1 -ATI ASUS EAH48xx .*ATI.*ASUS.*EAH48.* 3 1 -ATI ASUS EAH57xx .*ATI.*ASUS.*EAH57.* 3 1 -ATI ASUS EAH58xx .*ATI.*ASUS.*EAH58.* 3 1 -ATI ASUS EAH6xxx .*ATI.*ASUS.*EAH6.* 3 1 -ATI ASUS Radeon X1xxx .*ATI.*ASUS.*X1.* 3 1 -ATI Radeon X7xx .*ATI.*ASUS.*X7.* 1 1 -ATI Radeon X19xx .*ATI.*(Radeon|Diamond) X19.* ?.* 3 1 -ATI Radeon X18xx .*ATI.*(Radeon|Diamond) X18.* ?.* 3 1 -ATI Radeon X17xx .*ATI.*(Radeon|Diamond) X17.* ?.* 2 1 -ATI Radeon X16xx .*ATI.*(Radeon|Diamond) X16.* ?.* 2 1 -ATI Radeon X15xx .*ATI.*(Radeon|Diamond) X15.* ?.* 2 1 -ATI Radeon X13xx .*ATI.*(Radeon|Diamond) X13.* ?.* 1 1 -ATI Radeon X1xxx .*ATI.*(Radeon|Diamond) X1.. ?.* 1 1 -ATI Radeon X2xxx .*ATI.*(Radeon|Diamond) X2.. ?.* 1 1 -ATI Display Adapter .*ATI.*display adapter.* 0 1 -ATI FireGL 5200 .*ATI.*FireGL V52.* 0 1 -ATI FireGL 5xxx .*ATI.*FireGL V5.* 1 1 -ATI FireGL .*ATI.*Fire.*GL.* 0 1 -ATI FirePro M3900 .*ATI.*FirePro.*M39.* 2 1 -ATI FirePro M5800 .*ATI.*FirePro.*M58.* 3 1 -ATI FirePro M7740 .*ATI.*FirePro.*M77.* 3 1 -ATI FirePro M7820 .*ATI.*FirePro.*M78.* 3 1 -ATI FireMV .*ATI.*FireMV.* 0 1 -ATI Geforce 9500 GT .*ATI.*Geforce 9500 *GT.* 2 1 -ATI Geforce 9600 GT .*ATI.*Geforce 9600 *GT.* 2 1 -ATI Geforce 9800 GT .*ATI.*Geforce 9800 *GT.* 2 1 -ATI Generic .*ATI.*Generic.* 0 0 -ATI Hercules 9800 .*ATI.*Hercules.*9800.* 1 1 -ATI IGP 340M .*ATI.*IGP.*340M.* 0 0 -ATI M52 .*ATI.*M52.* 1 1 -ATI M54 .*ATI.*M54.* 1 1 -ATI M56 .*ATI.*M56.* 1 1 -ATI M71 .*ATI.*M71.* 1 1 -ATI M72 .*ATI.*M72.* 1 1 -ATI M76 .*ATI.*M76.* 3 1 -ATI Radeon HD 64xx .*ATI.*AMD Radeon.* HD [67]4..[MG] 3 1 -ATI Radeon HD 65xx .*ATI.*AMD Radeon.* HD [67]5..[MG] 3 1 -ATI Radeon HD 66xx .*ATI.*AMD Radeon.* HD [67]6..[MG] 3 1 -ATI Mobility Radeon 4100 .*ATI.*Mobility.*41.. 1 1 -ATI Mobility Radeon 7xxx .*ATI.*Mobility.*Radeon 7.* 0 1 -ATI Mobility Radeon 8xxx .*ATI.*Mobility.*Radeon 8.* 0 1 -ATI Mobility Radeon 9800 .*ATI.*Mobility.*98.* 1 1 -ATI Mobility Radeon 9700 .*ATI.*Mobility.*97.* 1 1 -ATI Mobility Radeon 9600 .*ATI.*Mobility.*96.* 0 1 -ATI Mobility Radeon HD 530v .*ATI.*Mobility.*HD *530v.* 1 1 -ATI Mobility Radeon HD 540v .*ATI.*Mobility.*HD *540v.* 2 1 -ATI Mobility Radeon HD 545v .*ATI.*Mobility.*HD *545v.* 2 1 -ATI Mobility Radeon HD 550v .*ATI.*Mobility.*HD *550v.* 2 1 -ATI Mobility Radeon HD 560v .*ATI.*Mobility.*HD *560v.* 2 1 -ATI Mobility Radeon HD 565v .*ATI.*Mobility.*HD *565v.* 2 1 -ATI Mobility Radeon HD 2300 .*ATI.*Mobility.*HD *23.* 2 1 -ATI Mobility Radeon HD 2400 .*ATI.*Mobility.*HD *24.* 2 1 -ATI Mobility Radeon HD 2600 .*ATI.*Mobility.*HD *26.* 3 1 -ATI Mobility Radeon HD 2700 .*ATI.*Mobility.*HD *27.* 3 1 -ATI Mobility Radeon HD 3100 .*ATI.*Mobility.*HD *31.* 0 1 -ATI Mobility Radeon HD 3200 .*ATI.*Mobility.*HD *32.* 0 1 -ATI Mobility Radeon HD 3400 .*ATI.*Mobility.*HD *34.* 2 1 -ATI Mobility Radeon HD 3600 .*ATI.*Mobility.*HD *36.* 3 1 -ATI Mobility Radeon HD 3800 .*ATI.*Mobility.*HD *38.* 3 1 -ATI Mobility Radeon HD 4200 .*ATI.*Mobility.*HD *42.* 2 1 -ATI Mobility Radeon HD 4300 .*ATI.*Mobility.*HD *43.* 2 1 -ATI Mobility Radeon HD 4500 .*ATI.*Mobility.*HD *45.* 3 1 -ATI Mobility Radeon HD 4600 .*ATI.*Mobility.*HD *46.* 3 1 -ATI Mobility Radeon HD 4800 .*ATI.*Mobility.*HD *48.* 3 1 -ATI Mobility Radeon HD 5100 .*ATI.*Mobility.*HD *51.* 3 1 -ATI Mobility Radeon HD 5300 .*ATI.*Mobility.*HD *53.* 3 1 -ATI Mobility Radeon HD 5400 .*ATI.*Mobility.*HD *54.* 3 1 -ATI Mobility Radeon HD 5500 .*ATI.*Mobility.*HD *55.* 3 1 -ATI Mobility Radeon HD 5600 .*ATI.*Mobility.*HD *56.* 3 1 -ATI Mobility Radeon HD 5700 .*ATI.*Mobility.*HD *57.* 3 1 -ATI Mobility Radeon HD 6200 .*ATI.*Mobility.*HD *62.* 3 1 -ATI Mobility Radeon HD 6300 .*ATI.*Mobility.*HD *63.* 3 1 -ATI Mobility Radeon HD 6400M .*ATI.*Mobility.*HD *64.* 3 1 -ATI Mobility Radeon HD 6500M .*ATI.*Mobility.*HD *65.* 3 1 -ATI Mobility Radeon HD 6600M .*ATI.*Mobility.*HD *66.* 3 1 -ATI Mobility Radeon HD 6700M .*ATI.*Mobility.*HD *67.* 3 1 -ATI Mobility Radeon HD 6800M .*ATI.*Mobility.*HD *68.* 3 1 -ATI Mobility Radeon HD 6900M .*ATI.*Mobility.*HD *69.* 3 1 -ATI Radeon HD 2300 .*ATI.*Radeon HD *23.. 2 1 -ATI Radeon HD 2400 .*ATI.*Radeon HD *24.. 2 1 -ATI Radeon HD 2600 .*ATI.*Radeon HD *26.. 2 1 -ATI Radeon HD 2900 .*ATI.*Radeon HD *29.. 3 1 -ATI Radeon HD 3000 .*ATI.*Radeon HD *30.. 0 1 -ATI Radeon HD 3100 .*ATI.*Radeon HD *31.. 1 1 -ATI Radeon HD 3200 .*ATI.*Radeon HD *32.. 1 1 -ATI Radeon HD 3300 .*ATI.*Radeon HD *33.. 2 1 -ATI Radeon HD 3400 .*ATI.*Radeon HD *34.. 2 1 -ATI Radeon HD 3500 .*ATI.*Radeon HD *35.. 2 1 -ATI Radeon HD 3600 .*ATI.*Radeon HD *36.. 3 1 -ATI Radeon HD 3700 .*ATI.*Radeon HD *37.. 3 1 -ATI Radeon HD 3800 .*ATI.*Radeon HD *38.. 3 1 -ATI Radeon HD 4100 .*ATI.*Radeon HD *41.. 1 1 -ATI Radeon HD 4200 .*ATI.*Radeon HD *42.. 1 1 -ATI Radeon HD 4300 .*ATI.*Radeon HD *43.. 2 1 -ATI Radeon HD 4400 .*ATI.*Radeon HD *44.. 2 1 -ATI Radeon HD 4500 .*ATI.*Radeon HD *45.. 3 1 -ATI Radeon HD 4600 .*ATI.*Radeon HD *46.. 3 1 -ATI Radeon HD 4700 .*ATI.*Radeon HD *47.. 3 1 -ATI Radeon HD 4800 .*ATI.*Radeon HD *48.. 3 1 -ATI Radeon HD 5400 .*ATI.*Radeon HD *54.. 3 1 -ATI Radeon HD 5500 .*ATI.*Radeon HD *55.. 3 1 -ATI Radeon HD 5600 .*ATI.*Radeon HD *56.. 3 1 -ATI Radeon HD 5700 .*ATI.*Radeon HD *57.. 3 1 -ATI Radeon HD 5800 .*ATI.*Radeon HD *58.. 3 1 -ATI Radeon HD 5900 .*ATI.*Radeon HD *59.. 3 1 -ATI Radeon HD 6200 .*ATI.*Radeon HD *62.. 3 1 -ATI Radeon HD 6300 .*ATI.*Radeon HD *63.. 3 1 -ATI Radeon HD 6400 .*ATI.*Radeon HD *64.. 3 1 -ATI Radeon HD 6500 .*ATI.*Radeon HD *65.. 3 1 -ATI Radeon HD 6600 .*ATI.*Radeon HD *66.. 3 1 -ATI Radeon HD 6700 .*ATI.*Radeon HD *67.. 3 1 -ATI Radeon HD 6800 .*ATI.*Radeon HD *68.. 3 1 -ATI Radeon HD 6900 .*ATI.*Radeon HD *69.. 3 1 -ATI Radeon OpenGL .*ATI.*Radeon OpenGL.* 0 0 -ATI Radeon 2100 .*ATI.*Radeon 21.. 0 1 -ATI Radeon 3000 .*ATI.*Radeon 30.. 0 1 -ATI Radeon 3100 .*ATI.*Radeon 31.. 1 1 -ATI Radeon 5xxx .*ATI.*Radeon 5... 3 1 -ATI Radeon 7xxx .*ATI.*Radeon 7... 0 1 -ATI Radeon 8xxx .*ATI.*Radeon 8... 0 1 -ATI Radeon 9000 .*ATI.*Radeon 90.. 0 1 -ATI Radeon 9100 .*ATI.*Radeon 91.. 0 1 -ATI Radeon 9200 .*ATI.*Radeon 92.. 0 1 -ATI Radeon 9500 .*ATI.*Radeon 95.. 0 1 -ATI Radeon 9600 .*ATI.*Radeon 96.. 0 1 -ATI Radeon 9700 .*ATI.*Radeon 97.. 1 1 -ATI Radeon 9800 .*ATI.*Radeon 98.. 1 1 -ATI Radeon RV250 .*ATI.*RV250.* 0 1 -ATI Radeon RV600 .*ATI.*RV6.* 1 1 -ATI Radeon RX700 .*ATI.*RX70.* 1 1 -ATI Radeon RX800 .*ATI.*Radeon *RX80.* 2 1 -ATI RS880M .*ATI.*RS880M 1 1 -ATI Radeon RX9550 .*ATI.*RX9550.* 1 1 -ATI Radeon VE .*ATI.*Radeon.*VE.* 0 0 -ATI Radeon X300 .*ATI.*Radeon *X3.* 0 1 -ATI Radeon X400 .*ATI.*Radeon ?X4.* 0 1 -ATI Radeon X500 .*ATI.*Radeon ?X5.* 0 1 -ATI Radeon X600 .*ATI.*Radeon ?X6.* 1 1 -ATI Radeon X700 .*ATI.*Radeon ?X7.* 1 1 -ATI Radeon X800 .*ATI.*Radeon ?X8.* 2 1 -ATI Radeon X900 .*ATI.*Radeon ?X9.* 2 1 -ATI Radeon Xpress .*ATI.*Radeon Xpress.* 0 1 -ATI Rage 128 .*ATI.*Rage 128.* 0 1 -ATI R300 (9700) .*R300.* 1 1 -ATI R350 (9800) .*R350.* 1 1 -ATI R580 (X1900) .*R580.* 3 1 -ATI RC410 (Xpress 200) .*RC410.* 0 0 -ATI RS48x (Xpress 200x) .*RS48.* 0 0 -ATI RS600 (Xpress 3200) .*RS600.* 0 0 -ATI RV350 (9600) .*RV350.* 0 1 -ATI RV370 (X300) .*RV370.* 0 1 -ATI RV410 (X700) .*RV410.* 1 1 -ATI RV515 .*RV515.* 1 1 -ATI RV570 (X1900 GT/PRO) .*RV570.* 3 1 -ATI RV380 .*RV380.* 0 1 -ATI RV530 .*RV530.* 1 1 -ATI RX480 (Xpress 200P) .*RX480.* 0 1 -ATI RX700 .*RX700.* 1 1 -AMD ANTILLES (HD 6990) .*(AMD|ATI).*Antilles.* 3 1 -AMD BARTS (HD 6800) .*(AMD|ATI).*Barts.* 3 1 -AMD CAICOS (HD 6400) .*(AMD|ATI).*Caicos.* 3 1 -AMD CAYMAN (HD 6900) .*(AMD|ATI).*(Cayman|CAYMAM).* 3 1 -AMD CEDAR (HD 5450) .*(AMD|ATI).*Cedar.* 2 1 -AMD CYPRESS (HD 5800) .*(AMD|ATI).*Cypress.* 3 1 -AMD HEMLOCK (HD 5970) .*(AMD|ATI).*Hemlock.* 3 1 -AMD JUNIPER (HD 5700) .*(AMD|ATI).*Juniper.* 3 1 -AMD PARK .*(AMD|ATI).*Park.* 3 1 -AMD REDWOOD (HD 5500/5600) .*(AMD|ATI).*Redwood.* 3 1 -AMD TURKS (HD 6500/6600) .*(AMD|ATI).*Turks.* 3 1 -AMD RS780 (HD 3200) .*RS780.* 0 1 -AMD RS880 (HD 4200) .*RS880.* 1 1 -AMD RV610 (HD 2400) .*RV610.* 1 1 -AMD RV620 (HD 3400) .*RV620.* 1 1 -AMD RV630 (HD 2600) .*RV630.* 2 1 -AMD RV635 (HD 3600) .*RV635.* 3 1 -AMD RV670 (HD 3800) .*RV670.* 3 1 -AMD R680 (HD 3870 X2) .*R680.* 3 1 -AMD R700 (HD 4800 X2) .*R700.* 3 1 -AMD RV710 (HD 4300) .*RV710.* 1 1 -AMD RV730 (HD 4600) .*RV730.* 3 1 -AMD RV740 (HD 4700) .*RV740.* 3 1 -AMD RV770 (HD 4800) .*RV770.* 3 1 -AMD RV790 (HD 4800) .*RV790.* 3 1 -ATI 760G/Radeon 3000 .*ATI.*AMD 760G.* 1 1 -ATI 780L/Radeon 3000 .*ATI.*AMD 780L.* 1 1 -ATI Radeon DDR .*ATI.*Radeon ?DDR.* 0 1 -ATI FirePro 2000 .*ATI.*FirePro 2.* 1 1 -ATI FirePro 3000 .*ATI.*FirePro V3.* 1 1 -ATI FirePro 4000 .*ATI.*FirePro V4.* 2 1 -ATI FirePro 5000 .*ATI.*FirePro V5.* 3 1 -ATI FirePro 7000 .*ATI.*FirePro V7.* 3 1 -ATI FirePro M .*ATI.*FirePro M.* 3 1 -ATI Technologies .*ATI *Technologies.* 0 1 -// This entry is last to work around the "R300" driver problem. -ATI R300 (9700) .*R300.* 1 1 -ATI Radeon .*ATI.*(Diamond|Radeon).* 0 1 -Intel X3100 .*Intel.*X3100.* 0 1 -Intel 830M .*Intel.*830M 0 0 -Intel 845G .*Intel.*845G 0 0 -Intel 855GM .*Intel.*855GM 0 0 -Intel 865G .*Intel.*865G 0 0 -Intel 900 .*Intel.*900.*900 0 0 -Intel 915GM .*Intel.*915GM 0 0 -Intel 915G .*Intel.*915G 0 0 -Intel 945GM .*Intel.*945GM.* 0 1 -Intel 945G .*Intel.*945G.* 0 1 -Intel 950 .*Intel.*950.* 0 1 -Intel 965 .*Intel.*965.* 0 1 -Intel G33 .*Intel.*G33.* 0 0 -Intel G41 .*Intel.*G41.* 0 1 -Intel G45 .*Intel.*G45.* 0 1 -Intel Bear Lake .*Intel.*Bear Lake.* 0 0 -Intel Broadwater .*Intel.*Broadwater.* 0 0 -Intel Brookdale .*Intel.*Brookdale.* 0 0 -Intel Cantiga .*Intel.*Cantiga.* 0 0 -Intel Eaglelake .*Intel.*Eaglelake.* 0 1 -Intel Graphics Media HD .*Intel.*Graphics Media.*HD.* 0 1 -Intel HD Graphics .*Intel.*HD Graphics.* 2 1 -Intel Mobile 4 Series .*Intel.*Mobile.* 4 Series.* 0 1 -Intel Media Graphics HD .*Intel.*Media Graphics HD.* 0 1 -Intel Montara .*Intel.*Montara.* 0 0 -Intel Pineview .*Intel.*Pineview.* 0 1 -Intel Springdale .*Intel.*Springdale.* 0 0 -Intel HD Graphics 2000 .*Intel.*HD2000.* 1 1 -Intel HD Graphics 3000 .*Intel.*HD3000.* 2 1 -Matrox .*Matrox.* 0 0 -Mesa .*Mesa.* 0 0 -NVIDIA 205 .*NVIDIA .*GeForce 205.* 2 1 -NVIDIA 210 .*NVIDIA .*GeForce 210.* 2 1 -NVIDIA 310M .*NVIDIA .*GeForce 310M.* 1 1 -NVIDIA 310 .*NVIDIA .*GeForce 310.* 3 1 -NVIDIA 315M .*NVIDIA .*GeForce 315M.* 2 1 -NVIDIA 315 .*NVIDIA .*GeForce 315.* 3 1 -NVIDIA 320M .*NVIDIA .*GeForce 320M.* 2 1 -NVIDIA G100M .*NVIDIA .*100M.* 0 1 -NVIDIA G100 .*NVIDIA .*100.* 0 1 -NVIDIA G102M .*NVIDIA .*102M.* 0 1 -NVIDIA G103M .*NVIDIA .*103M.* 0 1 -NVIDIA G105M .*NVIDIA .*105M.* 0 1 -NVIDIA G 110M .*NVIDIA .*110M.* 0 1 -NVIDIA G 120M .*NVIDIA .*120M.* 1 1 -NVIDIA G 200 .*NVIDIA .*200(M)?.* 0 1 -NVIDIA G 205M .*NVIDIA .*205(M)?.* 0 1 -NVIDIA G 210 .*NVIDIA .*210(M)?.* 1 1 -NVIDIA 305M .*NVIDIA .*305(M)?.* 1 1 -NVIDIA G 310M .*NVIDIA .*310(M)?.* 2 1 -NVIDIA G 315 .*NVIDIA .*315(M)?.* 2 1 -NVIDIA G 320M .*NVIDIA .*320(M)?.* 2 1 -NVIDIA G 405 .*NVIDIA .*405(M)?.* 1 1 -NVIDIA G 410M .*NVIDIA .*410(M)?.* 1 1 -NVIDIA GT 120M .*NVIDIA .*GT *120(M)?.* 2 1 -NVIDIA GT 120 .*NVIDIA .*GT.*120 2 1 -NVIDIA GT 130M .*NVIDIA .*GT *130(M)?.* 2 1 -NVIDIA GT 140M .*NVIDIA .*GT *140(M)?.* 2 1 -NVIDIA GT 150M .*NVIDIA .*GT(S)? *150(M)?.* 2 1 -NVIDIA GT 160M .*NVIDIA .*GT *160(M)?.* 2 1 -NVIDIA GT 220M .*NVIDIA .*GT *220(M)?.* 2 1 -NVIDIA GT 230M .*NVIDIA .*GT *230(M)?.* 2 1 -NVIDIA GT 240M .*NVIDIA .*GT *240(M)?.* 2 1 -NVIDIA GT 250M .*NVIDIA .*GT *250(M)?.* 2 1 -NVIDIA GT 260M .*NVIDIA .*GT *260(M)?.* 2 1 -NVIDIA GT 320M .*NVIDIA .*GT *320(M)?.* 2 1 -NVIDIA GT 325M .*NVIDIA .*GT *325(M)?.* 0 1 -NVIDIA GT 330M .*NVIDIA .*GT *330(M)?.* 3 1 -NVIDIA GT 335M .*NVIDIA .*GT *335(M)?.* 1 1 -NVIDIA GT 340M .*NVIDIA .*GT *340(M)?.* 2 1 -NVIDIA GT 415M .*NVIDIA .*GT *415(M)?.* 2 1 -NVIDIA GT 420M .*NVIDIA .*GT *420(M)?.* 2 1 -NVIDIA GT 425M .*NVIDIA .*GT *425(M)?.* 3 1 -NVIDIA GT 430M .*NVIDIA .*GT *430(M)?.* 3 1 -NVIDIA GT 435M .*NVIDIA .*GT *435(M)?.* 3 1 -NVIDIA GT 440M .*NVIDIA .*GT *440(M)?.* 3 1 -NVIDIA GT 445M .*NVIDIA .*GT *445(M)?.* 3 1 -NVIDIA GT 450M .*NVIDIA .*GT *450(M)?.* 3 1 -NVIDIA GT 520M .*NVIDIA .*GT *52.(M)?.* 3 1 -NVIDIA GT 530M .*NVIDIA .*GT *530(M)?.* 3 1 -NVIDIA GT 540M .*NVIDIA .*GT *54.(M)?.* 3 1 -NVIDIA GT 550M .*NVIDIA .*GT *550(M)?.* 3 1 -NVIDIA GT 555M .*NVIDIA .*GT *555(M)?.* 3 1 -NVIDIA GTS 160M .*NVIDIA .*GT(S)? *160(M)?.* 2 1 -NVIDIA GTS 240 .*NVIDIA .*GTS *24.* 3 1 -NVIDIA GTS 250 .*NVIDIA .*GTS *25.* 3 1 -NVIDIA GTS 350M .*NVIDIA .*GTS *350M.* 3 1 -NVIDIA GTS 360M .*NVIDIA .*GTS *360M.* 3 1 -NVIDIA GTS 360 .*NVIDIA .*GTS *360.* 3 1 -NVIDIA GTS 450 .*NVIDIA .*GTS *45.* 3 1 -NVIDIA GTX 260 .*NVIDIA .*GTX *26.* 3 1 -NVIDIA GTX 275 .*NVIDIA .*GTX *275.* 3 1 -NVIDIA GTX 270 .*NVIDIA .*GTX *27.* 3 1 -NVIDIA GTX 285 .*NVIDIA .*GTX *285.* 3 1 -NVIDIA GTX 280 .*NVIDIA .*GTX *280.* 3 1 -NVIDIA GTX 290 .*NVIDIA .*GTX *290.* 3 1 -NVIDIA GTX 295 .*NVIDIA .*GTX *295.* 3 1 -NVIDIA GTX 460M .*NVIDIA .*GTX *460M.* 3 1 -NVIDIA GTX 465 .*NVIDIA .*GTX *465.* 3 1 -NVIDIA GTX 460 .*NVIDIA .*GTX *46.* 3 1 -NVIDIA GTX 470M .*NVIDIA .*GTX *470M.* 3 1 -NVIDIA GTX 470 .*NVIDIA .*GTX *47.* 3 1 -NVIDIA GTX 480M .*NVIDIA .*GTX *480M.* 3 1 -NVIDIA GTX 485M .*NVIDIA .*GTX *485M.* 3 1 -NVIDIA GTX 480 .*NVIDIA .*GTX *48.* 3 1 -NVIDIA GTX 530 .*NVIDIA .*GTX *53.* 3 1 -NVIDIA GTX 550 .*NVIDIA .*GTX *55.* 3 1 -NVIDIA GTX 560 .*NVIDIA .*GTX *56.* 3 1 -NVIDIA GTX 570 .*NVIDIA .*GTX *57.* 3 1 -NVIDIA GTX 580M .*NVIDIA .*GTX *580M.* 3 1 -NVIDIA GTX 580 .*NVIDIA .*GTX *58.* 3 1 -NVIDIA GTX 590 .*NVIDIA .*GTX *59.* 3 1 -NVIDIA C51 .*NVIDIA .*C51.* 0 1 -NVIDIA G72 .*NVIDIA .*G72.* 1 1 -NVIDIA G73 .*NVIDIA .*G73.* 1 1 -NVIDIA G84 .*NVIDIA .*G84.* 2 1 -NVIDIA G86 .*NVIDIA .*G86.* 3 1 -NVIDIA G92 .*NVIDIA .*G92.* 3 1 -NVIDIA GeForce .*GeForce 256.* 0 0 -NVIDIA GeForce 2 .*GeForce ?2 ?.* 0 1 -NVIDIA GeForce 3 .*GeForce ?3 ?.* 0 1 -NVIDIA GeForce 3 Ti .*GeForce ?3 Ti.* 0 1 -NVIDIA GeForce 4 .*NVIDIA .*GeForce ?4.* 0 1 -NVIDIA GeForce 4 Go .*NVIDIA .*GeForce ?4.*Go.* 0 1 -NVIDIA GeForce 4 MX .*NVIDIA .*GeForce ?4 MX.* 0 1 -NVIDIA GeForce 4 PCX .*NVIDIA .*GeForce ?4 PCX.* 0 1 -NVIDIA GeForce 4 Ti .*NVIDIA .*GeForce ?4 Ti.* 0 1 -NVIDIA GeForce 6100 .*NVIDIA .*GeForce 61.* 0 1 -NVIDIA GeForce 6200 .*NVIDIA .*GeForce 62.* 0 1 -NVIDIA GeForce 6500 .*NVIDIA .*GeForce 65.* 0 1 -NVIDIA GeForce 6600 .*NVIDIA .*GeForce 66.* 1 1 -NVIDIA GeForce 6700 .*NVIDIA .*GeForce 67.* 2 1 -NVIDIA GeForce 6800 .*NVIDIA .*GeForce 68.* 2 1 -NVIDIA GeForce 7000 .*NVIDIA .*GeForce 70.* 0 1 -NVIDIA GeForce 7100 .*NVIDIA .*GeForce 71.* 0 1 -NVIDIA GeForce 7200 .*NVIDIA .*GeForce 72.* 1 1 -NVIDIA GeForce 7300 .*NVIDIA .*GeForce 73.* 1 1 -NVIDIA GeForce 7500 .*NVIDIA .*GeForce 75.* 1 1 -NVIDIA GeForce 7600 .*NVIDIA .*GeForce 76.* 2 1 -NVIDIA GeForce 7800 .*NVIDIA .*GeForce 78.* 2 1 -NVIDIA GeForce 7900 .*NVIDIA .*GeForce 79.* 2 1 -NVIDIA GeForce 8100 .*NVIDIA .*GeForce 81.* 1 1 -NVIDIA GeForce 8200M .*NVIDIA .*GeForce 8200M.* 1 1 -NVIDIA GeForce 8200 .*NVIDIA .*GeForce 82.* 1 1 -NVIDIA GeForce 8300 .*NVIDIA .*GeForce 83.* 2 1 -NVIDIA GeForce 8400M .*NVIDIA .*GeForce 8400M.* 2 1 -NVIDIA GeForce 8400 .*NVIDIA .*GeForce 84.* 2 1 -NVIDIA GeForce 8500 .*NVIDIA .*GeForce 85.* 3 1 -NVIDIA GeForce 8600M .*NVIDIA .*GeForce 8600M.* 2 1 -NVIDIA GeForce 8600 .*NVIDIA .*GeForce 86.* 3 1 -NVIDIA GeForce 8700M .*NVIDIA .*GeForce 8700M.* 3 1 -NVIDIA GeForce 8700 .*NVIDIA .*GeForce 87.* 3 1 -NVIDIA GeForce 8800M .*NVIDIA .*GeForce 8800M.* 3 1 -NVIDIA GeForce 8800 .*NVIDIA .*GeForce 88.* 3 1 -NVIDIA GeForce 9100M .*NVIDIA .*GeForce 9100M.* 0 1 -NVIDIA GeForce 9100 .*NVIDIA .*GeForce 91.* 0 1 -NVIDIA GeForce 9200M .*NVIDIA .*GeForce 9200M.* 1 1 -NVIDIA GeForce 9200 .*NVIDIA .*GeForce 92.* 1 1 -NVIDIA GeForce 9300M .*NVIDIA .*GeForce 9300M.* 2 1 -NVIDIA GeForce 9300 .*NVIDIA .*GeForce 93.* 2 1 -NVIDIA GeForce 9400M .*NVIDIA .*GeForce 9400M.* 2 1 -NVIDIA GeForce 9400 .*NVIDIA .*GeForce 94.* 2 1 -NVIDIA GeForce 9500M .*NVIDIA .*GeForce 9500M.* 2 1 -NVIDIA GeForce 9500 .*NVIDIA .*GeForce 95.* 2 1 -NVIDIA GeForce 9600M .*NVIDIA .*GeForce 9600M.* 3 1 -NVIDIA GeForce 9600 .*NVIDIA .*GeForce 96.* 2 1 -NVIDIA GeForce 9700M .*NVIDIA .*GeForce 9700M.* 2 1 -NVIDIA GeForce 9800M .*NVIDIA .*GeForce 9800M.* 3 1 -NVIDIA GeForce 9800 .*NVIDIA .*GeForce 98.* 3 1 -NVIDIA GeForce FX 5100 .*NVIDIA .*GeForce FX 51.* 0 1 -NVIDIA GeForce FX 5200 .*NVIDIA .*GeForce FX 52.* 0 1 -NVIDIA GeForce FX 5300 .*NVIDIA .*GeForce FX 53.* 0 1 -NVIDIA GeForce FX 5500 .*NVIDIA .*GeForce FX 55.* 0 1 -NVIDIA GeForce FX 5600 .*NVIDIA .*GeForce FX 56.* 0 1 -NVIDIA GeForce FX 5700 .*NVIDIA .*GeForce FX 57.* 1 1 -NVIDIA GeForce FX 5800 .*NVIDIA .*GeForce FX 58.* 1 1 -NVIDIA GeForce FX 5900 .*NVIDIA .*GeForce FX 59.* 1 1 -NVIDIA GeForce FX Go5100 .*NVIDIA .*GeForce FX Go51.* 0 1 -NVIDIA GeForce FX Go5200 .*NVIDIA .*GeForce FX Go52.* 0 1 -NVIDIA GeForce FX Go5300 .*NVIDIA .*GeForce FX Go53.* 0 1 -NVIDIA GeForce FX Go5500 .*NVIDIA .*GeForce FX Go55.* 0 1 -NVIDIA GeForce FX Go5600 .*NVIDIA .*GeForce FX Go56.* 0 1 -NVIDIA GeForce FX Go5700 .*NVIDIA .*GeForce FX Go57.* 1 1 -NVIDIA GeForce FX Go5800 .*NVIDIA .*GeForce FX Go58.* 1 1 -NVIDIA GeForce FX Go5900 .*NVIDIA .*GeForce FX Go59.* 1 1 -NVIDIA GeForce FX Go5xxx .*NVIDIA .*GeForce FX Go.* 0 1 -NVIDIA GeForce Go 6100 .*NVIDIA .*GeForce Go 61.* 0 1 -NVIDIA GeForce Go 6200 .*NVIDIA .*GeForce Go 62.* 0 1 -NVIDIA GeForce Go 6400 .*NVIDIA .*GeForce Go 64.* 1 1 -NVIDIA GeForce Go 6500 .*NVIDIA .*GeForce Go 65.* 1 1 -NVIDIA GeForce Go 6600 .*NVIDIA .*GeForce Go 66.* 1 1 -NVIDIA GeForce Go 6700 .*NVIDIA .*GeForce Go 67.* 1 1 -NVIDIA GeForce Go 6800 .*NVIDIA .*GeForce Go 68.* 1 1 -NVIDIA GeForce Go 7200 .*NVIDIA .*GeForce Go 72.* 1 1 -NVIDIA GeForce Go 7300 LE .*NVIDIA .*GeForce Go 73.*LE.* 0 1 -NVIDIA GeForce Go 7300 .*NVIDIA .*GeForce Go 73.* 1 1 -NVIDIA GeForce Go 7400 .*NVIDIA .*GeForce Go 74.* 1 1 -NVIDIA GeForce Go 7600 .*NVIDIA .*GeForce Go 76.* 2 1 -NVIDIA GeForce Go 7700 .*NVIDIA .*GeForce Go 77.* 2 1 -NVIDIA GeForce Go 7800 .*NVIDIA .*GeForce Go 78.* 2 1 -NVIDIA GeForce Go 7900 .*NVIDIA .*GeForce Go 79.* 2 1 -NVIDIA D9M .*NVIDIA .*D9M.* 1 1 -NVIDIA G94 .*NVIDIA .*G94.* 3 1 -NVIDIA GeForce Go 6 .*GeForce Go 6.* 1 1 -NVIDIA ION 2 .*NVIDIA .*ION 2.* 2 1 -NVIDIA ION .*NVIDIA .*ION.* 2 1 -NVIDIA NB8M .*NVIDIA .*NB8M.* 1 1 -NVIDIA NB8P .*NVIDIA .*NB8P.* 2 1 -NVIDIA NB9E .*NVIDIA .*NB9E.* 3 1 -NVIDIA NB9M .*NVIDIA .*NB9M.* 1 1 -NVIDIA NB9P .*NVIDIA .*NB9P.* 2 1 -NVIDIA N10 .*NVIDIA .*N10.* 1 1 -NVIDIA GeForce PCX .*GeForce PCX.* 0 1 -NVIDIA Generic .*NVIDIA .*Unknown.* 0 0 -NVIDIA NV17 .*NVIDIA .*NV17.* 0 1 -NVIDIA NV34 .*NVIDIA .*NV34.* 0 1 -NVIDIA NV35 .*NVIDIA .*NV35.* 0 1 -NVIDIA NV36 .*NVIDIA .*NV36.* 1 1 -NVIDIA NV41 .*NVIDIA .*NV41.* 1 1 -NVIDIA NV43 .*NVIDIA .*NV43.* 1 1 -NVIDIA NV44 .*NVIDIA .*NV44.* 1 1 -NVIDIA nForce .*NVIDIA .*nForce.* 0 0 -NVIDIA MCP51 .*NVIDIA .*MCP51.* 1 1 -NVIDIA MCP61 .*NVIDIA .*MCP61.* 1 1 -NVIDIA MCP67 .*NVIDIA .*MCP67.* 1 1 -NVIDIA MCP68 .*NVIDIA .*MCP68.* 1 1 -NVIDIA MCP73 .*NVIDIA .*MCP73.* 1 1 -NVIDIA MCP77 .*NVIDIA .*MCP77.* 1 1 -NVIDIA MCP78 .*NVIDIA .*MCP78.* 1 1 -NVIDIA MCP79 .*NVIDIA .*MCP79.* 1 1 -NVIDIA MCP7A .*NVIDIA .*MCP7A.* 1 1 -NVIDIA Quadro2 .*Quadro2.* 0 1 -NVIDIA Quadro 1000M .*Quadro.*1000M.* 2 1 -NVIDIA Quadro 2000 M/D .*Quadro.*2000.* 3 1 -NVIDIA Quadro 3000M .*Quadro.*3000M.* 3 1 -NVIDIA Quadro 4000M .*Quadro.*4000M.* 3 1 -NVIDIA Quadro 4000 .*Quadro *4000.* 3 1 -NVIDIA Quadro 50x0 M .*Quadro.*50.0.* 3 1 -NVIDIA Quadro 6000 .*Quadro.*6000.* 3 1 -NVIDIA Quadro 400 .*Quadro.*400.* 2 1 -NVIDIA Quadro 600 .*Quadro.*600.* 2 1 -NVIDIA Quadro4 .*Quadro4.* 0 1 -NVIDIA Quadro DCC .*Quadro DCC.* 0 1 -NVIDIA Quadro CX .*Quadro.*CX.* 3 1 -NVIDIA Quadro FX 770M .*Quadro.*FX *770M.* 2 1 -NVIDIA Quadro FX 1500M .*Quadro.*FX *1500M.* 1 1 -NVIDIA Quadro FX 1600M .*Quadro.*FX *1600M.* 2 1 -NVIDIA Quadro FX 2500M .*Quadro.*FX *2500M.* 2 1 -NVIDIA Quadro FX 2700M .*Quadro.*FX *2700M.* 3 1 -NVIDIA Quadro FX 2800M .*Quadro.*FX *2800M.* 3 1 -NVIDIA Quadro FX 3500 .*Quadro.*FX *3500.* 2 1 -NVIDIA Quadro FX 3600 .*Quadro.*FX *3600.* 3 1 -NVIDIA Quadro FX 3700 .*Quadro.*FX *3700.* 3 1 -NVIDIA Quadro FX 3800 .*Quadro.*FX *3800.* 3 1 -NVIDIA Quadro FX 4500 .*Quadro.*FX *45.* 3 1 -NVIDIA Quadro FX 880M .*Quadro.*FX *880M.* 3 1 -NVIDIA Quadro FX 4800 .*NVIDIA .*Quadro *FX *4800.* 3 1 -NVIDIA Quadro FX .*Quadro FX.* 1 1 -NVIDIA Quadro NVS 1xxM .*Quadro NVS *1.[05]M.* 0 1 -NVIDIA Quadro NVS 300M .*NVIDIA .*NVS *300M.* 2 1 -NVIDIA Quadro NVS 320M .*NVIDIA .*NVS *320M.* 2 1 -NVIDIA Quadro NVS 2100M .*NVIDIA .*NVS *2100M.* 2 1 -NVIDIA Quadro NVS 3100M .*NVIDIA .*NVS *3100M.* 2 1 -NVIDIA Quadro NVS 4200M .*NVIDIA .*NVS *4200M.* 2 1 -NVIDIA Quadro NVS 5100M .*NVIDIA .*NVS *5100M.* 2 1 -NVIDIA Quadro NVS .*NVIDIA .*NVS 0 1 -NVIDIA RIVA TNT .*RIVA TNT.* 0 0 -S3 .*S3 Graphics.* 0 0 -SiS SiS.* 0 0 -Trident Trident.* 0 0 -Tungsten Graphics Tungsten.* 0 0 -XGI XGI.* 0 0 -VIA VIA.* 0 0 -Apple Generic Apple.*Generic.* 0 0 -Apple Software Renderer Apple.*Software Renderer.* 0 0 -Humper Humper.* 0 1 +3Dfx .*3Dfx.* 0 0 +3Dlabs .*3Dlabs.* 0 0 +ATI 3D-Analyze .*ATI.*3D-Analyze.* 0 0 +ATI All-in-Wonder 7500 .*ATI.*All-in-Wonder 75.* 0 1 +ATI All-in-Wonder 8500 .*ATI.*All-in-Wonder 85.* 0 1 +ATI All-in-Wonder 9200 .*ATI.*All-in-Wonder 92.* 0 1 +ATI All-in-Wonder 9xxx .*ATI.*All-in-Wonder 9.* 1 1 +ATI All-in-Wonder HD .*ATI.*All-in-Wonder HD.* 1 1 +ATI All-in-Wonder X600 .*ATI.*All-in-Wonder X6.* 1 1 +ATI All-in-Wonder X800 .*ATI.*All-in-Wonder X8.* 2 1 +ATI All-in-Wonder X1800 .*ATI.*All-in-Wonder X18.* 3 1 +ATI All-in-Wonder X1900 .*ATI.*All-in-Wonder X19.* 3 1 +ATI All-in-Wonder PCI-E .*ATI.*All-in-Wonder.*PCI-E.* 1 1 +ATI All-in-Wonder Radeon .*ATI.*All-in-Wonder Radeon.* 0 1 +ATI ASUS ARES .*ATI.*ASUS.*ARES.* 3 1 +ATI ASUS A9xxx .*ATI.*ASUS.*A9.* 1 1 +ATI ASUS AH24xx .*ATI.*ASUS.*AH24.* 1 1 +ATI ASUS AH26xx .*ATI.*ASUS.*AH26.* 3 1 +ATI ASUS AH34xx .*ATI.*ASUS.*AH34.* 1 1 +ATI ASUS AH36xx .*ATI.*ASUS.*AH36.* 3 1 +ATI ASUS AH46xx .*ATI.*ASUS.*AH46.* 3 1 +ATI ASUS AX3xx .*ATI.*ASUS.*AX3.* 1 1 +ATI ASUS AX5xx .*ATI.*ASUS.*AX5.* 1 1 +ATI ASUS AX8xx .*ATI.*ASUS.*AX8.* 2 1 +ATI ASUS EAH24xx .*ATI.*ASUS.*EAH24.* 2 1 +ATI ASUS EAH26xx .*ATI.*ASUS.*EAH26.* 3 1 +ATI ASUS EAH29xx .*ATI.*ASUS.*EAH29.* 3 1 +ATI ASUS EAH34xx .*ATI.*ASUS.*EAH34.* 1 1 +ATI ASUS EAH36xx .*ATI.*ASUS.*EAH36.* 3 1 +ATI ASUS EAH38xx .*ATI.*ASUS.*EAH38.* 3 1 +ATI ASUS EAH43xx .*ATI.*ASUS.*EAH43.* 1 1 +ATI ASUS EAH45xx .*ATI.*ASUS.*EAH45.* 1 1 +ATI ASUS EAH48xx .*ATI.*ASUS.*EAH48.* 3 1 +ATI ASUS EAH57xx .*ATI.*ASUS.*EAH57.* 3 1 +ATI ASUS EAH58xx .*ATI.*ASUS.*EAH58.* 3 1 +ATI ASUS EAH6xxx .*ATI.*ASUS.*EAH6.* 3 1 +ATI ASUS Radeon X1xxx .*ATI.*ASUS.*X1.* 3 1 +ATI Radeon X7xx .*ATI.*ASUS.*X7.* 1 1 +ATI Radeon X19xx .*ATI.*(Radeon|Diamond) X19.* ?.* 2 1 +ATI Radeon X18xx .*ATI.*(Radeon|Diamond) X18.* ?.* 3 1 +ATI Radeon X17xx .*ATI.*(Radeon|Diamond) X17.* ?.* 2 1 +ATI Radeon X16xx .*ATI.*(Radeon|Diamond) X16.* ?.* 1 1 +ATI Radeon X15xx .*ATI.*(Radeon|Diamond) X15.* ?.* 2 1 +ATI Radeon X13xx .*ATI.*(Radeon|Diamond) X13.* ?.* 1 1 +ATI Radeon X1xxx .*ATI.*(Radeon|Diamond) X1.. ?.* 1 1 +ATI Radeon X2xxx .*ATI.*(Radeon|Diamond) X2.. ?.* 1 1 +ATI Display Adapter .*ATI.*display adapter.* 0 1 +ATI FireGL 5200 .*ATI.*FireGL V52.* 0 1 +ATI FireGL 5xxx .*ATI.*FireGL V5.* 1 1 +ATI FireGL .*ATI.*Fire.*GL.* 0 1 +ATI FirePro M3900 .*ATI.*FirePro.*M39.* 2 1 +ATI FirePro M5800 .*ATI.*FirePro.*M58.* 3 1 +ATI FirePro M7740 .*ATI.*FirePro.*M77.* 3 1 +ATI FirePro M7820 .*ATI.*FirePro.*M78.* 3 1 +ATI FireMV .*ATI.*FireMV.* 0 1 +ATI Geforce 9500 GT .*ATI.*Geforce 9500 *GT.* 2 1 +ATI Geforce 9600 GT .*ATI.*Geforce 9600 *GT.* 3 1 +ATI Geforce 9800 GT .*ATI.*Geforce 9800 *GT.* 3 1 +ATI Generic .*ATI.*Generic.* 0 0 +ATI Hercules 9800 .*ATI.*Hercules.*9800.* 1 1 +ATI IGP 340M .*ATI.*IGP.*340M.* 0 0 +ATI M52 .*ATI.*M52.* 1 1 +ATI M54 .*ATI.*M54.* 1 1 +ATI M56 .*ATI.*M56.* 1 1 +ATI M71 .*ATI.*M71.* 1 1 +ATI M72 .*ATI.*M72.* 1 1 +ATI M76 .*ATI.*M76.* 3 1 +ATI Radeon HD 64xx .*ATI.*AMD Radeon.* HD [67]4..[MG] 2 1 +ATI Radeon HD 65xx .*ATI.*AMD Radeon.* HD [67]5..[MG] 2 1 +ATI Radeon HD 66xx .*ATI.*AMD Radeon.* HD [67]6..[MG] 3 1 +ATI Mobility Radeon 4100 .*ATI.*Mobility.*41.. 1 1 +ATI Mobility Radeon 7xxx .*ATI.*Mobility.*Radeon 7.* 0 1 +ATI Mobility Radeon 8xxx .*ATI.*Mobility.*Radeon 8.* 0 1 +ATI Mobility Radeon 9800 .*ATI.*Mobility.*98.* 1 1 +ATI Mobility Radeon 9700 .*ATI.*Mobility.*97.* 1 1 +ATI Mobility Radeon 9600 .*ATI.*Mobility.*96.* 0 1 +ATI Mobility Radeon HD 530v .*ATI.*Mobility.*HD *530v.* 1 1 +ATI Mobility Radeon HD 540v .*ATI.*Mobility.*HD *540v.* 2 1 +ATI Mobility Radeon HD 545v .*ATI.*Mobility.*HD *545v.* 2 1 +ATI Mobility Radeon HD 550v .*ATI.*Mobility.*HD *550v.* 2 1 +ATI Mobility Radeon HD 560v .*ATI.*Mobility.*HD *560v.* 2 1 +ATI Mobility Radeon HD 565v .*ATI.*Mobility.*HD *565v.* 2 1 +ATI Mobility Radeon HD 2300 .*ATI.*Mobility.*HD *23.* 2 1 +ATI Mobility Radeon HD 2400 .*ATI.*Mobility.*HD *24.* 2 1 +ATI Mobility Radeon HD 2600 .*ATI.*Mobility.*HD *26.* 3 1 +ATI Mobility Radeon HD 2700 .*ATI.*Mobility.*HD *27.* 3 1 +ATI Mobility Radeon HD 3100 .*ATI.*Mobility.*HD *31.* 0 1 +ATI Mobility Radeon HD 3200 .*ATI.*Mobility.*HD *32.* 0 1 +ATI Mobility Radeon HD 3400 .*ATI.*Mobility.*HD *34.* 1 1 +ATI Mobility Radeon HD 3600 .*ATI.*Mobility.*HD *36.* 1 1 +ATI Mobility Radeon HD 3800 .*ATI.*Mobility.*HD *38.* 3 1 +ATI Mobility Radeon HD 4200 .*ATI.*Mobility.*HD *42.* 1 1 +ATI Mobility Radeon HD 4300 .*ATI.*Mobility.*HD *43.* 1 1 +ATI Mobility Radeon HD 4500 .*ATI.*Mobility.*HD *45.* 1 1 +ATI Mobility Radeon HD 4600 .*ATI.*Mobility.*HD *46.* 2 1 +ATI Mobility Radeon HD 4800 .*ATI.*Mobility.*HD *48.* 3 1 +ATI Mobility Radeon HD 5100 .*ATI.*Mobility.*HD *51.* 1 1 +ATI Mobility Radeon HD 5300 .*ATI.*Mobility.*HD *53.* 3 1 +ATI Mobility Radeon HD 5400 .*ATI.*Mobility.*HD *54.* 2 1 +ATI Mobility Radeon HD 5500 .*ATI.*Mobility.*HD *55.* 3 1 +ATI Mobility Radeon HD 5600 .*ATI.*Mobility.*HD *56.* 3 1 +ATI Mobility Radeon HD 5700 .*ATI.*Mobility.*HD *57.* 3 1 +ATI Mobility Radeon HD 6200 .*ATI.*Mobility.*HD *62.* 3 1 +ATI Mobility Radeon HD 6300 .*ATI.*Mobility.*HD *63.* 3 1 +ATI Mobility Radeon HD 6400M .*ATI.*Mobility.*HD *64.* 3 1 +ATI Mobility Radeon HD 6500M .*ATI.*Mobility.*HD *65.* 3 1 +ATI Mobility Radeon HD 6600M .*ATI.*Mobility.*HD *66.* 3 1 +ATI Mobility Radeon HD 6700M .*ATI.*Mobility.*HD *67.* 3 1 +ATI Mobility Radeon HD 6800M .*ATI.*Mobility.*HD *68.* 3 1 +ATI Mobility Radeon HD 6900M .*ATI.*Mobility.*HD *69.* 3 1 +ATI Radeon HD 2300 .*ATI.*Radeon HD *23.. 2 1 +ATI Radeon HD 2400 .*ATI.*Radeon HD *24.. 1 1 +ATI Radeon HD 2600 .*ATI.*Radeon HD *26.. 2 1 +ATI Radeon HD 2900 .*ATI.*Radeon HD *29.. 3 1 +ATI Radeon HD 3000 .*ATI.*Radeon HD *30.. 0 1 +ATI Radeon HD 3100 .*ATI.*Radeon HD *31.. 1 1 +ATI Radeon HD 3200 .*ATI.*Radeon HD *32.. 1 1 +ATI Radeon HD 3300 .*ATI.*Radeon HD *33.. 2 1 +ATI Radeon HD 3400 .*ATI.*Radeon HD *34.. 1 1 +ATI Radeon HD 3500 .*ATI.*Radeon HD *35.. 2 1 +ATI Radeon HD 3600 .*ATI.*Radeon HD *36.. 3 1 +ATI Radeon HD 3700 .*ATI.*Radeon HD *37.. 3 1 +ATI Radeon HD 3800 .*ATI.*Radeon HD *38.. 3 1 +ATI Radeon HD 4100 .*ATI.*Radeon HD *41.. 1 1 +ATI Radeon HD 4200 .*ATI.*Radeon HD *42.. 1 1 +ATI Radeon HD 4300 .*ATI.*Radeon HD *43.. 2 1 +ATI Radeon HD 4400 .*ATI.*Radeon HD *44.. 2 1 +ATI Radeon HD 4500 .*ATI.*Radeon HD *45.. 2 1 +ATI Radeon HD 4600 .*ATI.*Radeon HD *46.. 3 1 +ATI Radeon HD 4700 .*ATI.*Radeon HD *47.. 3 1 +ATI Radeon HD 4800 .*ATI.*Radeon HD *48.. 3 1 +ATI Radeon HD 5400 .*ATI.*Radeon HD *54.. 3 1 +ATI Radeon HD 5500 .*ATI.*Radeon HD *55.. 2 1 +ATI Radeon HD 5600 .*ATI.*Radeon HD *56.. 3 1 +ATI Radeon HD 5700 .*ATI.*Radeon HD *57.. 3 1 +ATI Radeon HD 5800 .*ATI.*Radeon HD *58.. 4 1 +ATI Radeon HD 5900 .*ATI.*Radeon HD *59.. 3 1 +ATI Radeon HD 6200 .*ATI.*Radeon HD *62.. 1 1 +ATI Radeon HD 6300 .*ATI.*Radeon HD *63.. 1 1 +ATI Radeon HD 6400 .*ATI.*Radeon HD *64.. 2 1 +ATI Radeon HD 6500 .*ATI.*Radeon HD *65.. 2 1 +ATI Radeon HD 6600 .*ATI.*Radeon HD *66.. 3 1 +ATI Radeon HD 6700 .*ATI.*Radeon HD *67.. 3 1 +ATI Radeon HD 6800 .*ATI.*Radeon HD *68.. 4 1 +ATI Radeon HD 6900 .*ATI.*Radeon HD *69.. 5 1 +ATI Radeon OpenGL .*ATI.*Radeon OpenGL.* 0 0 +ATI Radeon 2100 .*ATI.*Radeon 21.. 0 1 +ATI Radeon 3000 .*ATI.*Radeon 30.. 1 1 +ATI Radeon 3100 .*ATI.*Radeon 31.. 1 1 +ATI Radeon 5xxx .*ATI.*Radeon 5... 3 1 +ATI Radeon 7xxx .*ATI.*Radeon 7... 0 1 +ATI Radeon 8xxx .*ATI.*Radeon 8... 0 1 +ATI Radeon 9000 .*ATI.*Radeon 90.. 0 1 +ATI Radeon 9100 .*ATI.*Radeon 91.. 0 1 +ATI Radeon 9200 .*ATI.*Radeon 92.. 1 1 +ATI Radeon 9500 .*ATI.*Radeon 95.. 0 1 +ATI Radeon 9600 .*ATI.*Radeon 96.. 0 1 +ATI Radeon 9700 .*ATI.*Radeon 97.. 1 1 +ATI Radeon 9800 .*ATI.*Radeon 98.. 1 1 +ATI Radeon RV250 .*ATI.*RV250.* 0 1 +ATI Radeon RV600 .*ATI.*RV6.* 1 1 +ATI Radeon RX700 .*ATI.*RX70.* 1 1 +ATI Radeon RX800 .*ATI.*Radeon *RX80.* 2 1 +ATI RS880M .*ATI.*RS880M 1 1 +ATI Radeon RX9550 .*ATI.*RX9550.* 1 1 +ATI Radeon VE .*ATI.*Radeon.*VE.* 0 0 +ATI Radeon X300 .*ATI.*Radeon *X3.* 1 1 +ATI Radeon X400 .*ATI.*Radeon ?X4.* 0 1 +ATI Radeon X500 .*ATI.*Radeon ?X5.* 0 1 +ATI Radeon X600 .*ATI.*Radeon ?X6.* 1 1 +ATI Radeon X700 .*ATI.*Radeon ?X7.* 1 1 +ATI Radeon X800 .*ATI.*Radeon ?X8.* 2 1 +ATI Radeon X900 .*ATI.*Radeon ?X9.* 2 1 +ATI Radeon Xpress .*ATI.*Radeon Xpress.* 1 1 +ATI Rage 128 .*ATI.*Rage 128.* 0 1 +ATI R300 (9700) .*R300.* 1 1 +ATI R350 (9800) .*R350.* 1 1 +ATI R580 (X1900) .*R580.* 3 1 +ATI RC410 (Xpress 200) .*RC410.* 0 0 +ATI RS48x (Xpress 200x) .*RS48.* 0 0 +ATI RS600 (Xpress 3200) .*RS600.* 0 0 +ATI RV350 (9600) .*RV350.* 0 1 +ATI RV370 (X300) .*RV370.* 0 1 +ATI RV410 (X700) .*RV410.* 1 1 +ATI RV515 .*RV515.* 1 1 +ATI RV570 (X1900 GT/PRO) .*RV570.* 3 1 +ATI RV380 .*RV380.* 0 1 +ATI RV530 .*RV530.* 1 1 +ATI RX480 (Xpress 200P) .*RX480.* 0 1 +ATI RX700 .*RX700.* 1 1 +AMD ANTILLES (HD 6990) .*(AMD|ATI).*Antilles.* 3 1 +AMD BARTS (HD 6800) .*(AMD|ATI).*Barts.* 3 1 +AMD CAICOS (HD 6400) .*(AMD|ATI).*Caicos.* 3 1 +AMD CAYMAN (HD 6900) .*(AMD|ATI).*(Cayman|CAYMAM).* 3 1 +AMD CEDAR (HD 5450) .*(AMD|ATI).*Cedar.* 2 1 +AMD CYPRESS (HD 5800) .*(AMD|ATI).*Cypress.* 3 1 +AMD HEMLOCK (HD 5970) .*(AMD|ATI).*Hemlock.* 3 1 +AMD JUNIPER (HD 5700) .*(AMD|ATI).*Juniper.* 3 1 +AMD PARK .*(AMD|ATI).*Park.* 3 1 +AMD REDWOOD (HD 5500/5600) .*(AMD|ATI).*Redwood.* 3 1 +AMD TURKS (HD 6500/6600) .*(AMD|ATI).*Turks.* 3 1 +AMD RS780 (HD 3200) .*RS780.* 0 1 +AMD RS880 (HD 4200) .*RS880.* 1 1 +AMD RV610 (HD 2400) .*RV610.* 1 1 +AMD RV620 (HD 3400) .*RV620.* 1 1 +AMD RV630 (HD 2600) .*RV630.* 2 1 +AMD RV635 (HD 3600) .*RV635.* 3 1 +AMD RV670 (HD 3800) .*RV670.* 3 1 +AMD R680 (HD 3870 X2) .*R680.* 3 1 +AMD R700 (HD 4800 X2) .*R700.* 3 1 +AMD RV710 (HD 4300) .*RV710.* 1 1 +AMD RV730 (HD 4600) .*RV730.* 3 1 +AMD RV740 (HD 4700) .*RV740.* 3 1 +AMD RV770 (HD 4800) .*RV770.* 3 1 +AMD RV790 (HD 4800) .*RV790.* 3 1 +ATI 760G/Radeon 3000 .*ATI.*AMD 760G.* 1 1 +ATI 780L/Radeon 3000 .*ATI.*AMD 780L.* 1 1 +ATI Radeon DDR .*ATI.*Radeon ?DDR.* 0 1 +ATI FirePro 2000 .*ATI.*FirePro 2.* 1 1 +ATI FirePro 3000 .*ATI.*FirePro V3.* 1 1 +ATI FirePro 4000 .*ATI.*FirePro V4.* 2 1 +ATI FirePro 5000 .*ATI.*FirePro V5.* 3 1 +ATI FirePro 7000 .*ATI.*FirePro V7.* 3 1 +ATI FirePro M .*ATI.*FirePro M.* 3 1 +ATI Technologies .*ATI *Technologies.* 4 1 +ATI R300 (9700) .*R300.* 1 1 +ATI Radeon .*ATI.*(Diamond|Radeon).* 0 1 +Intel X3100 .*Intel.*X3100.* 1 1 +Intel 830M .*Intel.*830M 0 0 +Intel 845G .*Intel.*845G 1 0 +Intel 855GM .*Intel.*855GM 0 0 +Intel 865G .*Intel.*865G 1 0 +Intel 900 .*Intel.*900.*900 0 0 +Intel 915GM .*Intel.*915GM 1 0 +Intel 915G .*Intel.*915G 1 0 +Intel 945GM .*Intel.*945GM.* 1 1 +Intel 945G .*Intel.*945G.* 1 1 +Intel 950 .*Intel.*950.* 1 1 +Intel 965 .*Intel.*965.* 1 1 +Intel G33 .*Intel.*G33.* 0 0 +Intel G41 .*Intel.*G41.* 1 1 +Intel G45 .*Intel.*G45.* 1 1 +Intel Bear Lake .*Intel.*Bear Lake.* 1 0 +Intel Broadwater .*Intel.*Broadwater.* 0 0 +Intel Brookdale .*Intel.*Brookdale.* 0 0 +Intel Cantiga .*Intel.*Cantiga.* 1 0 +Intel Eaglelake .*Intel.*Eaglelake.* 1 1 +Intel Graphics Media HD .*Intel.*Graphics Media.*HD.* 1 1 +Intel HD Graphics .*Intel.*HD Graphics.* 2 1 +Intel Mobile 4 Series .*Intel.*Mobile.* 4 Series.* 1 1 +Intel Media Graphics HD .*Intel.*Media Graphics HD.* 0 1 +Intel Montara .*Intel.*Montara.* 0 0 +Intel Pineview .*Intel.*Pineview.* 1 1 +Intel Springdale .*Intel.*Springdale.* 0 0 +Intel HD Graphics 2000 .*Intel.*HD2000.* 1 1 +Intel HD Graphics 3000 .*Intel.*HD3000.* 2 1 +Matrox .*Matrox.* 0 0 +Mesa .*Mesa.* 1 0 +NVIDIA 205 .*NVIDIA .*GeForce 205.* 2 1 +NVIDIA 210 .*NVIDIA .*GeForce 210.* 2 1 +NVIDIA 310M .*NVIDIA .*GeForce 310M.* 2 1 +NVIDIA 310 .*NVIDIA .*GeForce 310.* 2 1 +NVIDIA 315M .*NVIDIA .*GeForce 315M.* 2 1 +NVIDIA 315 .*NVIDIA .*GeForce 315.* 3 1 +NVIDIA 320M .*NVIDIA .*GeForce 320M.* 3 1 +NVIDIA G100M .*NVIDIA .*100M.* 2 1 +NVIDIA G100 .*NVIDIA .*100.* 1 1 +NVIDIA G102M .*NVIDIA .*102M.* 0 1 +NVIDIA G103M .*NVIDIA .*103M.* 0 1 +NVIDIA G105M .*NVIDIA .*105M.* 2 1 +NVIDIA G 110M .*NVIDIA .*110M.* 0 1 +NVIDIA G 120M .*NVIDIA .*120M.* 1 1 +NVIDIA G 200 .*NVIDIA .*200(M)?.* 1 1 +NVIDIA G 205M .*NVIDIA .*205(M)?.* 0 1 +NVIDIA G 210 .*NVIDIA .*210(M)?.* 2 1 +NVIDIA 305M .*NVIDIA .*305(M)?.* 1 1 +NVIDIA G 310M .*NVIDIA .*310(M)?.* 2 1 +NVIDIA G 315 .*NVIDIA .*315(M)?.* 2 1 +NVIDIA G 320M .*NVIDIA .*320(M)?.* 2 1 +NVIDIA G 405 .*NVIDIA .*405(M)?.* 3 1 +NVIDIA G 410M .*NVIDIA .*410(M)?.* 3 1 +NVIDIA GT 120M .*NVIDIA .*GT *120(M)?.* 2 1 +NVIDIA GT 120 .*NVIDIA .*GT.*120 2 1 +NVIDIA GT 130M .*NVIDIA .*GT *130(M)?.* 3 1 +NVIDIA GT 140M .*NVIDIA .*GT *140(M)?.* 2 1 +NVIDIA GT 150M .*NVIDIA .*GT(S)? *150(M)?.* 2 1 +NVIDIA GT 160M .*NVIDIA .*GT *160(M)?.* 2 1 +NVIDIA GT 220M .*NVIDIA .*GT *220(M)?.* 3 1 +NVIDIA GT 230M .*NVIDIA .*GT *230(M)?.* 3 1 +NVIDIA GT 240M .*NVIDIA .*GT *240(M)?.* 3 1 +NVIDIA GT 250M .*NVIDIA .*GT *250(M)?.* 2 1 +NVIDIA GT 260M .*NVIDIA .*GT *260(M)?.* 2 1 +NVIDIA GT 320M .*NVIDIA .*GT *320(M)?.* 2 1 +NVIDIA GT 325M .*NVIDIA .*GT *325(M)?.* 0 1 +NVIDIA GT 330M .*NVIDIA .*GT *330(M)?.* 3 1 +NVIDIA GT 335M .*NVIDIA .*GT *335(M)?.* 2 1 +NVIDIA GT 340M .*NVIDIA .*GT *340(M)?.* 2 1 +NVIDIA GT 415M .*NVIDIA .*GT *415(M)?.* 2 1 +NVIDIA GT 420M .*NVIDIA .*GT *420(M)?.* 3 1 +NVIDIA GT 425M .*NVIDIA .*GT *425(M)?.* 4 1 +NVIDIA GT 430M .*NVIDIA .*GT *430(M)?.* 3 1 +NVIDIA GT 435M .*NVIDIA .*GT *435(M)?.* 4 1 +NVIDIA GT 440M .*NVIDIA .*GT *440(M)?.* 3 1 +NVIDIA GT 445M .*NVIDIA .*GT *445(M)?.* 3 1 +NVIDIA GT 450M .*NVIDIA .*GT *450(M)?.* 3 1 +NVIDIA GT 520M .*NVIDIA .*GT *52.(M)?.* 3 1 +NVIDIA GT 530M .*NVIDIA .*GT *530(M)?.* 3 1 +NVIDIA GT 540M .*NVIDIA .*GT *54.(M)?.* 3 1 +NVIDIA GT 550M .*NVIDIA .*GT *550(M)?.* 3 1 +NVIDIA GT 555M .*NVIDIA .*GT *555(M)?.* 3 1 +NVIDIA GTS 160M .*NVIDIA .*GT(S)? *160(M)?.* 2 1 +NVIDIA GTS 240 .*NVIDIA .*GTS *24.* 3 1 +NVIDIA GTS 250 .*NVIDIA .*GTS *25.* 4 1 +NVIDIA GTS 350M .*NVIDIA .*GTS *350M.* 3 1 +NVIDIA GTS 360M .*NVIDIA .*GTS *360M.* 5 1 +NVIDIA GTS 360 .*NVIDIA .*GTS *360.* 3 1 +NVIDIA GTS 450 .*NVIDIA .*GTS *45.* 4 1 +NVIDIA GTX 260 .*NVIDIA .*GTX *26.* 4 1 +NVIDIA GTX 275 .*NVIDIA .*GTX *275.* 4 1 +NVIDIA GTX 270 .*NVIDIA .*GTX *27.* 3 1 +NVIDIA GTX 285 .*NVIDIA .*GTX *285.* 5 1 +NVIDIA GTX 280 .*NVIDIA .*GTX *280.* 4 1 +NVIDIA GTX 290 .*NVIDIA .*GTX *290.* 3 1 +NVIDIA GTX 295 .*NVIDIA .*GTX *295.* 5 1 +NVIDIA GTX 460M .*NVIDIA .*GTX *460M.* 4 1 +NVIDIA GTX 465 .*NVIDIA .*GTX *465.* 5 1 +NVIDIA GTX 460 .*NVIDIA .*GTX *46.* 5 1 +NVIDIA GTX 470M .*NVIDIA .*GTX *470M.* 3 1 +NVIDIA GTX 470 .*NVIDIA .*GTX *47.* 5 1 +NVIDIA GTX 480M .*NVIDIA .*GTX *480M.* 3 1 +NVIDIA GTX 485M .*NVIDIA .*GTX *485M.* 3 1 +NVIDIA GTX 480 .*NVIDIA .*GTX *48.* 5 1 +NVIDIA GTX 530 .*NVIDIA .*GTX *53.* 3 1 +NVIDIA GTX 550 .*NVIDIA .*GTX *55.* 5 1 +NVIDIA GTX 560 .*NVIDIA .*GTX *56.* 5 1 +NVIDIA GTX 570 .*NVIDIA .*GTX *57.* 5 1 +NVIDIA GTX 580M .*NVIDIA .*GTX *580M.* 3 1 +NVIDIA GTX 580 .*NVIDIA .*GTX *58.* 5 1 +NVIDIA GTX 590 .*NVIDIA .*GTX *59.* 3 1 +NVIDIA C51 .*NVIDIA .*C51.* 0 1 +NVIDIA G72 .*NVIDIA .*G72.* 1 1 +NVIDIA G73 .*NVIDIA .*G73.* 1 1 +NVIDIA G84 .*NVIDIA .*G84.* 2 1 +NVIDIA G86 .*NVIDIA .*G86.* 3 1 +NVIDIA G92 .*NVIDIA .*G92.* 3 1 +NVIDIA GeForce .*GeForce 256.* 0 0 +NVIDIA GeForce 2 .*GeForce ?2 ?.* 0 1 +NVIDIA GeForce 3 .*GeForce ?3 ?.* 0 1 +NVIDIA GeForce 3 Ti .*GeForce ?3 Ti.* 0 1 +NVIDIA GeForce 4 .*NVIDIA .*GeForce ?4.* 1 1 +NVIDIA GeForce 4 Go .*NVIDIA .*GeForce ?4.*Go.* 0 1 +NVIDIA GeForce 4 MX .*NVIDIA .*GeForce ?4 MX.* 0 1 +NVIDIA GeForce 4 PCX .*NVIDIA .*GeForce ?4 PCX.* 0 1 +NVIDIA GeForce 4 Ti .*NVIDIA .*GeForce ?4 Ti.* 0 1 +NVIDIA GeForce 6100 .*NVIDIA .*GeForce 61.* 3 1 +NVIDIA GeForce 6200 .*NVIDIA .*GeForce 62.* 0 1 +NVIDIA GeForce 6500 .*NVIDIA .*GeForce 65.* 0 1 +NVIDIA GeForce 6600 .*NVIDIA .*GeForce 66.* 2 1 +NVIDIA GeForce 6700 .*NVIDIA .*GeForce 67.* 2 1 +NVIDIA GeForce 6800 .*NVIDIA .*GeForce 68.* 2 1 +NVIDIA GeForce 7000 .*NVIDIA .*GeForce 70.* 1 1 +NVIDIA GeForce 7100 .*NVIDIA .*GeForce 71.* 1 1 +NVIDIA GeForce 7200 .*NVIDIA .*GeForce 72.* 1 1 +NVIDIA GeForce 7300 .*NVIDIA .*GeForce 73.* 1 1 +NVIDIA GeForce 7500 .*NVIDIA .*GeForce 75.* 1 1 +NVIDIA GeForce 7600 .*NVIDIA .*GeForce 76.* 2 1 +NVIDIA GeForce 7800 .*NVIDIA .*GeForce 78.* 2 1 +NVIDIA GeForce 7900 .*NVIDIA .*GeForce 79.* 2 1 +NVIDIA GeForce 8100 .*NVIDIA .*GeForce 81.* 1 1 +NVIDIA GeForce 8200M .*NVIDIA .*GeForce 8200M.* 1 1 +NVIDIA GeForce 8200 .*NVIDIA .*GeForce 82.* 1 1 +NVIDIA GeForce 8300 .*NVIDIA .*GeForce 83.* 2 1 +NVIDIA GeForce 8400M .*NVIDIA .*GeForce 8400M.* 1 1 +NVIDIA GeForce 8400 .*NVIDIA .*GeForce 84.* 2 1 +NVIDIA GeForce 8500 .*NVIDIA .*GeForce 85.* 2 1 +NVIDIA GeForce 8600M .*NVIDIA .*GeForce 8600M.* 2 1 +NVIDIA GeForce 8600 .*NVIDIA .*GeForce 86.* 3 1 +NVIDIA GeForce 8700M .*NVIDIA .*GeForce 8700M.* 3 1 +NVIDIA GeForce 8700 .*NVIDIA .*GeForce 87.* 3 1 +NVIDIA GeForce 8800M .*NVIDIA .*GeForce 8800M.* 3 1 +NVIDIA GeForce 8800 .*NVIDIA .*GeForce 88.* 3 1 +NVIDIA GeForce 9100M .*NVIDIA .*GeForce 9100M.* 0 1 +NVIDIA GeForce 9100 .*NVIDIA .*GeForce 91.* 0 1 +NVIDIA GeForce 9200M .*NVIDIA .*GeForce 9200M.* 1 1 +NVIDIA GeForce 9200 .*NVIDIA .*GeForce 92.* 1 1 +NVIDIA GeForce 9300M .*NVIDIA .*GeForce 9300M.* 1 1 +NVIDIA GeForce 9300 .*NVIDIA .*GeForce 93.* 1 1 +NVIDIA GeForce 9400M .*NVIDIA .*GeForce 9400M.* 2 1 +NVIDIA GeForce 9400 .*NVIDIA .*GeForce 94.* 3 1 +NVIDIA GeForce 9500M .*NVIDIA .*GeForce 9500M.* 2 1 +NVIDIA GeForce 9500 .*NVIDIA .*GeForce 95.* 2 1 +NVIDIA GeForce 9600M .*NVIDIA .*GeForce 9600M.* 2 1 +NVIDIA GeForce 9600 .*NVIDIA .*GeForce 96.* 3 1 +NVIDIA GeForce 9700M .*NVIDIA .*GeForce 9700M.* 2 1 +NVIDIA GeForce 9800M .*NVIDIA .*GeForce 9800M.* 2 1 +NVIDIA GeForce 9800 .*NVIDIA .*GeForce 98.* 3 1 +NVIDIA GeForce FX 5100 .*NVIDIA .*GeForce FX 51.* 0 1 +NVIDIA GeForce FX 5200 .*NVIDIA .*GeForce FX 52.* 0 1 +NVIDIA GeForce FX 5300 .*NVIDIA .*GeForce FX 53.* 0 1 +NVIDIA GeForce FX 5500 .*NVIDIA .*GeForce FX 55.* 0 1 +NVIDIA GeForce FX 5600 .*NVIDIA .*GeForce FX 56.* 0 1 +NVIDIA GeForce FX 5700 .*NVIDIA .*GeForce FX 57.* 1 1 +NVIDIA GeForce FX 5800 .*NVIDIA .*GeForce FX 58.* 1 1 +NVIDIA GeForce FX 5900 .*NVIDIA .*GeForce FX 59.* 1 1 +NVIDIA GeForce FX Go5100 .*NVIDIA .*GeForce FX Go51.* 0 1 +NVIDIA GeForce FX Go5200 .*NVIDIA .*GeForce FX Go52.* 0 1 +NVIDIA GeForce FX Go5300 .*NVIDIA .*GeForce FX Go53.* 0 1 +NVIDIA GeForce FX Go5500 .*NVIDIA .*GeForce FX Go55.* 0 1 +NVIDIA GeForce FX Go5600 .*NVIDIA .*GeForce FX Go56.* 0 1 +NVIDIA GeForce FX Go5700 .*NVIDIA .*GeForce FX Go57.* 1 1 +NVIDIA GeForce FX Go5800 .*NVIDIA .*GeForce FX Go58.* 1 1 +NVIDIA GeForce FX Go5900 .*NVIDIA .*GeForce FX Go59.* 1 1 +NVIDIA GeForce FX Go5xxx .*NVIDIA .*GeForce FX Go.* 0 1 +NVIDIA GeForce Go 6100 .*NVIDIA .*GeForce Go 61.* 0 1 +NVIDIA GeForce Go 6200 .*NVIDIA .*GeForce Go 62.* 0 1 +NVIDIA GeForce Go 6400 .*NVIDIA .*GeForce Go 64.* 1 1 +NVIDIA GeForce Go 6500 .*NVIDIA .*GeForce Go 65.* 1 1 +NVIDIA GeForce Go 6600 .*NVIDIA .*GeForce Go 66.* 1 1 +NVIDIA GeForce Go 6700 .*NVIDIA .*GeForce Go 67.* 1 1 +NVIDIA GeForce Go 6800 .*NVIDIA .*GeForce Go 68.* 1 1 +NVIDIA GeForce Go 7200 .*NVIDIA .*GeForce Go 72.* 1 1 +NVIDIA GeForce Go 7300 LE .*NVIDIA .*GeForce Go 73.*LE.* 0 1 +NVIDIA GeForce Go 7300 .*NVIDIA .*GeForce Go 73.* 1 1 +NVIDIA GeForce Go 7400 .*NVIDIA .*GeForce Go 74.* 1 1 +NVIDIA GeForce Go 7600 .*NVIDIA .*GeForce Go 76.* 2 1 +NVIDIA GeForce Go 7700 .*NVIDIA .*GeForce Go 77.* 2 1 +NVIDIA GeForce Go 7800 .*NVIDIA .*GeForce Go 78.* 2 1 +NVIDIA GeForce Go 7900 .*NVIDIA .*GeForce Go 79.* 2 1 +NVIDIA D9M .*NVIDIA .*D9M.* 1 1 +NVIDIA G94 .*NVIDIA .*G94.* 3 1 +NVIDIA GeForce Go 6 .*GeForce Go 6.* 1 1 +NVIDIA ION 2 .*NVIDIA .*ION 2.* 2 1 +NVIDIA ION .*NVIDIA .*ION.* 5 1 +NVIDIA NB8M .*NVIDIA .*NB8M.* 1 1 +NVIDIA NB8P .*NVIDIA .*NB8P.* 2 1 +NVIDIA NB9E .*NVIDIA .*NB9E.* 3 1 +NVIDIA NB9M .*NVIDIA .*NB9M.* 1 1 +NVIDIA NB9P .*NVIDIA .*NB9P.* 2 1 +NVIDIA N10 .*NVIDIA .*N10.* 1 1 +NVIDIA GeForce PCX .*GeForce PCX.* 0 1 +NVIDIA Generic .*NVIDIA .*Unknown.* 0 0 +NVIDIA NV17 .*NVIDIA .*NV17.* 0 1 +NVIDIA NV34 .*NVIDIA .*NV34.* 0 1 +NVIDIA NV35 .*NVIDIA .*NV35.* 0 1 +NVIDIA NV36 .*NVIDIA .*NV36.* 1 1 +NVIDIA NV41 .*NVIDIA .*NV41.* 1 1 +NVIDIA NV43 .*NVIDIA .*NV43.* 1 1 +NVIDIA NV44 .*NVIDIA .*NV44.* 1 1 +NVIDIA nForce .*NVIDIA .*nForce.* 0 0 +NVIDIA MCP51 .*NVIDIA .*MCP51.* 1 1 +NVIDIA MCP61 .*NVIDIA .*MCP61.* 1 1 +NVIDIA MCP67 .*NVIDIA .*MCP67.* 1 1 +NVIDIA MCP68 .*NVIDIA .*MCP68.* 1 1 +NVIDIA MCP73 .*NVIDIA .*MCP73.* 1 1 +NVIDIA MCP77 .*NVIDIA .*MCP77.* 1 1 +NVIDIA MCP78 .*NVIDIA .*MCP78.* 1 1 +NVIDIA MCP79 .*NVIDIA .*MCP79.* 1 1 +NVIDIA MCP7A .*NVIDIA .*MCP7A.* 1 1 +NVIDIA Quadro2 .*Quadro2.* 0 1 +NVIDIA Quadro 1000M .*Quadro.*1000M.* 2 1 +NVIDIA Quadro 2000 M/D .*Quadro.*2000.* 3 1 +NVIDIA Quadro 3000M .*Quadro.*3000M.* 3 1 +NVIDIA Quadro 4000M .*Quadro.*4000M.* 3 1 +NVIDIA Quadro 4000 .*Quadro *4000.* 3 1 +NVIDIA Quadro 50x0 M .*Quadro.*50.0.* 3 1 +NVIDIA Quadro 6000 .*Quadro.*6000.* 3 1 +NVIDIA Quadro 400 .*Quadro.*400.* 2 1 +NVIDIA Quadro 600 .*Quadro.*600.* 2 1 +NVIDIA Quadro4 .*Quadro4.* 0 1 +NVIDIA Quadro DCC .*Quadro DCC.* 0 1 +NVIDIA Quadro CX .*Quadro.*CX.* 3 1 +NVIDIA Quadro FX 770M .*Quadro.*FX *770M.* 2 1 +NVIDIA Quadro FX 1500M .*Quadro.*FX *1500M.* 1 1 +NVIDIA Quadro FX 1600M .*Quadro.*FX *1600M.* 2 1 +NVIDIA Quadro FX 2500M .*Quadro.*FX *2500M.* 2 1 +NVIDIA Quadro FX 2700M .*Quadro.*FX *2700M.* 3 1 +NVIDIA Quadro FX 2800M .*Quadro.*FX *2800M.* 3 1 +NVIDIA Quadro FX 3500 .*Quadro.*FX *3500.* 2 1 +NVIDIA Quadro FX 3600 .*Quadro.*FX *3600.* 3 1 +NVIDIA Quadro FX 3700 .*Quadro.*FX *3700.* 3 1 +NVIDIA Quadro FX 3800 .*Quadro.*FX *3800.* 3 1 +NVIDIA Quadro FX 4500 .*Quadro.*FX *45.* 3 1 +NVIDIA Quadro FX 880M .*Quadro.*FX *880M.* 3 1 +NVIDIA Quadro FX 4800 .*NVIDIA .*Quadro *FX *4800.* 3 1 +NVIDIA Quadro FX .*Quadro FX.* 1 1 +NVIDIA Quadro NVS 1xxM .*Quadro NVS *1.[05]M.* 0 1 +NVIDIA Quadro NVS 300M .*NVIDIA .*NVS *300M.* 2 1 +NVIDIA Quadro NVS 320M .*NVIDIA .*NVS *320M.* 2 1 +NVIDIA Quadro NVS 2100M .*NVIDIA .*NVS *2100M.* 2 1 +NVIDIA Quadro NVS 3100M .*NVIDIA .*NVS *3100M.* 2 1 +NVIDIA Quadro NVS 4200M .*NVIDIA .*NVS *4200M.* 2 1 +NVIDIA Quadro NVS 5100M .*NVIDIA .*NVS *5100M.* 2 1 +NVIDIA Quadro NVS .*NVIDIA .*NVS 0 1 +NVIDIA RIVA TNT .*RIVA TNT.* 0 0 +S3 .*S3 Graphics.* 1 0 +SiS SiS.* 1 0 +Trident Trident.* 0 0 +Tungsten Graphics Tungsten.* 0 0 +XGI XGI.* 0 0 +VIA VIA.* 0 0 +Apple Generic Apple.*Generic.* 0 0 +Apple Software Renderer Apple.*Software Renderer.* 0 0 +Humper Humper.* 0 1 + diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp index ec2493dd2e..ff7b6689af 100644 --- a/indra/newview/llfeaturemanager.cpp +++ b/indra/newview/llfeaturemanager.cpp @@ -57,6 +57,7 @@ #include "lldxhardware.h" #endif +#define LL_EXPORT_GPU_TABLE 0 #if LL_DARWIN const char FEATURE_TABLE_FILENAME[] = "featuretable_mac.txt"; @@ -386,6 +387,13 @@ void LLFeatureManager::parseGPUTable(std::string filename) *i = tolower(*i); } +#if LL_EXPORT_GPU_TABLE + llofstream json; + json.open("gpu_table.json"); + + json << "var gpu_table = [" << std::endl; +#endif + bool gpuFound; U32 lineNumber; for (gpuFound = false, lineNumber = 0; !gpuFound && !file.eof(); lineNumber++) @@ -438,7 +446,13 @@ void LLFeatureManager::parseGPUTable(std::string filename) LL_WARNS("RenderInit") << "invald gpu_table.txt:" << lineNumber << ": '" << buffer << "'" << LL_ENDL; continue; } - +#if LL_EXPORT_GPU_TABLE + json << "{'label' : '" << label << "',\n" << + "'regexp' : '" << expr << "',\n" << + "'class' : '" << cls << "',\n" << + "'supported' : '" << supported << "'\n},\n"; +#endif + for (U32 i = 0; i < expr.length(); i++) /*Flawfinder: ignore*/ { expr[i] = tolower(expr[i]); @@ -449,12 +463,18 @@ void LLFeatureManager::parseGPUTable(std::string filename) if(boost::regex_search(renderer, re)) { // if we found it, stop! +#if !LL_EXPORT_GPU_TABLE gpuFound = true; +#endif mGPUString = label; mGPUClass = (EGPUClass) strtol(cls.c_str(), NULL, 10); mGPUSupported = (BOOL) strtol(supported.c_str(), NULL, 10); } } +#if LL_EXPORT_GPU_TABLE + json << "];\n\n"; + json.close(); +#endif file.close(); if ( gpuFound ) @@ -585,7 +605,7 @@ void LLFeatureManager::applyRecommendedSettings() { // apply saved settings // cap the level at 2 (high) - S32 level = llmax(GPU_CLASS_0, llmin(mGPUClass, GPU_CLASS_2)); + S32 level = llmax(GPU_CLASS_0, llmin(mGPUClass, GPU_CLASS_5)); llinfos << "Applying Recommended Features" << llendl; @@ -678,18 +698,32 @@ void LLFeatureManager::setGraphicsLevel(S32 level, bool skipFeatures) { //same as low, but with "Basic Shaders" enabled maskFeatures("Low"); } + maskFeatures("Class0"); break; case 1: maskFeatures("Mid"); + maskFeatures("Class1"); break; case 2: maskFeatures("High"); + maskFeatures("Class2"); break; case 3: - maskFeatures("Ultra"); + maskFeatures("High"); + maskFeatures("Class3"); + break; + case 4: + maskFeatures("High"); + maskFeatures("Class4"); break; + case 5: + maskFeatures("High"); + maskFeatures("Class5"); + break; + default: maskFeatures("Low"); + maskFeatures("Class0"); break; } @@ -714,14 +748,16 @@ void LLFeatureManager::applyBaseMasks() mFeatures = maskp->getFeatures(); // mask class - if (mGPUClass >= 0 && mGPUClass < 4) + if (mGPUClass >= 0 && mGPUClass < 6) { const char* class_table[] = { "Class0", "Class1", "Class2", - "Class3" + "Class3", + "Class4", + "Class5", }; LL_INFOS("RenderInit") << "Setting GPU Class to " << class_table[mGPUClass] << LL_ENDL; diff --git a/indra/newview/llfeaturemanager.h b/indra/newview/llfeaturemanager.h index c9cb397fcc..6f9d2e49c6 100644 --- a/indra/newview/llfeaturemanager.h +++ b/indra/newview/llfeaturemanager.h @@ -39,7 +39,9 @@ typedef enum EGPUClass GPU_CLASS_0 = 0, GPU_CLASS_1 = 1, GPU_CLASS_2 = 2, - GPU_CLASS_3 = 3 + GPU_CLASS_3 = 3, + GPU_CLASS_4 = 4, + GPU_CLASS_5 = 5 } EGPUClass; diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 28f4ec72f3..dfd4981946 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -794,7 +794,18 @@ void send_stats() S32 shader_level = 0; if (LLPipeline::sRenderDeferred) { - shader_level = 3; + if (LLPipeline::RenderShadowDetail > 0) + { + shader_level = 5; + } + else if (LLPipeline::RenderDeferredSSAO) + { + shader_level = 4; + } + else + { + shader_level = 3; + } } else if (gPipeline.canUseWindLightShadersOnObjects()) { -- cgit v1.2.3 From 81996a034c3cb811a7d2136cc12ae49f52e0490e Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Tue, 28 Aug 2012 17:53:04 -0500 Subject: MAINT-1491 Tuned analysis function for more consistent results --- indra/newview/gpu_table.txt | 1026 ++++++++++++++++++++++--------------------- 1 file changed, 522 insertions(+), 504 deletions(-) diff --git a/indra/newview/gpu_table.txt b/indra/newview/gpu_table.txt index 2a47646a4e..4081e8848a 100644 --- a/indra/newview/gpu_table.txt +++ b/indra/newview/gpu_table.txt @@ -17,7 +17,7 @@ // // Format: // Fields are separated by one or more tab (not space) characters -// +// // // Class Numbers: // 0 - Defaults to low graphics settings. No shaders on by default @@ -32,507 +32,525 @@ // 1 - We claim to support this card. // -3Dfx .*3Dfx.* 0 0 -3Dlabs .*3Dlabs.* 0 0 -ATI 3D-Analyze .*ATI.*3D-Analyze.* 0 0 -ATI All-in-Wonder 7500 .*ATI.*All-in-Wonder 75.* 0 1 -ATI All-in-Wonder 8500 .*ATI.*All-in-Wonder 85.* 0 1 -ATI All-in-Wonder 9200 .*ATI.*All-in-Wonder 92.* 0 1 -ATI All-in-Wonder 9xxx .*ATI.*All-in-Wonder 9.* 1 1 -ATI All-in-Wonder HD .*ATI.*All-in-Wonder HD.* 1 1 -ATI All-in-Wonder X600 .*ATI.*All-in-Wonder X6.* 1 1 -ATI All-in-Wonder X800 .*ATI.*All-in-Wonder X8.* 2 1 -ATI All-in-Wonder X1800 .*ATI.*All-in-Wonder X18.* 3 1 -ATI All-in-Wonder X1900 .*ATI.*All-in-Wonder X19.* 3 1 -ATI All-in-Wonder PCI-E .*ATI.*All-in-Wonder.*PCI-E.* 1 1 -ATI All-in-Wonder Radeon .*ATI.*All-in-Wonder Radeon.* 0 1 -ATI ASUS ARES .*ATI.*ASUS.*ARES.* 3 1 -ATI ASUS A9xxx .*ATI.*ASUS.*A9.* 1 1 -ATI ASUS AH24xx .*ATI.*ASUS.*AH24.* 1 1 -ATI ASUS AH26xx .*ATI.*ASUS.*AH26.* 3 1 -ATI ASUS AH34xx .*ATI.*ASUS.*AH34.* 1 1 -ATI ASUS AH36xx .*ATI.*ASUS.*AH36.* 3 1 -ATI ASUS AH46xx .*ATI.*ASUS.*AH46.* 3 1 -ATI ASUS AX3xx .*ATI.*ASUS.*AX3.* 1 1 -ATI ASUS AX5xx .*ATI.*ASUS.*AX5.* 1 1 -ATI ASUS AX8xx .*ATI.*ASUS.*AX8.* 2 1 -ATI ASUS EAH24xx .*ATI.*ASUS.*EAH24.* 2 1 -ATI ASUS EAH26xx .*ATI.*ASUS.*EAH26.* 3 1 -ATI ASUS EAH29xx .*ATI.*ASUS.*EAH29.* 3 1 -ATI ASUS EAH34xx .*ATI.*ASUS.*EAH34.* 1 1 -ATI ASUS EAH36xx .*ATI.*ASUS.*EAH36.* 3 1 -ATI ASUS EAH38xx .*ATI.*ASUS.*EAH38.* 3 1 -ATI ASUS EAH43xx .*ATI.*ASUS.*EAH43.* 1 1 -ATI ASUS EAH45xx .*ATI.*ASUS.*EAH45.* 1 1 -ATI ASUS EAH48xx .*ATI.*ASUS.*EAH48.* 3 1 -ATI ASUS EAH57xx .*ATI.*ASUS.*EAH57.* 3 1 -ATI ASUS EAH58xx .*ATI.*ASUS.*EAH58.* 3 1 -ATI ASUS EAH6xxx .*ATI.*ASUS.*EAH6.* 3 1 -ATI ASUS Radeon X1xxx .*ATI.*ASUS.*X1.* 3 1 -ATI Radeon X7xx .*ATI.*ASUS.*X7.* 1 1 -ATI Radeon X19xx .*ATI.*(Radeon|Diamond) X19.* ?.* 2 1 -ATI Radeon X18xx .*ATI.*(Radeon|Diamond) X18.* ?.* 3 1 -ATI Radeon X17xx .*ATI.*(Radeon|Diamond) X17.* ?.* 2 1 -ATI Radeon X16xx .*ATI.*(Radeon|Diamond) X16.* ?.* 1 1 -ATI Radeon X15xx .*ATI.*(Radeon|Diamond) X15.* ?.* 2 1 -ATI Radeon X13xx .*ATI.*(Radeon|Diamond) X13.* ?.* 1 1 -ATI Radeon X1xxx .*ATI.*(Radeon|Diamond) X1.. ?.* 1 1 -ATI Radeon X2xxx .*ATI.*(Radeon|Diamond) X2.. ?.* 1 1 -ATI Display Adapter .*ATI.*display adapter.* 0 1 -ATI FireGL 5200 .*ATI.*FireGL V52.* 0 1 -ATI FireGL 5xxx .*ATI.*FireGL V5.* 1 1 -ATI FireGL .*ATI.*Fire.*GL.* 0 1 -ATI FirePro M3900 .*ATI.*FirePro.*M39.* 2 1 -ATI FirePro M5800 .*ATI.*FirePro.*M58.* 3 1 -ATI FirePro M7740 .*ATI.*FirePro.*M77.* 3 1 -ATI FirePro M7820 .*ATI.*FirePro.*M78.* 3 1 -ATI FireMV .*ATI.*FireMV.* 0 1 -ATI Geforce 9500 GT .*ATI.*Geforce 9500 *GT.* 2 1 -ATI Geforce 9600 GT .*ATI.*Geforce 9600 *GT.* 3 1 -ATI Geforce 9800 GT .*ATI.*Geforce 9800 *GT.* 3 1 -ATI Generic .*ATI.*Generic.* 0 0 -ATI Hercules 9800 .*ATI.*Hercules.*9800.* 1 1 -ATI IGP 340M .*ATI.*IGP.*340M.* 0 0 -ATI M52 .*ATI.*M52.* 1 1 -ATI M54 .*ATI.*M54.* 1 1 -ATI M56 .*ATI.*M56.* 1 1 -ATI M71 .*ATI.*M71.* 1 1 -ATI M72 .*ATI.*M72.* 1 1 -ATI M76 .*ATI.*M76.* 3 1 -ATI Radeon HD 64xx .*ATI.*AMD Radeon.* HD [67]4..[MG] 2 1 -ATI Radeon HD 65xx .*ATI.*AMD Radeon.* HD [67]5..[MG] 2 1 -ATI Radeon HD 66xx .*ATI.*AMD Radeon.* HD [67]6..[MG] 3 1 -ATI Mobility Radeon 4100 .*ATI.*Mobility.*41.. 1 1 -ATI Mobility Radeon 7xxx .*ATI.*Mobility.*Radeon 7.* 0 1 -ATI Mobility Radeon 8xxx .*ATI.*Mobility.*Radeon 8.* 0 1 -ATI Mobility Radeon 9800 .*ATI.*Mobility.*98.* 1 1 -ATI Mobility Radeon 9700 .*ATI.*Mobility.*97.* 1 1 -ATI Mobility Radeon 9600 .*ATI.*Mobility.*96.* 0 1 -ATI Mobility Radeon HD 530v .*ATI.*Mobility.*HD *530v.* 1 1 -ATI Mobility Radeon HD 540v .*ATI.*Mobility.*HD *540v.* 2 1 -ATI Mobility Radeon HD 545v .*ATI.*Mobility.*HD *545v.* 2 1 -ATI Mobility Radeon HD 550v .*ATI.*Mobility.*HD *550v.* 2 1 -ATI Mobility Radeon HD 560v .*ATI.*Mobility.*HD *560v.* 2 1 -ATI Mobility Radeon HD 565v .*ATI.*Mobility.*HD *565v.* 2 1 -ATI Mobility Radeon HD 2300 .*ATI.*Mobility.*HD *23.* 2 1 -ATI Mobility Radeon HD 2400 .*ATI.*Mobility.*HD *24.* 2 1 -ATI Mobility Radeon HD 2600 .*ATI.*Mobility.*HD *26.* 3 1 -ATI Mobility Radeon HD 2700 .*ATI.*Mobility.*HD *27.* 3 1 -ATI Mobility Radeon HD 3100 .*ATI.*Mobility.*HD *31.* 0 1 -ATI Mobility Radeon HD 3200 .*ATI.*Mobility.*HD *32.* 0 1 -ATI Mobility Radeon HD 3400 .*ATI.*Mobility.*HD *34.* 1 1 -ATI Mobility Radeon HD 3600 .*ATI.*Mobility.*HD *36.* 1 1 -ATI Mobility Radeon HD 3800 .*ATI.*Mobility.*HD *38.* 3 1 -ATI Mobility Radeon HD 4200 .*ATI.*Mobility.*HD *42.* 1 1 -ATI Mobility Radeon HD 4300 .*ATI.*Mobility.*HD *43.* 1 1 -ATI Mobility Radeon HD 4500 .*ATI.*Mobility.*HD *45.* 1 1 -ATI Mobility Radeon HD 4600 .*ATI.*Mobility.*HD *46.* 2 1 -ATI Mobility Radeon HD 4800 .*ATI.*Mobility.*HD *48.* 3 1 -ATI Mobility Radeon HD 5100 .*ATI.*Mobility.*HD *51.* 1 1 -ATI Mobility Radeon HD 5300 .*ATI.*Mobility.*HD *53.* 3 1 -ATI Mobility Radeon HD 5400 .*ATI.*Mobility.*HD *54.* 2 1 -ATI Mobility Radeon HD 5500 .*ATI.*Mobility.*HD *55.* 3 1 -ATI Mobility Radeon HD 5600 .*ATI.*Mobility.*HD *56.* 3 1 -ATI Mobility Radeon HD 5700 .*ATI.*Mobility.*HD *57.* 3 1 -ATI Mobility Radeon HD 6200 .*ATI.*Mobility.*HD *62.* 3 1 -ATI Mobility Radeon HD 6300 .*ATI.*Mobility.*HD *63.* 3 1 -ATI Mobility Radeon HD 6400M .*ATI.*Mobility.*HD *64.* 3 1 -ATI Mobility Radeon HD 6500M .*ATI.*Mobility.*HD *65.* 3 1 -ATI Mobility Radeon HD 6600M .*ATI.*Mobility.*HD *66.* 3 1 -ATI Mobility Radeon HD 6700M .*ATI.*Mobility.*HD *67.* 3 1 -ATI Mobility Radeon HD 6800M .*ATI.*Mobility.*HD *68.* 3 1 -ATI Mobility Radeon HD 6900M .*ATI.*Mobility.*HD *69.* 3 1 -ATI Radeon HD 2300 .*ATI.*Radeon HD *23.. 2 1 -ATI Radeon HD 2400 .*ATI.*Radeon HD *24.. 1 1 -ATI Radeon HD 2600 .*ATI.*Radeon HD *26.. 2 1 -ATI Radeon HD 2900 .*ATI.*Radeon HD *29.. 3 1 -ATI Radeon HD 3000 .*ATI.*Radeon HD *30.. 0 1 -ATI Radeon HD 3100 .*ATI.*Radeon HD *31.. 1 1 -ATI Radeon HD 3200 .*ATI.*Radeon HD *32.. 1 1 -ATI Radeon HD 3300 .*ATI.*Radeon HD *33.. 2 1 -ATI Radeon HD 3400 .*ATI.*Radeon HD *34.. 1 1 -ATI Radeon HD 3500 .*ATI.*Radeon HD *35.. 2 1 -ATI Radeon HD 3600 .*ATI.*Radeon HD *36.. 3 1 -ATI Radeon HD 3700 .*ATI.*Radeon HD *37.. 3 1 -ATI Radeon HD 3800 .*ATI.*Radeon HD *38.. 3 1 -ATI Radeon HD 4100 .*ATI.*Radeon HD *41.. 1 1 -ATI Radeon HD 4200 .*ATI.*Radeon HD *42.. 1 1 -ATI Radeon HD 4300 .*ATI.*Radeon HD *43.. 2 1 -ATI Radeon HD 4400 .*ATI.*Radeon HD *44.. 2 1 -ATI Radeon HD 4500 .*ATI.*Radeon HD *45.. 2 1 -ATI Radeon HD 4600 .*ATI.*Radeon HD *46.. 3 1 -ATI Radeon HD 4700 .*ATI.*Radeon HD *47.. 3 1 -ATI Radeon HD 4800 .*ATI.*Radeon HD *48.. 3 1 -ATI Radeon HD 5400 .*ATI.*Radeon HD *54.. 3 1 -ATI Radeon HD 5500 .*ATI.*Radeon HD *55.. 2 1 -ATI Radeon HD 5600 .*ATI.*Radeon HD *56.. 3 1 -ATI Radeon HD 5700 .*ATI.*Radeon HD *57.. 3 1 -ATI Radeon HD 5800 .*ATI.*Radeon HD *58.. 4 1 -ATI Radeon HD 5900 .*ATI.*Radeon HD *59.. 3 1 -ATI Radeon HD 6200 .*ATI.*Radeon HD *62.. 1 1 -ATI Radeon HD 6300 .*ATI.*Radeon HD *63.. 1 1 -ATI Radeon HD 6400 .*ATI.*Radeon HD *64.. 2 1 -ATI Radeon HD 6500 .*ATI.*Radeon HD *65.. 2 1 -ATI Radeon HD 6600 .*ATI.*Radeon HD *66.. 3 1 -ATI Radeon HD 6700 .*ATI.*Radeon HD *67.. 3 1 -ATI Radeon HD 6800 .*ATI.*Radeon HD *68.. 4 1 -ATI Radeon HD 6900 .*ATI.*Radeon HD *69.. 5 1 -ATI Radeon OpenGL .*ATI.*Radeon OpenGL.* 0 0 -ATI Radeon 2100 .*ATI.*Radeon 21.. 0 1 -ATI Radeon 3000 .*ATI.*Radeon 30.. 1 1 -ATI Radeon 3100 .*ATI.*Radeon 31.. 1 1 -ATI Radeon 5xxx .*ATI.*Radeon 5... 3 1 -ATI Radeon 7xxx .*ATI.*Radeon 7... 0 1 -ATI Radeon 8xxx .*ATI.*Radeon 8... 0 1 -ATI Radeon 9000 .*ATI.*Radeon 90.. 0 1 -ATI Radeon 9100 .*ATI.*Radeon 91.. 0 1 -ATI Radeon 9200 .*ATI.*Radeon 92.. 1 1 -ATI Radeon 9500 .*ATI.*Radeon 95.. 0 1 -ATI Radeon 9600 .*ATI.*Radeon 96.. 0 1 -ATI Radeon 9700 .*ATI.*Radeon 97.. 1 1 -ATI Radeon 9800 .*ATI.*Radeon 98.. 1 1 -ATI Radeon RV250 .*ATI.*RV250.* 0 1 -ATI Radeon RV600 .*ATI.*RV6.* 1 1 -ATI Radeon RX700 .*ATI.*RX70.* 1 1 -ATI Radeon RX800 .*ATI.*Radeon *RX80.* 2 1 -ATI RS880M .*ATI.*RS880M 1 1 -ATI Radeon RX9550 .*ATI.*RX9550.* 1 1 -ATI Radeon VE .*ATI.*Radeon.*VE.* 0 0 -ATI Radeon X300 .*ATI.*Radeon *X3.* 1 1 -ATI Radeon X400 .*ATI.*Radeon ?X4.* 0 1 -ATI Radeon X500 .*ATI.*Radeon ?X5.* 0 1 -ATI Radeon X600 .*ATI.*Radeon ?X6.* 1 1 -ATI Radeon X700 .*ATI.*Radeon ?X7.* 1 1 -ATI Radeon X800 .*ATI.*Radeon ?X8.* 2 1 -ATI Radeon X900 .*ATI.*Radeon ?X9.* 2 1 -ATI Radeon Xpress .*ATI.*Radeon Xpress.* 1 1 -ATI Rage 128 .*ATI.*Rage 128.* 0 1 -ATI R300 (9700) .*R300.* 1 1 -ATI R350 (9800) .*R350.* 1 1 -ATI R580 (X1900) .*R580.* 3 1 -ATI RC410 (Xpress 200) .*RC410.* 0 0 -ATI RS48x (Xpress 200x) .*RS48.* 0 0 -ATI RS600 (Xpress 3200) .*RS600.* 0 0 -ATI RV350 (9600) .*RV350.* 0 1 -ATI RV370 (X300) .*RV370.* 0 1 -ATI RV410 (X700) .*RV410.* 1 1 -ATI RV515 .*RV515.* 1 1 -ATI RV570 (X1900 GT/PRO) .*RV570.* 3 1 -ATI RV380 .*RV380.* 0 1 -ATI RV530 .*RV530.* 1 1 -ATI RX480 (Xpress 200P) .*RX480.* 0 1 -ATI RX700 .*RX700.* 1 1 -AMD ANTILLES (HD 6990) .*(AMD|ATI).*Antilles.* 3 1 -AMD BARTS (HD 6800) .*(AMD|ATI).*Barts.* 3 1 -AMD CAICOS (HD 6400) .*(AMD|ATI).*Caicos.* 3 1 -AMD CAYMAN (HD 6900) .*(AMD|ATI).*(Cayman|CAYMAM).* 3 1 -AMD CEDAR (HD 5450) .*(AMD|ATI).*Cedar.* 2 1 -AMD CYPRESS (HD 5800) .*(AMD|ATI).*Cypress.* 3 1 -AMD HEMLOCK (HD 5970) .*(AMD|ATI).*Hemlock.* 3 1 -AMD JUNIPER (HD 5700) .*(AMD|ATI).*Juniper.* 3 1 -AMD PARK .*(AMD|ATI).*Park.* 3 1 -AMD REDWOOD (HD 5500/5600) .*(AMD|ATI).*Redwood.* 3 1 -AMD TURKS (HD 6500/6600) .*(AMD|ATI).*Turks.* 3 1 -AMD RS780 (HD 3200) .*RS780.* 0 1 -AMD RS880 (HD 4200) .*RS880.* 1 1 -AMD RV610 (HD 2400) .*RV610.* 1 1 -AMD RV620 (HD 3400) .*RV620.* 1 1 -AMD RV630 (HD 2600) .*RV630.* 2 1 -AMD RV635 (HD 3600) .*RV635.* 3 1 -AMD RV670 (HD 3800) .*RV670.* 3 1 -AMD R680 (HD 3870 X2) .*R680.* 3 1 -AMD R700 (HD 4800 X2) .*R700.* 3 1 -AMD RV710 (HD 4300) .*RV710.* 1 1 -AMD RV730 (HD 4600) .*RV730.* 3 1 -AMD RV740 (HD 4700) .*RV740.* 3 1 -AMD RV770 (HD 4800) .*RV770.* 3 1 -AMD RV790 (HD 4800) .*RV790.* 3 1 -ATI 760G/Radeon 3000 .*ATI.*AMD 760G.* 1 1 -ATI 780L/Radeon 3000 .*ATI.*AMD 780L.* 1 1 -ATI Radeon DDR .*ATI.*Radeon ?DDR.* 0 1 -ATI FirePro 2000 .*ATI.*FirePro 2.* 1 1 -ATI FirePro 3000 .*ATI.*FirePro V3.* 1 1 -ATI FirePro 4000 .*ATI.*FirePro V4.* 2 1 -ATI FirePro 5000 .*ATI.*FirePro V5.* 3 1 -ATI FirePro 7000 .*ATI.*FirePro V7.* 3 1 -ATI FirePro M .*ATI.*FirePro M.* 3 1 -ATI Technologies .*ATI *Technologies.* 4 1 -ATI R300 (9700) .*R300.* 1 1 -ATI Radeon .*ATI.*(Diamond|Radeon).* 0 1 -Intel X3100 .*Intel.*X3100.* 1 1 -Intel 830M .*Intel.*830M 0 0 -Intel 845G .*Intel.*845G 1 0 -Intel 855GM .*Intel.*855GM 0 0 -Intel 865G .*Intel.*865G 1 0 -Intel 900 .*Intel.*900.*900 0 0 -Intel 915GM .*Intel.*915GM 1 0 -Intel 915G .*Intel.*915G 1 0 -Intel 945GM .*Intel.*945GM.* 1 1 -Intel 945G .*Intel.*945G.* 1 1 -Intel 950 .*Intel.*950.* 1 1 -Intel 965 .*Intel.*965.* 1 1 -Intel G33 .*Intel.*G33.* 0 0 -Intel G41 .*Intel.*G41.* 1 1 -Intel G45 .*Intel.*G45.* 1 1 -Intel Bear Lake .*Intel.*Bear Lake.* 1 0 -Intel Broadwater .*Intel.*Broadwater.* 0 0 -Intel Brookdale .*Intel.*Brookdale.* 0 0 -Intel Cantiga .*Intel.*Cantiga.* 1 0 -Intel Eaglelake .*Intel.*Eaglelake.* 1 1 -Intel Graphics Media HD .*Intel.*Graphics Media.*HD.* 1 1 -Intel HD Graphics .*Intel.*HD Graphics.* 2 1 -Intel Mobile 4 Series .*Intel.*Mobile.* 4 Series.* 1 1 -Intel Media Graphics HD .*Intel.*Media Graphics HD.* 0 1 -Intel Montara .*Intel.*Montara.* 0 0 -Intel Pineview .*Intel.*Pineview.* 1 1 -Intel Springdale .*Intel.*Springdale.* 0 0 -Intel HD Graphics 2000 .*Intel.*HD2000.* 1 1 -Intel HD Graphics 3000 .*Intel.*HD3000.* 2 1 -Matrox .*Matrox.* 0 0 -Mesa .*Mesa.* 1 0 -NVIDIA 205 .*NVIDIA .*GeForce 205.* 2 1 -NVIDIA 210 .*NVIDIA .*GeForce 210.* 2 1 -NVIDIA 310M .*NVIDIA .*GeForce 310M.* 2 1 -NVIDIA 310 .*NVIDIA .*GeForce 310.* 2 1 -NVIDIA 315M .*NVIDIA .*GeForce 315M.* 2 1 -NVIDIA 315 .*NVIDIA .*GeForce 315.* 3 1 -NVIDIA 320M .*NVIDIA .*GeForce 320M.* 3 1 -NVIDIA G100M .*NVIDIA .*100M.* 2 1 -NVIDIA G100 .*NVIDIA .*100.* 1 1 -NVIDIA G102M .*NVIDIA .*102M.* 0 1 -NVIDIA G103M .*NVIDIA .*103M.* 0 1 -NVIDIA G105M .*NVIDIA .*105M.* 2 1 -NVIDIA G 110M .*NVIDIA .*110M.* 0 1 -NVIDIA G 120M .*NVIDIA .*120M.* 1 1 -NVIDIA G 200 .*NVIDIA .*200(M)?.* 1 1 -NVIDIA G 205M .*NVIDIA .*205(M)?.* 0 1 -NVIDIA G 210 .*NVIDIA .*210(M)?.* 2 1 -NVIDIA 305M .*NVIDIA .*305(M)?.* 1 1 -NVIDIA G 310M .*NVIDIA .*310(M)?.* 2 1 -NVIDIA G 315 .*NVIDIA .*315(M)?.* 2 1 -NVIDIA G 320M .*NVIDIA .*320(M)?.* 2 1 -NVIDIA G 405 .*NVIDIA .*405(M)?.* 3 1 -NVIDIA G 410M .*NVIDIA .*410(M)?.* 3 1 -NVIDIA GT 120M .*NVIDIA .*GT *120(M)?.* 2 1 -NVIDIA GT 120 .*NVIDIA .*GT.*120 2 1 -NVIDIA GT 130M .*NVIDIA .*GT *130(M)?.* 3 1 -NVIDIA GT 140M .*NVIDIA .*GT *140(M)?.* 2 1 -NVIDIA GT 150M .*NVIDIA .*GT(S)? *150(M)?.* 2 1 -NVIDIA GT 160M .*NVIDIA .*GT *160(M)?.* 2 1 -NVIDIA GT 220M .*NVIDIA .*GT *220(M)?.* 3 1 -NVIDIA GT 230M .*NVIDIA .*GT *230(M)?.* 3 1 -NVIDIA GT 240M .*NVIDIA .*GT *240(M)?.* 3 1 -NVIDIA GT 250M .*NVIDIA .*GT *250(M)?.* 2 1 -NVIDIA GT 260M .*NVIDIA .*GT *260(M)?.* 2 1 -NVIDIA GT 320M .*NVIDIA .*GT *320(M)?.* 2 1 -NVIDIA GT 325M .*NVIDIA .*GT *325(M)?.* 0 1 -NVIDIA GT 330M .*NVIDIA .*GT *330(M)?.* 3 1 -NVIDIA GT 335M .*NVIDIA .*GT *335(M)?.* 2 1 -NVIDIA GT 340M .*NVIDIA .*GT *340(M)?.* 2 1 -NVIDIA GT 415M .*NVIDIA .*GT *415(M)?.* 2 1 -NVIDIA GT 420M .*NVIDIA .*GT *420(M)?.* 3 1 -NVIDIA GT 425M .*NVIDIA .*GT *425(M)?.* 4 1 -NVIDIA GT 430M .*NVIDIA .*GT *430(M)?.* 3 1 -NVIDIA GT 435M .*NVIDIA .*GT *435(M)?.* 4 1 -NVIDIA GT 440M .*NVIDIA .*GT *440(M)?.* 3 1 -NVIDIA GT 445M .*NVIDIA .*GT *445(M)?.* 3 1 -NVIDIA GT 450M .*NVIDIA .*GT *450(M)?.* 3 1 -NVIDIA GT 520M .*NVIDIA .*GT *52.(M)?.* 3 1 -NVIDIA GT 530M .*NVIDIA .*GT *530(M)?.* 3 1 -NVIDIA GT 540M .*NVIDIA .*GT *54.(M)?.* 3 1 -NVIDIA GT 550M .*NVIDIA .*GT *550(M)?.* 3 1 -NVIDIA GT 555M .*NVIDIA .*GT *555(M)?.* 3 1 -NVIDIA GTS 160M .*NVIDIA .*GT(S)? *160(M)?.* 2 1 -NVIDIA GTS 240 .*NVIDIA .*GTS *24.* 3 1 -NVIDIA GTS 250 .*NVIDIA .*GTS *25.* 4 1 -NVIDIA GTS 350M .*NVIDIA .*GTS *350M.* 3 1 -NVIDIA GTS 360M .*NVIDIA .*GTS *360M.* 5 1 -NVIDIA GTS 360 .*NVIDIA .*GTS *360.* 3 1 -NVIDIA GTS 450 .*NVIDIA .*GTS *45.* 4 1 -NVIDIA GTX 260 .*NVIDIA .*GTX *26.* 4 1 -NVIDIA GTX 275 .*NVIDIA .*GTX *275.* 4 1 -NVIDIA GTX 270 .*NVIDIA .*GTX *27.* 3 1 -NVIDIA GTX 285 .*NVIDIA .*GTX *285.* 5 1 -NVIDIA GTX 280 .*NVIDIA .*GTX *280.* 4 1 -NVIDIA GTX 290 .*NVIDIA .*GTX *290.* 3 1 -NVIDIA GTX 295 .*NVIDIA .*GTX *295.* 5 1 -NVIDIA GTX 460M .*NVIDIA .*GTX *460M.* 4 1 -NVIDIA GTX 465 .*NVIDIA .*GTX *465.* 5 1 -NVIDIA GTX 460 .*NVIDIA .*GTX *46.* 5 1 -NVIDIA GTX 470M .*NVIDIA .*GTX *470M.* 3 1 -NVIDIA GTX 470 .*NVIDIA .*GTX *47.* 5 1 -NVIDIA GTX 480M .*NVIDIA .*GTX *480M.* 3 1 -NVIDIA GTX 485M .*NVIDIA .*GTX *485M.* 3 1 -NVIDIA GTX 480 .*NVIDIA .*GTX *48.* 5 1 -NVIDIA GTX 530 .*NVIDIA .*GTX *53.* 3 1 -NVIDIA GTX 550 .*NVIDIA .*GTX *55.* 5 1 -NVIDIA GTX 560 .*NVIDIA .*GTX *56.* 5 1 -NVIDIA GTX 570 .*NVIDIA .*GTX *57.* 5 1 -NVIDIA GTX 580M .*NVIDIA .*GTX *580M.* 3 1 -NVIDIA GTX 580 .*NVIDIA .*GTX *58.* 5 1 -NVIDIA GTX 590 .*NVIDIA .*GTX *59.* 3 1 -NVIDIA C51 .*NVIDIA .*C51.* 0 1 -NVIDIA G72 .*NVIDIA .*G72.* 1 1 -NVIDIA G73 .*NVIDIA .*G73.* 1 1 -NVIDIA G84 .*NVIDIA .*G84.* 2 1 -NVIDIA G86 .*NVIDIA .*G86.* 3 1 -NVIDIA G92 .*NVIDIA .*G92.* 3 1 -NVIDIA GeForce .*GeForce 256.* 0 0 -NVIDIA GeForce 2 .*GeForce ?2 ?.* 0 1 -NVIDIA GeForce 3 .*GeForce ?3 ?.* 0 1 -NVIDIA GeForce 3 Ti .*GeForce ?3 Ti.* 0 1 -NVIDIA GeForce 4 .*NVIDIA .*GeForce ?4.* 1 1 -NVIDIA GeForce 4 Go .*NVIDIA .*GeForce ?4.*Go.* 0 1 -NVIDIA GeForce 4 MX .*NVIDIA .*GeForce ?4 MX.* 0 1 -NVIDIA GeForce 4 PCX .*NVIDIA .*GeForce ?4 PCX.* 0 1 -NVIDIA GeForce 4 Ti .*NVIDIA .*GeForce ?4 Ti.* 0 1 -NVIDIA GeForce 6100 .*NVIDIA .*GeForce 61.* 3 1 -NVIDIA GeForce 6200 .*NVIDIA .*GeForce 62.* 0 1 -NVIDIA GeForce 6500 .*NVIDIA .*GeForce 65.* 0 1 -NVIDIA GeForce 6600 .*NVIDIA .*GeForce 66.* 2 1 -NVIDIA GeForce 6700 .*NVIDIA .*GeForce 67.* 2 1 -NVIDIA GeForce 6800 .*NVIDIA .*GeForce 68.* 2 1 -NVIDIA GeForce 7000 .*NVIDIA .*GeForce 70.* 1 1 -NVIDIA GeForce 7100 .*NVIDIA .*GeForce 71.* 1 1 -NVIDIA GeForce 7200 .*NVIDIA .*GeForce 72.* 1 1 -NVIDIA GeForce 7300 .*NVIDIA .*GeForce 73.* 1 1 -NVIDIA GeForce 7500 .*NVIDIA .*GeForce 75.* 1 1 -NVIDIA GeForce 7600 .*NVIDIA .*GeForce 76.* 2 1 -NVIDIA GeForce 7800 .*NVIDIA .*GeForce 78.* 2 1 -NVIDIA GeForce 7900 .*NVIDIA .*GeForce 79.* 2 1 -NVIDIA GeForce 8100 .*NVIDIA .*GeForce 81.* 1 1 -NVIDIA GeForce 8200M .*NVIDIA .*GeForce 8200M.* 1 1 -NVIDIA GeForce 8200 .*NVIDIA .*GeForce 82.* 1 1 -NVIDIA GeForce 8300 .*NVIDIA .*GeForce 83.* 2 1 -NVIDIA GeForce 8400M .*NVIDIA .*GeForce 8400M.* 1 1 -NVIDIA GeForce 8400 .*NVIDIA .*GeForce 84.* 2 1 -NVIDIA GeForce 8500 .*NVIDIA .*GeForce 85.* 2 1 -NVIDIA GeForce 8600M .*NVIDIA .*GeForce 8600M.* 2 1 -NVIDIA GeForce 8600 .*NVIDIA .*GeForce 86.* 3 1 -NVIDIA GeForce 8700M .*NVIDIA .*GeForce 8700M.* 3 1 -NVIDIA GeForce 8700 .*NVIDIA .*GeForce 87.* 3 1 -NVIDIA GeForce 8800M .*NVIDIA .*GeForce 8800M.* 3 1 -NVIDIA GeForce 8800 .*NVIDIA .*GeForce 88.* 3 1 -NVIDIA GeForce 9100M .*NVIDIA .*GeForce 9100M.* 0 1 -NVIDIA GeForce 9100 .*NVIDIA .*GeForce 91.* 0 1 -NVIDIA GeForce 9200M .*NVIDIA .*GeForce 9200M.* 1 1 -NVIDIA GeForce 9200 .*NVIDIA .*GeForce 92.* 1 1 -NVIDIA GeForce 9300M .*NVIDIA .*GeForce 9300M.* 1 1 -NVIDIA GeForce 9300 .*NVIDIA .*GeForce 93.* 1 1 -NVIDIA GeForce 9400M .*NVIDIA .*GeForce 9400M.* 2 1 -NVIDIA GeForce 9400 .*NVIDIA .*GeForce 94.* 3 1 -NVIDIA GeForce 9500M .*NVIDIA .*GeForce 9500M.* 2 1 -NVIDIA GeForce 9500 .*NVIDIA .*GeForce 95.* 2 1 -NVIDIA GeForce 9600M .*NVIDIA .*GeForce 9600M.* 2 1 -NVIDIA GeForce 9600 .*NVIDIA .*GeForce 96.* 3 1 -NVIDIA GeForce 9700M .*NVIDIA .*GeForce 9700M.* 2 1 -NVIDIA GeForce 9800M .*NVIDIA .*GeForce 9800M.* 2 1 -NVIDIA GeForce 9800 .*NVIDIA .*GeForce 98.* 3 1 -NVIDIA GeForce FX 5100 .*NVIDIA .*GeForce FX 51.* 0 1 -NVIDIA GeForce FX 5200 .*NVIDIA .*GeForce FX 52.* 0 1 -NVIDIA GeForce FX 5300 .*NVIDIA .*GeForce FX 53.* 0 1 -NVIDIA GeForce FX 5500 .*NVIDIA .*GeForce FX 55.* 0 1 -NVIDIA GeForce FX 5600 .*NVIDIA .*GeForce FX 56.* 0 1 -NVIDIA GeForce FX 5700 .*NVIDIA .*GeForce FX 57.* 1 1 -NVIDIA GeForce FX 5800 .*NVIDIA .*GeForce FX 58.* 1 1 -NVIDIA GeForce FX 5900 .*NVIDIA .*GeForce FX 59.* 1 1 -NVIDIA GeForce FX Go5100 .*NVIDIA .*GeForce FX Go51.* 0 1 -NVIDIA GeForce FX Go5200 .*NVIDIA .*GeForce FX Go52.* 0 1 -NVIDIA GeForce FX Go5300 .*NVIDIA .*GeForce FX Go53.* 0 1 -NVIDIA GeForce FX Go5500 .*NVIDIA .*GeForce FX Go55.* 0 1 -NVIDIA GeForce FX Go5600 .*NVIDIA .*GeForce FX Go56.* 0 1 -NVIDIA GeForce FX Go5700 .*NVIDIA .*GeForce FX Go57.* 1 1 -NVIDIA GeForce FX Go5800 .*NVIDIA .*GeForce FX Go58.* 1 1 -NVIDIA GeForce FX Go5900 .*NVIDIA .*GeForce FX Go59.* 1 1 -NVIDIA GeForce FX Go5xxx .*NVIDIA .*GeForce FX Go.* 0 1 -NVIDIA GeForce Go 6100 .*NVIDIA .*GeForce Go 61.* 0 1 -NVIDIA GeForce Go 6200 .*NVIDIA .*GeForce Go 62.* 0 1 -NVIDIA GeForce Go 6400 .*NVIDIA .*GeForce Go 64.* 1 1 -NVIDIA GeForce Go 6500 .*NVIDIA .*GeForce Go 65.* 1 1 -NVIDIA GeForce Go 6600 .*NVIDIA .*GeForce Go 66.* 1 1 -NVIDIA GeForce Go 6700 .*NVIDIA .*GeForce Go 67.* 1 1 -NVIDIA GeForce Go 6800 .*NVIDIA .*GeForce Go 68.* 1 1 -NVIDIA GeForce Go 7200 .*NVIDIA .*GeForce Go 72.* 1 1 -NVIDIA GeForce Go 7300 LE .*NVIDIA .*GeForce Go 73.*LE.* 0 1 -NVIDIA GeForce Go 7300 .*NVIDIA .*GeForce Go 73.* 1 1 -NVIDIA GeForce Go 7400 .*NVIDIA .*GeForce Go 74.* 1 1 -NVIDIA GeForce Go 7600 .*NVIDIA .*GeForce Go 76.* 2 1 -NVIDIA GeForce Go 7700 .*NVIDIA .*GeForce Go 77.* 2 1 -NVIDIA GeForce Go 7800 .*NVIDIA .*GeForce Go 78.* 2 1 -NVIDIA GeForce Go 7900 .*NVIDIA .*GeForce Go 79.* 2 1 -NVIDIA D9M .*NVIDIA .*D9M.* 1 1 -NVIDIA G94 .*NVIDIA .*G94.* 3 1 -NVIDIA GeForce Go 6 .*GeForce Go 6.* 1 1 -NVIDIA ION 2 .*NVIDIA .*ION 2.* 2 1 -NVIDIA ION .*NVIDIA .*ION.* 5 1 -NVIDIA NB8M .*NVIDIA .*NB8M.* 1 1 -NVIDIA NB8P .*NVIDIA .*NB8P.* 2 1 -NVIDIA NB9E .*NVIDIA .*NB9E.* 3 1 -NVIDIA NB9M .*NVIDIA .*NB9M.* 1 1 -NVIDIA NB9P .*NVIDIA .*NB9P.* 2 1 -NVIDIA N10 .*NVIDIA .*N10.* 1 1 -NVIDIA GeForce PCX .*GeForce PCX.* 0 1 -NVIDIA Generic .*NVIDIA .*Unknown.* 0 0 -NVIDIA NV17 .*NVIDIA .*NV17.* 0 1 -NVIDIA NV34 .*NVIDIA .*NV34.* 0 1 -NVIDIA NV35 .*NVIDIA .*NV35.* 0 1 -NVIDIA NV36 .*NVIDIA .*NV36.* 1 1 -NVIDIA NV41 .*NVIDIA .*NV41.* 1 1 -NVIDIA NV43 .*NVIDIA .*NV43.* 1 1 -NVIDIA NV44 .*NVIDIA .*NV44.* 1 1 -NVIDIA nForce .*NVIDIA .*nForce.* 0 0 -NVIDIA MCP51 .*NVIDIA .*MCP51.* 1 1 -NVIDIA MCP61 .*NVIDIA .*MCP61.* 1 1 -NVIDIA MCP67 .*NVIDIA .*MCP67.* 1 1 -NVIDIA MCP68 .*NVIDIA .*MCP68.* 1 1 -NVIDIA MCP73 .*NVIDIA .*MCP73.* 1 1 -NVIDIA MCP77 .*NVIDIA .*MCP77.* 1 1 -NVIDIA MCP78 .*NVIDIA .*MCP78.* 1 1 -NVIDIA MCP79 .*NVIDIA .*MCP79.* 1 1 -NVIDIA MCP7A .*NVIDIA .*MCP7A.* 1 1 -NVIDIA Quadro2 .*Quadro2.* 0 1 -NVIDIA Quadro 1000M .*Quadro.*1000M.* 2 1 -NVIDIA Quadro 2000 M/D .*Quadro.*2000.* 3 1 -NVIDIA Quadro 3000M .*Quadro.*3000M.* 3 1 -NVIDIA Quadro 4000M .*Quadro.*4000M.* 3 1 -NVIDIA Quadro 4000 .*Quadro *4000.* 3 1 -NVIDIA Quadro 50x0 M .*Quadro.*50.0.* 3 1 -NVIDIA Quadro 6000 .*Quadro.*6000.* 3 1 -NVIDIA Quadro 400 .*Quadro.*400.* 2 1 -NVIDIA Quadro 600 .*Quadro.*600.* 2 1 -NVIDIA Quadro4 .*Quadro4.* 0 1 -NVIDIA Quadro DCC .*Quadro DCC.* 0 1 -NVIDIA Quadro CX .*Quadro.*CX.* 3 1 -NVIDIA Quadro FX 770M .*Quadro.*FX *770M.* 2 1 -NVIDIA Quadro FX 1500M .*Quadro.*FX *1500M.* 1 1 -NVIDIA Quadro FX 1600M .*Quadro.*FX *1600M.* 2 1 -NVIDIA Quadro FX 2500M .*Quadro.*FX *2500M.* 2 1 -NVIDIA Quadro FX 2700M .*Quadro.*FX *2700M.* 3 1 -NVIDIA Quadro FX 2800M .*Quadro.*FX *2800M.* 3 1 -NVIDIA Quadro FX 3500 .*Quadro.*FX *3500.* 2 1 -NVIDIA Quadro FX 3600 .*Quadro.*FX *3600.* 3 1 -NVIDIA Quadro FX 3700 .*Quadro.*FX *3700.* 3 1 -NVIDIA Quadro FX 3800 .*Quadro.*FX *3800.* 3 1 -NVIDIA Quadro FX 4500 .*Quadro.*FX *45.* 3 1 -NVIDIA Quadro FX 880M .*Quadro.*FX *880M.* 3 1 -NVIDIA Quadro FX 4800 .*NVIDIA .*Quadro *FX *4800.* 3 1 -NVIDIA Quadro FX .*Quadro FX.* 1 1 -NVIDIA Quadro NVS 1xxM .*Quadro NVS *1.[05]M.* 0 1 -NVIDIA Quadro NVS 300M .*NVIDIA .*NVS *300M.* 2 1 -NVIDIA Quadro NVS 320M .*NVIDIA .*NVS *320M.* 2 1 -NVIDIA Quadro NVS 2100M .*NVIDIA .*NVS *2100M.* 2 1 -NVIDIA Quadro NVS 3100M .*NVIDIA .*NVS *3100M.* 2 1 -NVIDIA Quadro NVS 4200M .*NVIDIA .*NVS *4200M.* 2 1 -NVIDIA Quadro NVS 5100M .*NVIDIA .*NVS *5100M.* 2 1 -NVIDIA Quadro NVS .*NVIDIA .*NVS 0 1 -NVIDIA RIVA TNT .*RIVA TNT.* 0 0 -S3 .*S3 Graphics.* 1 0 -SiS SiS.* 1 0 -Trident Trident.* 0 0 -Tungsten Graphics Tungsten.* 0 0 -XGI XGI.* 0 0 -VIA VIA.* 0 0 -Apple Generic Apple.*Generic.* 0 0 -Apple Software Renderer Apple.*Software Renderer.* 0 0 -Humper Humper.* 0 1 +3Dfx .*3Dfx.* 0 0 0 +3Dlabs .*3Dlabs.* 0 0 0 +ATI 3D-Analyze .*ATI.*3D-Analyze.* 0 0 0 +ATI All-in-Wonder 7500 .*ATI.*All-in-Wonder 75.* 0 1 0 +ATI All-in-Wonder 8500 .*ATI.*All-in-Wonder 85.* 0 1 0 +ATI All-in-Wonder 9200 .*ATI.*All-in-Wonder 92.* 0 1 0 +ATI All-in-Wonder 9xxx .*ATI.*All-in-Wonder 9.* 1 1 0 +ATI All-in-Wonder HD .*ATI.*All-in-Wonder HD.* 1 1 1 +ATI All-in-Wonder X600 .*ATI.*All-in-Wonder X6.* 1 1 0 +ATI All-in-Wonder X800 .*ATI.*All-in-Wonder X8.* 1 1 1 +ATI All-in-Wonder X1800 .*ATI.*All-in-Wonder X18.* 3 1 0 +ATI All-in-Wonder X1900 .*ATI.*All-in-Wonder X19.* 3 1 0 +ATI All-in-Wonder PCI-E .*ATI.*All-in-Wonder.*PCI-E.* 1 1 0 +ATI All-in-Wonder Radeon .*ATI.*All-in-Wonder Radeon.* 0 1 0 +ATI ASUS ARES .*ATI.*ASUS.*ARES.* 3 1 0 +ATI ASUS A9xxx .*ATI.*ASUS.*A9.* 1 1 0 +ATI ASUS AH24xx .*ATI.*ASUS.*AH24.* 1 1 1 +ATI ASUS AH26xx .*ATI.*ASUS.*AH26.* 1 1 1 +ATI ASUS AH34xx .*ATI.*ASUS.*AH34.* 1 1 1 +ATI ASUS AH36xx .*ATI.*ASUS.*AH36.* 1 1 1 +ATI ASUS AH46xx .*ATI.*ASUS.*AH46.* 2 1 1 +ATI ASUS AX3xx .*ATI.*ASUS.*AX3.* 1 1 0 +ATI ASUS AX5xx .*ATI.*ASUS.*AX5.* 1 1 0 +ATI ASUS AX8xx .*ATI.*ASUS.*AX8.* 2 1 0 +ATI ASUS EAH24xx .*ATI.*ASUS.*EAH24.* 2 1 0 +ATI ASUS EAH26xx .*ATI.*ASUS.*EAH26.* 3 1 0 +ATI ASUS EAH29xx .*ATI.*ASUS.*EAH29.* 3 1 0 +ATI ASUS EAH34xx .*ATI.*ASUS.*EAH34.* 1 1 0 +ATI ASUS EAH36xx .*ATI.*ASUS.*EAH36.* 3 1 0 +ATI ASUS EAH38xx .*ATI.*ASUS.*EAH38.* 2 1 1 +ATI ASUS EAH43xx .*ATI.*ASUS.*EAH43.* 2 1 1 +ATI ASUS EAH45xx .*ATI.*ASUS.*EAH45.* 1 1 0 +ATI ASUS EAH48xx .*ATI.*ASUS.*EAH48.* 3 1 1 +ATI ASUS EAH57xx .*ATI.*ASUS.*EAH57.* 3 1 1 +ATI ASUS EAH58xx .*ATI.*ASUS.*EAH58.* 5 1 1 +ATI ASUS EAH6xxx .*ATI.*ASUS.*EAH6.* 3 1 1 +ATI ASUS Radeon X1xxx .*ATI.*ASUS.*X1.* 2 1 1 +ATI Radeon X7xx .*ATI.*ASUS.*X7.* 1 1 0 +ATI Radeon X19xx .*ATI.*(Radeon|Diamond) X19.* ?.* 2 1 1 +ATI Radeon X18xx .*ATI.*(Radeon|Diamond) X18.* ?.* 3 1 1 +ATI Radeon X17xx .*ATI.*(Radeon|Diamond) X17.* ?.* 1 1 1 +ATI Radeon X16xx .*ATI.*(Radeon|Diamond) X16.* ?.* 1 1 1 +ATI Radeon X15xx .*ATI.*(Radeon|Diamond) X15.* ?.* 1 1 1 +ATI Radeon X13xx .*ATI.*(Radeon|Diamond) X13.* ?.* 1 1 1 +ATI Radeon X1xxx .*ATI.*(Radeon|Diamond) X1.. ?.* 0 1 1 +ATI Radeon X2xxx .*ATI.*(Radeon|Diamond) X2.. ?.* 1 1 1 +ATI Display Adapter .*ATI.*display adapter.* 1 1 1 +ATI FireGL 5200 .*ATI.*FireGL V52.* 1 1 1 +ATI FireGL 5xxx .*ATI.*FireGL V5.* 3 1 1 +ATI FireGL .*ATI.*Fire.*GL.* 4 1 1 +ATI FirePro M3900 .*ATI.*FirePro.*M39.* 2 1 0 +ATI FirePro M5800 .*ATI.*FirePro.*M58.* 3 1 0 +ATI FirePro M7740 .*ATI.*FirePro.*M77.* 3 1 0 +ATI FirePro M7820 .*ATI.*FirePro.*M78.* 5 1 1 +ATI FireMV .*ATI.*FireMV.* 0 1 1 +ATI Geforce 9500 GT .*ATI.*Geforce 9500 *GT.* 3 1 1 +ATI Geforce 9600 GT .*ATI.*Geforce 9600 *GT.* 3 1 1 +ATI Geforce 9800 GT .*ATI.*Geforce 9800 *GT.* 3 1 1 +ATI Generic .*ATI.*Generic.* 0 0 0 +ATI Hercules 9800 .*ATI.*Hercules.*9800.* 1 1 0 +ATI IGP 340M .*ATI.*IGP.*340M.* 0 0 0 +ATI M52 .*ATI.*M52.* 1 1 0 +ATI M54 .*ATI.*M54.* 1 1 0 +ATI M56 .*ATI.*M56.* 1 1 0 +ATI M71 .*ATI.*M71.* 1 1 0 +ATI M72 .*ATI.*M72.* 1 1 0 +ATI M76 .*ATI.*M76.* 3 1 0 +ATI Radeon HD 64xx .*ATI.*AMD Radeon.* HD [67]4..[MG] 2 1 1 +ATI Radeon HD 65xx .*ATI.*AMD Radeon.* HD [67]5..[MG] 2 1 1 +ATI Radeon HD 66xx .*ATI.*AMD Radeon.* HD [67]6..[MG] 3 1 1 +ATI Mobility Radeon 4100 .*ATI.*Mobility.*41.. 1 1 1 +ATI Mobility Radeon 7xxx .*ATI.*Mobility.*Radeon 7.* 0 1 1 +ATI Mobility Radeon 8xxx .*ATI.*Mobility.*Radeon 8.* 0 1 0 +ATI Mobility Radeon 9800 .*ATI.*Mobility.*98.* 1 1 0 +ATI Mobility Radeon 9700 .*ATI.*Mobility.*97.* 0 1 1 +ATI Mobility Radeon 9600 .*ATI.*Mobility.*96.* 1 1 1 +ATI Mobility Radeon HD 530v .*ATI.*Mobility.*HD *530v.* 1 1 1 +ATI Mobility Radeon HD 540v .*ATI.*Mobility.*HD *540v.* 1 1 1 +ATI Mobility Radeon HD 545v .*ATI.*Mobility.*HD *545v.* 2 1 1 +ATI Mobility Radeon HD 550v .*ATI.*Mobility.*HD *550v.* 3 1 1 +ATI Mobility Radeon HD 560v .*ATI.*Mobility.*HD *560v.* 3 1 1 +ATI Mobility Radeon HD 565v .*ATI.*Mobility.*HD *565v.* 3 1 1 +ATI Mobility Radeon HD 2300 .*ATI.*Mobility.*HD *23.* 0 1 1 +ATI Mobility Radeon HD 2400 .*ATI.*Mobility.*HD *24.* 1 1 1 +ATI Mobility Radeon HD 2600 .*ATI.*Mobility.*HD *26.* 0 1 1 +ATI Mobility Radeon HD 2700 .*ATI.*Mobility.*HD *27.* 3 1 0 +ATI Mobility Radeon HD 3100 .*ATI.*Mobility.*HD *31.* 0 1 0 +ATI Mobility Radeon HD 3200 .*ATI.*Mobility.*HD *32.* 0 1 0 +ATI Mobility Radeon HD 3400 .*ATI.*Mobility.*HD *34.* 1 1 1 +ATI Mobility Radeon HD 3600 .*ATI.*Mobility.*HD *36.* 1 1 1 +ATI Mobility Radeon HD 3800 .*ATI.*Mobility.*HD *38.* 3 1 1 +ATI Mobility Radeon HD 4200 .*ATI.*Mobility.*HD *42.* 1 1 1 +ATI Mobility Radeon HD 4300 .*ATI.*Mobility.*HD *43.* 1 1 1 +ATI Mobility Radeon HD 4500 .*ATI.*Mobility.*HD *45.* 1 1 1 +ATI Mobility Radeon HD 4600 .*ATI.*Mobility.*HD *46.* 2 1 1 +ATI Mobility Radeon HD 4800 .*ATI.*Mobility.*HD *48.* 3 1 1 +ATI Mobility Radeon HD 5100 .*ATI.*Mobility.*HD *51.* 3 1 1 +ATI Mobility Radeon HD 5300 .*ATI.*Mobility.*HD *53.* 3 1 0 +ATI Mobility Radeon HD 5400 .*ATI.*Mobility.*HD *54.* 2 1 1 +ATI Mobility Radeon HD 5500 .*ATI.*Mobility.*HD *55.* 3 1 0 +ATI Mobility Radeon HD 5600 .*ATI.*Mobility.*HD *56.* 3 1 1 +ATI Mobility Radeon HD 5700 .*ATI.*Mobility.*HD *57.* 1 1 1 +ATI Mobility Radeon HD 6200 .*ATI.*Mobility.*HD *62.* 3 1 0 +ATI Mobility Radeon HD 6300 .*ATI.*Mobility.*HD *63.* 3 1 1 +ATI Mobility Radeon HD 6400M .*ATI.*Mobility.*HD *64.* 3 1 0 +ATI Mobility Radeon HD 6500M .*ATI.*Mobility.*HD *65.* 5 1 1 +ATI Mobility Radeon HD 6600M .*ATI.*Mobility.*HD *66.* 3 1 0 +ATI Mobility Radeon HD 6700M .*ATI.*Mobility.*HD *67.* 3 1 0 +ATI Mobility Radeon HD 6800M .*ATI.*Mobility.*HD *68.* 3 1 0 +ATI Mobility Radeon HD 6900M .*ATI.*Mobility.*HD *69.* 3 1 0 +ATI Radeon HD 2300 .*ATI.*Radeon HD *23.. 0 1 1 +ATI Radeon HD 2400 .*ATI.*Radeon HD *24.. 1 1 1 +ATI Radeon HD 2600 .*ATI.*Radeon HD *26.. 2 1 1 +ATI Radeon HD 2900 .*ATI.*Radeon HD *29.. 3 1 1 +ATI Radeon HD 3000 .*ATI.*Radeon HD *30.. 0 1 0 +ATI Radeon HD 3100 .*ATI.*Radeon HD *31.. 1 1 0 +ATI Radeon HD 3200 .*ATI.*Radeon HD *32.. 1 1 1 +ATI Radeon HD 3300 .*ATI.*Radeon HD *33.. 1 1 1 +ATI Radeon HD 3400 .*ATI.*Radeon HD *34.. 1 1 1 +ATI Radeon HD 3500 .*ATI.*Radeon HD *35.. 2 1 0 +ATI Radeon HD 3600 .*ATI.*Radeon HD *36.. 3 1 1 +ATI Radeon HD 3700 .*ATI.*Radeon HD *37.. 3 1 0 +ATI Radeon HD 3800 .*ATI.*Radeon HD *38.. 3 1 1 +ATI Radeon HD 4100 .*ATI.*Radeon HD *41.. 1 1 0 +ATI Radeon HD 4200 .*ATI.*Radeon HD *42.. 1 1 1 +ATI Radeon HD 4300 .*ATI.*Radeon HD *43.. 2 1 1 +ATI Radeon HD 4400 .*ATI.*Radeon HD *44.. 2 1 0 +ATI Radeon HD 4500 .*ATI.*Radeon HD *45.. 2 1 1 +ATI Radeon HD 4600 .*ATI.*Radeon HD *46.. 3 1 1 +ATI Radeon HD 4700 .*ATI.*Radeon HD *47.. 3 1 1 +ATI Radeon HD 4800 .*ATI.*Radeon HD *48.. 3 1 1 +ATI Radeon HD 5400 .*ATI.*Radeon HD *54.. 3 1 1 +ATI Radeon HD 5500 .*ATI.*Radeon HD *55.. 3 1 1 +ATI Radeon HD 5600 .*ATI.*Radeon HD *56.. 3 1 1 +ATI Radeon HD 5700 .*ATI.*Radeon HD *57.. 3 1 1 +ATI Radeon HD 5800 .*ATI.*Radeon HD *58.. 4 1 1 +ATI Radeon HD 5900 .*ATI.*Radeon HD *59.. 4 1 1 +ATI Radeon HD 6200 .*ATI.*Radeon HD *62.. 0 1 1 +ATI Radeon HD 6300 .*ATI.*Radeon HD *63.. 1 1 1 +ATI Radeon HD 6400 .*ATI.*Radeon HD *64.. 3 1 1 +ATI Radeon HD 6500 .*ATI.*Radeon HD *65.. 3 1 1 +ATI Radeon HD 6600 .*ATI.*Radeon HD *66.. 3 1 1 +ATI Radeon HD 6700 .*ATI.*Radeon HD *67.. 3 1 1 +ATI Radeon HD 6800 .*ATI.*Radeon HD *68.. 4 1 1 +ATI Radeon HD 6900 .*ATI.*Radeon HD *69.. 5 1 1 +ATI Radeon OpenGL .*ATI.*Radeon OpenGL.* 0 0 0 +ATI Radeon 2100 .*ATI.*Radeon 21.. 0 1 1 +ATI Radeon 3000 .*ATI.*Radeon 30.. 1 1 1 +ATI Radeon 3100 .*ATI.*Radeon 31.. 0 1 1 +ATI Radeon 5xxx .*ATI.*Radeon 5... 3 1 0 +ATI Radeon 7xxx .*ATI.*Radeon 7... 0 1 1 +ATI Radeon 8xxx .*ATI.*Radeon 8... 0 1 0 +ATI Radeon 9000 .*ATI.*Radeon 90.. 0 1 1 +ATI Radeon 9100 .*ATI.*Radeon 91.. 0 1 0 +ATI Radeon 9200 .*ATI.*Radeon 92.. 0 1 1 +ATI Radeon 9500 .*ATI.*Radeon 95.. 0 1 1 +ATI Radeon 9600 .*ATI.*Radeon 96.. 0 1 1 +ATI Radeon 9700 .*ATI.*Radeon 97.. 1 1 0 +ATI Radeon 9800 .*ATI.*Radeon 98.. 1 1 1 +ATI Radeon RV250 .*ATI.*RV250.* 0 1 0 +ATI Radeon RV600 .*ATI.*RV6.* 1 1 0 +ATI Radeon RX700 .*ATI.*RX70.* 1 1 0 +ATI Radeon RX800 .*ATI.*Radeon *RX80.* 2 1 0 +ATI RS880M .*ATI.*RS880M 1 1 0 +ATI Radeon RX9550 .*ATI.*RX9550.* 1 1 0 +ATI Radeon VE .*ATI.*Radeon.*VE.* 0 0 0 +ATI Radeon X300 .*ATI.*Radeon *X3.* 1 1 1 +ATI Radeon X400 .*ATI.*Radeon ?X4.* 0 1 0 +ATI Radeon X500 .*ATI.*Radeon ?X5.* 1 1 1 +ATI Radeon X600 .*ATI.*Radeon ?X6.* 1 1 1 +ATI Radeon X700 .*ATI.*Radeon ?X7.* 2 1 1 +ATI Radeon X800 .*ATI.*Radeon ?X8.* 1 1 1 +ATI Radeon X900 .*ATI.*Radeon ?X9.* 2 1 0 +ATI Radeon Xpress .*ATI.*Radeon Xpress.* 0 1 1 +ATI Rage 128 .*ATI.*Rage 128.* 0 1 0 +ATI R300 (9700) .*R300.* 0 1 1 +ATI R350 (9800) .*R350.* 1 1 0 +ATI R580 (X1900) .*R580.* 3 1 0 +ATI RC410 (Xpress 200) .*RC410.* 0 0 0 +ATI RS48x (Xpress 200x) .*RS48.* 0 0 0 +ATI RS600 (Xpress 3200) .*RS600.* 0 0 0 +ATI RV350 (9600) .*RV350.* 0 1 0 +ATI RV370 (X300) .*RV370.* 0 1 0 +ATI RV410 (X700) .*RV410.* 1 1 0 +ATI RV515 .*RV515.* 1 1 0 +ATI RV570 (X1900 GT/PRO) .*RV570.* 3 1 0 +ATI RV380 .*RV380.* 0 1 0 +ATI RV530 .*RV530.* 1 1 0 +ATI RX480 (Xpress 200P) .*RX480.* 0 1 0 +ATI RX700 .*RX700.* 1 1 0 +AMD ANTILLES (HD 6990) .*(AMD|ATI).*Antilles.* 3 1 0 +AMD BARTS (HD 6800) .*(AMD|ATI).*Barts.* 3 1 1 +AMD CAICOS (HD 6400) .*(AMD|ATI).*Caicos.* 3 1 0 +AMD CAYMAN (HD 6900) .*(AMD|ATI).*(Cayman|CAYMAM).* 3 1 0 +AMD CEDAR (HD 5450) .*(AMD|ATI).*Cedar.* 2 1 0 +AMD CYPRESS (HD 5800) .*(AMD|ATI).*Cypress.* 3 1 0 +AMD HEMLOCK (HD 5970) .*(AMD|ATI).*Hemlock.* 3 1 0 +AMD JUNIPER (HD 5700) .*(AMD|ATI).*Juniper.* 3 1 0 +AMD PARK .*(AMD|ATI).*Park.* 3 1 0 +AMD REDWOOD (HD 5500/5600) .*(AMD|ATI).*Redwood.* 3 1 0 +AMD TURKS (HD 6500/6600) .*(AMD|ATI).*Turks.* 3 1 0 +AMD RS780 (HD 3200) .*RS780.* 0 1 1 +AMD RS880 (HD 4200) .*RS880.* 0 1 1 +AMD RV610 (HD 2400) .*RV610.* 1 1 0 +AMD RV620 (HD 3400) .*RV620.* 1 1 0 +AMD RV630 (HD 2600) .*RV630.* 2 1 0 +AMD RV635 (HD 3600) .*RV635.* 3 1 0 +AMD RV670 (HD 3800) .*RV670.* 3 1 0 +AMD R680 (HD 3870 X2) .*R680.* 3 1 0 +AMD R700 (HD 4800 X2) .*R700.* 3 1 0 +AMD RV710 (HD 4300) .*RV710.* 0 1 1 +AMD RV730 (HD 4600) .*RV730.* 3 1 0 +AMD RV740 (HD 4700) .*RV740.* 3 1 0 +AMD RV770 (HD 4800) .*RV770.* 3 1 0 +AMD RV790 (HD 4800) .*RV790.* 3 1 0 +ATI 760G/Radeon 3000 .*ATI.*AMD 760G.* 1 1 1 +ATI 780L/Radeon 3000 .*ATI.*AMD 780L.* 1 1 0 +ATI Radeon DDR .*ATI.*Radeon ?DDR.* 0 1 0 +ATI FirePro 2000 .*ATI.*FirePro 2.* 2 1 1 +ATI FirePro 3000 .*ATI.*FirePro V3.* 1 1 0 +ATI FirePro 4000 .*ATI.*FirePro V4.* 2 1 0 +ATI FirePro 5000 .*ATI.*FirePro V5.* 3 1 0 +ATI FirePro 7000 .*ATI.*FirePro V7.* 3 1 0 +ATI FirePro M .*ATI.*FirePro M.* 3 1 1 +ATI Technologies .*ATI *Technologies.* 4 1 1 +ATI R300 (9700) .*R300.* 0 1 1 +ATI Radeon .*ATI.*(Diamond|Radeon).* 0 1 0 +Intel X3100 .*Intel.*X3100.* 1 1 1 +Intel GMA 3600 .*Intel.* 3600.* 0 1 1 +Intel 830M .*Intel.*830M 0 0 0 +Intel 845G .*Intel.*845G 0 0 1 +Intel 855GM .*Intel.*855GM 0 0 1 +Intel 865G .*Intel.*865G 0 0 1 +Intel 900 .*Intel.*900.*900 0 0 0 +Intel 915GM .*Intel.*915GM 0 0 1 +Intel 915G .*Intel.*915G 0 0 1 +Intel 945GM .*Intel.*945GM.* 0 1 1 +Intel 945G .*Intel.*945G.* 0 1 1 +Intel 950 .*Intel.*950.* 0 1 1 +Intel 965 .*Intel.*965.* 0 1 1 +Intel G33 .*Intel.*G33.* 1 0 1 +Intel G41 .*Intel.*G41.* 1 1 1 +Intel G45 .*Intel.*G45.* 1 1 1 +Intel Bear Lake .*Intel.*Bear Lake.* 1 0 1 +Intel Broadwater .*Intel.*Broadwater.* 0 0 1 +Intel Brookdale .*Intel.*Brookdale.* 0 0 1 +Intel Cantiga .*Intel.*Cantiga.* 0 0 1 +Intel Eaglelake .*Intel.*Eaglelake.* 1 1 1 +Intel Graphics Media HD .*Intel.*Graphics Media.*HD.* 1 1 1 +Intel HD Graphics .*Intel.*HD Graphics.* 2 1 1 +Intel Mobile 4 Series .*Intel.*Mobile.* 4 Series.* 0 1 1 +Intel 4 Series Internal .*Intel.* 4 Series Internal.* 1 1 1 +Intel Media Graphics HD .*Intel.*Media Graphics HD.* 0 1 0 +Intel Montara .*Intel.*Montara.* 0 0 1 +Intel Pineview .*Intel.*Pineview.* 0 1 1 +Intel Springdale .*Intel.*Springdale.* 0 0 1 +Intel Grantsdale .*Intel.*Grantsdale.* 1 1 0 +Intel HD Graphics 2000 .*Intel.*HD2000.* 1 1 0 +Intel HD Graphics 3000 .*Intel.*HD3000.* 2 1 0 +Intel Q45/Q43 .*Intel.*Q4.* 1 1 1 +Intel B45/B43 .*Intel.*B4.* 1 1 1 +Matrox .*Matrox.* 0 0 0 +Mesa .*Mesa.* 1 0 1 +Gallium .*Gallium.* 1 1 1 +NVIDIA 205 .*NVIDIA .*GeForce 205.* 2 1 1 +NVIDIA 210 .*NVIDIA .*GeForce 210.* 3 1 1 +NVIDIA 310M .*NVIDIA .*GeForce 310M.* 3 1 1 +NVIDIA 310 .*NVIDIA .*GeForce 310.* 3 1 1 +NVIDIA 315M .*NVIDIA .*GeForce 315M.* 3 1 1 +NVIDIA 315 .*NVIDIA .*GeForce 315.* 3 1 1 +NVIDIA 320M .*NVIDIA .*GeForce 320M.* 3 1 1 +NVIDIA G100M .*NVIDIA .*100M.* 4 1 1 +NVIDIA G100 .*NVIDIA .*100.* 3 1 1 +NVIDIA G102M .*NVIDIA .*102M.* 1 1 1 +NVIDIA G103M .*NVIDIA .*103M.* 2 1 1 +NVIDIA G105M .*NVIDIA .*105M.* 2 1 1 +NVIDIA G 110M .*NVIDIA .*110M.* 1 1 1 +NVIDIA G 120M .*NVIDIA .*120M.* 1 1 1 +NVIDIA G 200 .*NVIDIA .*200(M)?.* 1 1 1 +NVIDIA G 205M .*NVIDIA .*205(M)?.* 0 1 0 +NVIDIA G 210 .*NVIDIA .*210(M)?.* 2 1 1 +NVIDIA 305M .*NVIDIA .*305(M)?.* 1 1 0 +NVIDIA G 310M .*NVIDIA .*310(M)?.* 2 1 0 +NVIDIA G 315 .*NVIDIA .*315(M)?.* 2 1 0 +NVIDIA G 320M .*NVIDIA .*320(M)?.* 3 1 1 +NVIDIA G 405 .*NVIDIA .*405(M)?.* 3 1 1 +NVIDIA G 410M .*NVIDIA .*410(M)?.* 3 1 1 +NVIDIA GT 120M .*NVIDIA .*GT *120(M)?.* 3 1 1 +NVIDIA GT 120 .*NVIDIA .*GT.*120 2 1 0 +NVIDIA GT 130M .*NVIDIA .*GT *130(M)?.* 3 1 1 +NVIDIA GT 140M .*NVIDIA .*GT *140(M)?.* 3 1 1 +NVIDIA GT 150M .*NVIDIA .*GT(S)? *150(M)?.* 2 1 0 +NVIDIA GT 160M .*NVIDIA .*GT *160(M)?.* 2 1 0 +NVIDIA GT 220M .*NVIDIA .*GT *220(M)?.* 3 1 1 +NVIDIA GT 230M .*NVIDIA .*GT *230(M)?.* 3 1 1 +NVIDIA GT 240M .*NVIDIA .*GT *240(M)?.* 3 1 1 +NVIDIA GT 250M .*NVIDIA .*GT *250(M)?.* 2 1 0 +NVIDIA GT 260M .*NVIDIA .*GT *260(M)?.* 2 1 0 +NVIDIA GT 320M .*NVIDIA .*GT *320(M)?.* 2 1 0 +NVIDIA GT 325M .*NVIDIA .*GT *325(M)?.* 3 1 1 +NVIDIA GT 330M .*NVIDIA .*GT *330(M)?.* 3 1 1 +NVIDIA GT 335M .*NVIDIA .*GT *335(M)?.* 3 1 1 +NVIDIA GT 340M .*NVIDIA .*GT *340(M)?.* 4 1 1 +NVIDIA GT 415M .*NVIDIA .*GT *415(M)?.* 3 1 1 +NVIDIA GT 420M .*NVIDIA .*GT *420(M)?.* 3 1 1 +NVIDIA GT 425M .*NVIDIA .*GT *425(M)?.* 4 1 1 +NVIDIA GT 430M .*NVIDIA .*GT *430(M)?.* 3 1 1 +NVIDIA GT 435M .*NVIDIA .*GT *435(M)?.* 4 1 1 +NVIDIA GT 440M .*NVIDIA .*GT *440(M)?.* 3 1 1 +NVIDIA GT 445M .*NVIDIA .*GT *445(M)?.* 3 1 1 +NVIDIA GT 450M .*NVIDIA .*GT *450(M)?.* 3 1 0 +NVIDIA GT 520M .*NVIDIA .*GT *52.(M)?.* 3 1 1 +NVIDIA GT 530M .*NVIDIA .*GT *530(M)?.* 3 1 1 +NVIDIA GT 540M .*NVIDIA .*GT *54.(M)?.* 3 1 1 +NVIDIA GT 550M .*NVIDIA .*GT *550(M)?.* 3 1 1 +NVIDIA GT 555M .*NVIDIA .*GT *555(M)?.* 3 1 1 +NVIDIA GT 610 .*NVIDIA .*GT *61.* 3 1 1 +NVIDIA GT 620 .*NVIDIA .*GT *62.* 3 1 0 +NVIDIA GT 630 .*NVIDIA .*GT *63.* 3 1 1 +NVIDIA GT 640 .*NVIDIA .*GT *64.* 3 1 0 +NVIDIA GT 650 .*NVIDIA .*GT *65.* 3 1 1 +NVIDIA GTX 660 .*NVIDIA .*GTX *66.* 5 1 0 +NVIDIA GTX 670 .*NVIDIA .*GTX *67.* 5 1 1 +NVIDIA GTX 680 .*NVIDIA .*GTX *68.* 5 1 1 +NVIDIA GTX 690 .*NVIDIA .*GTX *69.* 5 1 1 +NVIDIA GTS 160M .*NVIDIA .*GT(S)? *160(M)?.* 2 1 0 +NVIDIA GTS 240 .*NVIDIA .*GTS *24.* 3 1 1 +NVIDIA GTS 250 .*NVIDIA .*GTS *25.* 4 1 1 +NVIDIA GTS 350M .*NVIDIA .*GTS *350M.* 4 1 1 +NVIDIA GTS 360M .*NVIDIA .*GTS *360M.* 5 1 1 +NVIDIA GTS 360 .*NVIDIA .*GTS *360.* 3 1 0 +NVIDIA GTS 450 .*NVIDIA .*GTS *45.* 4 1 1 +NVIDIA GTX 260 .*NVIDIA .*GTX *26.* 4 1 1 +NVIDIA GTX 275 .*NVIDIA .*GTX *275.* 4 1 1 +NVIDIA GTX 270 .*NVIDIA .*GTX *27.* 3 1 0 +NVIDIA GTX 285 .*NVIDIA .*GTX *285.* 5 1 1 +NVIDIA GTX 280 .*NVIDIA .*GTX *280.* 4 1 1 +NVIDIA GTX 290 .*NVIDIA .*GTX *290.* 3 1 0 +NVIDIA GTX 295 .*NVIDIA .*GTX *295.* 5 1 1 +NVIDIA GTX 460M .*NVIDIA .*GTX *460M.* 4 1 1 +NVIDIA GTX 465 .*NVIDIA .*GTX *465.* 5 1 1 +NVIDIA GTX 460 .*NVIDIA .*GTX *46.* 5 1 1 +NVIDIA GTX 470M .*NVIDIA .*GTX *470M.* 3 1 0 +NVIDIA GTX 470 .*NVIDIA .*GTX *47.* 5 1 1 +NVIDIA GTX 480M .*NVIDIA .*GTX *480M.* 3 1 1 +NVIDIA GTX 485M .*NVIDIA .*GTX *485M.* 3 1 0 +NVIDIA GTX 480 .*NVIDIA .*GTX *48.* 5 1 1 +NVIDIA GTX 530 .*NVIDIA .*GTX *53.* 3 1 0 +NVIDIA GTX 550 .*NVIDIA .*GTX *55.* 5 1 1 +NVIDIA GTX 560 .*NVIDIA .*GTX *56.* 5 1 1 +NVIDIA GTX 570 .*NVIDIA .*GTX *57.* 5 1 1 +NVIDIA GTX 580M .*NVIDIA .*GTX *580M.* 5 1 1 +NVIDIA GTX 580 .*NVIDIA .*GTX *58.* 5 1 1 +NVIDIA GTX 590 .*NVIDIA .*GTX *59.* 5 1 1 +NVIDIA C51 .*NVIDIA .*C51.* 0 1 1 +NVIDIA G72 .*NVIDIA .*G72.* 1 1 0 +NVIDIA G73 .*NVIDIA .*G73.* 1 1 0 +NVIDIA G84 .*NVIDIA .*G84.* 2 1 0 +NVIDIA G86 .*NVIDIA .*G86.* 3 1 0 +NVIDIA G92 .*NVIDIA .*G92.* 3 1 0 +NVIDIA GeForce .*GeForce 256.* 0 0 0 +NVIDIA GeForce 2 .*GeForce ?2 ?.* 0 1 1 +NVIDIA GeForce 3 .*GeForce ?3 ?.* 0 1 1 +NVIDIA GeForce 3 Ti .*GeForce ?3 Ti.* 0 1 0 +NVIDIA GeForce 4 .*NVIDIA .*GeForce ?4.* 0 1 1 +NVIDIA GeForce 4 Go .*NVIDIA .*GeForce ?4.*Go.* 0 1 0 +NVIDIA GeForce 4 MX .*NVIDIA .*GeForce ?4 MX.* 0 1 0 +NVIDIA GeForce 4 PCX .*NVIDIA .*GeForce ?4 PCX.* 0 1 0 +NVIDIA GeForce 4 Ti .*NVIDIA .*GeForce ?4 Ti.* 0 1 0 +NVIDIA GeForce 6100 .*NVIDIA .*GeForce 61.* 3 1 1 +NVIDIA GeForce 6200 .*NVIDIA .*GeForce 62.* 0 1 0 +NVIDIA GeForce 6500 .*NVIDIA .*GeForce 65.* 1 1 1 +NVIDIA GeForce 6600 .*NVIDIA .*GeForce 66.* 2 1 1 +NVIDIA GeForce 6700 .*NVIDIA .*GeForce 67.* 2 1 1 +NVIDIA GeForce 6800 .*NVIDIA .*GeForce 68.* 1 1 1 +NVIDIA GeForce 7000 .*NVIDIA .*GeForce 70.* 1 1 1 +NVIDIA GeForce 7100 .*NVIDIA .*GeForce 71.* 1 1 1 +NVIDIA GeForce 7200 .*NVIDIA .*GeForce 72.* 1 1 0 +NVIDIA GeForce 7300 .*NVIDIA .*GeForce 73.* 1 1 1 +NVIDIA GeForce 7500 .*NVIDIA .*GeForce 75.* 2 1 1 +NVIDIA GeForce 7600 .*NVIDIA .*GeForce 76.* 2 1 1 +NVIDIA GeForce 7800 .*NVIDIA .*GeForce 78.* 2 1 1 +NVIDIA GeForce 7900 .*NVIDIA .*GeForce 79.* 3 1 1 +NVIDIA GeForce 8100 .*NVIDIA .*GeForce 81.* 1 1 0 +NVIDIA GeForce 8200M .*NVIDIA .*GeForce 8200M.* 1 1 0 +NVIDIA GeForce 8200 .*NVIDIA .*GeForce 82.* 1 1 0 +NVIDIA GeForce 8300 .*NVIDIA .*GeForce 83.* 3 1 1 +NVIDIA GeForce 8400M .*NVIDIA .*GeForce 8400M.* 1 1 1 +NVIDIA GeForce 8400 .*NVIDIA .*GeForce 84.* 2 1 1 +NVIDIA GeForce 8500 .*NVIDIA .*GeForce 85.* 2 1 1 +NVIDIA GeForce 8600M .*NVIDIA .*GeForce 8600M.* 2 1 1 +NVIDIA GeForce 8600 .*NVIDIA .*GeForce 86.* 3 1 1 +NVIDIA GeForce 8700M .*NVIDIA .*GeForce 8700M.* 2 1 1 +NVIDIA GeForce 8700 .*NVIDIA .*GeForce 87.* 3 1 0 +NVIDIA GeForce 8800M .*NVIDIA .*GeForce 8800M.* 2 1 1 +NVIDIA GeForce 8800 .*NVIDIA .*GeForce 88.* 3 1 1 +NVIDIA GeForce 9100M .*NVIDIA .*GeForce 9100M.* 0 1 0 +NVIDIA GeForce 9100 .*NVIDIA .*GeForce 91.* 0 1 0 +NVIDIA GeForce 9200M .*NVIDIA .*GeForce 9200M.* 1 1 0 +NVIDIA GeForce 9200 .*NVIDIA .*GeForce 92.* 1 1 0 +NVIDIA GeForce 9300M .*NVIDIA .*GeForce 9300M.* 1 1 1 +NVIDIA GeForce 9300 .*NVIDIA .*GeForce 93.* 1 1 1 +NVIDIA GeForce 9400M .*NVIDIA .*GeForce 9400M.* 2 1 1 +NVIDIA GeForce 9400 .*NVIDIA .*GeForce 94.* 3 1 1 +NVIDIA GeForce 9500M .*NVIDIA .*GeForce 9500M.* 1 1 1 +NVIDIA GeForce 9500 .*NVIDIA .*GeForce 95.* 3 1 1 +NVIDIA GeForce 9600M .*NVIDIA .*GeForce 9600M.* 2 1 1 +NVIDIA GeForce 9600 .*NVIDIA .*GeForce 96.* 3 1 1 +NVIDIA GeForce 9700M .*NVIDIA .*GeForce 9700M.* 0 1 1 +NVIDIA GeForce 9800M .*NVIDIA .*GeForce 9800M.* 2 1 1 +NVIDIA GeForce 9800 .*NVIDIA .*GeForce 98.* 3 1 1 +NVIDIA GeForce FX 5100 .*NVIDIA .*GeForce FX 51.* 0 1 0 +NVIDIA GeForce FX 5200 .*NVIDIA .*GeForce FX 52.* 0 1 0 +NVIDIA GeForce FX 5300 .*NVIDIA .*GeForce FX 53.* 0 1 0 +NVIDIA GeForce FX 5500 .*NVIDIA .*GeForce FX 55.* 0 1 1 +NVIDIA GeForce FX 5600 .*NVIDIA .*GeForce FX 56.* 1 1 1 +NVIDIA GeForce FX 5700 .*NVIDIA .*GeForce FX 57.* 0 1 1 +NVIDIA GeForce FX 5800 .*NVIDIA .*GeForce FX 58.* 1 1 0 +NVIDIA GeForce FX 5900 .*NVIDIA .*GeForce FX 59.* 1 1 1 +NVIDIA GeForce FX Go5100 .*NVIDIA .*GeForce FX Go51.* 0 1 0 +NVIDIA GeForce FX Go5200 .*NVIDIA .*GeForce FX Go52.* 0 1 0 +NVIDIA GeForce FX Go5300 .*NVIDIA .*GeForce FX Go53.* 0 1 0 +NVIDIA GeForce FX Go5500 .*NVIDIA .*GeForce FX Go55.* 0 1 0 +NVIDIA GeForce FX Go5600 .*NVIDIA .*GeForce FX Go56.* 0 1 1 +NVIDIA GeForce FX Go5700 .*NVIDIA .*GeForce FX Go57.* 1 1 1 +NVIDIA GeForce FX Go5800 .*NVIDIA .*GeForce FX Go58.* 1 1 0 +NVIDIA GeForce FX Go5900 .*NVIDIA .*GeForce FX Go59.* 1 1 0 +NVIDIA GeForce FX Go5xxx .*NVIDIA .*GeForce FX Go.* 0 1 0 +NVIDIA GeForce Go 6100 .*NVIDIA .*GeForce Go 61.* 0 1 1 +NVIDIA GeForce Go 6200 .*NVIDIA .*GeForce Go 62.* 0 1 0 +NVIDIA GeForce Go 6400 .*NVIDIA .*GeForce Go 64.* 1 1 1 +NVIDIA GeForce Go 6500 .*NVIDIA .*GeForce Go 65.* 1 1 0 +NVIDIA GeForce Go 6600 .*NVIDIA .*GeForce Go 66.* 1 1 1 +NVIDIA GeForce Go 6700 .*NVIDIA .*GeForce Go 67.* 1 1 0 +NVIDIA GeForce Go 6800 .*NVIDIA .*GeForce Go 68.* 0 1 1 +NVIDIA GeForce Go 7200 .*NVIDIA .*GeForce Go 72.* 1 1 0 +NVIDIA GeForce Go 7300 LE .*NVIDIA .*GeForce Go 73.*LE.* 0 1 0 +NVIDIA GeForce Go 7300 .*NVIDIA .*GeForce Go 73.* 1 1 1 +NVIDIA GeForce Go 7400 .*NVIDIA .*GeForce Go 74.* 1 1 1 +NVIDIA GeForce Go 7600 .*NVIDIA .*GeForce Go 76.* 1 1 1 +NVIDIA GeForce Go 7700 .*NVIDIA .*GeForce Go 77.* 0 1 1 +NVIDIA GeForce Go 7800 .*NVIDIA .*GeForce Go 78.* 2 1 0 +NVIDIA GeForce Go 7900 .*NVIDIA .*GeForce Go 79.* 1 1 1 +NVIDIA D9M .*NVIDIA .*D9M.* 1 1 0 +NVIDIA G94 .*NVIDIA .*G94.* 3 1 0 +NVIDIA GeForce Go 6 .*GeForce Go 6.* 1 1 0 +NVIDIA ION 2 .*NVIDIA .*ION 2.* 2 1 0 +NVIDIA ION .*NVIDIA Corporation.*ION.* 2 1 1 +NVIDIA NB8M .*NVIDIA .*NB8M.* 1 1 0 +NVIDIA NB8P .*NVIDIA .*NB8P.* 2 1 0 +NVIDIA NB9E .*NVIDIA .*NB9E.* 3 1 0 +NVIDIA NB9M .*NVIDIA .*NB9M.* 1 1 0 +NVIDIA NB9P .*NVIDIA .*NB9P.* 2 1 0 +NVIDIA N10 .*NVIDIA .*N10.* 1 1 0 +NVIDIA GeForce PCX .*GeForce PCX.* 0 1 0 +NVIDIA Generic .*NVIDIA .*Unknown.* 0 0 0 +NVIDIA NV17 .*NVIDIA .*NV17.* 0 1 0 +NVIDIA NV34 .*NVIDIA .*NV34.* 0 1 0 +NVIDIA NV35 .*NVIDIA .*NV35.* 0 1 0 +NVIDIA NV36 .*NVIDIA .*NV36.* 1 1 0 +NVIDIA NV41 .*NVIDIA .*NV41.* 1 1 0 +NVIDIA NV43 .*NVIDIA .*NV43.* 1 1 0 +NVIDIA NV44 .*NVIDIA .*NV44.* 1 1 0 +NVIDIA nForce .*NVIDIA .*nForce.* 0 0 0 +NVIDIA MCP51 .*NVIDIA .*MCP51.* 1 1 0 +NVIDIA MCP61 .*NVIDIA .*MCP61.* 1 1 0 +NVIDIA MCP67 .*NVIDIA .*MCP67.* 1 1 0 +NVIDIA MCP68 .*NVIDIA .*MCP68.* 1 1 0 +NVIDIA MCP73 .*NVIDIA .*MCP73.* 1 1 0 +NVIDIA MCP77 .*NVIDIA .*MCP77.* 1 1 0 +NVIDIA MCP78 .*NVIDIA .*MCP78.* 1 1 0 +NVIDIA MCP79 .*NVIDIA .*MCP79.* 1 1 0 +NVIDIA MCP7A .*NVIDIA .*MCP7A.* 1 1 0 +NVIDIA Quadro2 .*Quadro2.* 0 1 0 +NVIDIA Quadro 1000M .*Quadro.*1000M.* 2 1 0 +NVIDIA Quadro 2000 M/D .*Quadro.*2000.* 3 1 0 +NVIDIA Quadro 3000M .*Quadro.*3000M.* 3 1 0 +NVIDIA Quadro 4000M .*Quadro.*4000M.* 3 1 0 +NVIDIA Quadro 4000 .*Quadro *4000.* 3 1 0 +NVIDIA Quadro 50x0 M .*Quadro.*50.0.* 3 1 0 +NVIDIA Quadro 6000 .*Quadro.*6000.* 3 1 0 +NVIDIA Quadro 400 .*Quadro.*400.* 2 1 0 +NVIDIA Quadro 600 .*Quadro.*600.* 2 1 0 +NVIDIA Quadro4 .*Quadro4.* 0 1 0 +NVIDIA Quadro DCC .*Quadro DCC.* 0 1 0 +NVIDIA Quadro CX .*Quadro.*CX.* 3 1 0 +NVIDIA Quadro FX 770M .*Quadro.*FX *770M.* 2 1 0 +NVIDIA Quadro FX 1500M .*Quadro.*FX *1500M.* 1 1 0 +NVIDIA Quadro FX 1600M .*Quadro.*FX *1600M.* 2 1 0 +NVIDIA Quadro FX 2500M .*Quadro.*FX *2500M.* 2 1 0 +NVIDIA Quadro FX 2700M .*Quadro.*FX *2700M.* 3 1 0 +NVIDIA Quadro FX 2800M .*Quadro.*FX *2800M.* 3 1 0 +NVIDIA Quadro FX 3500 .*Quadro.*FX *3500.* 2 1 0 +NVIDIA Quadro FX 3600 .*Quadro.*FX *3600.* 3 1 0 +NVIDIA Quadro FX 3700 .*Quadro.*FX *3700.* 3 1 0 +NVIDIA Quadro FX 3800 .*Quadro.*FX *3800.* 3 1 0 +NVIDIA Quadro FX 4500 .*Quadro.*FX *45.* 3 1 0 +NVIDIA Quadro FX 880M .*Quadro.*FX *880M.* 3 1 0 +NVIDIA Quadro FX 4800 .*NVIDIA .*Quadro *FX *4800.* 3 1 0 +NVIDIA Quadro FX .*Quadro FX.* 1 1 0 +NVIDIA Quadro NVS 1xxM .*Quadro NVS *1.[05]M.* 2 1 1 +NVIDIA Quadro NVS 300M .*NVIDIA .*NVS *300M.* 2 1 0 +NVIDIA Quadro NVS 320M .*NVIDIA .*NVS *320M.* 2 1 0 +NVIDIA Quadro NVS 2100M .*NVIDIA .*NVS *2100M.* 2 1 0 +NVIDIA Quadro NVS 3100M .*NVIDIA .*NVS *3100M.* 2 1 0 +NVIDIA Quadro NVS 4200M .*NVIDIA .*NVS *4200M.* 2 1 0 +NVIDIA Quadro NVS 5100M .*NVIDIA .*NVS *5100M.* 2 1 0 +NVIDIA Quadro NVS .*NVIDIA .*NVS 0 1 0 +NVIDIA Corporation N12P .*NVIDIA .*N12P.* 1 1 1 +NVIDIA Corporation N11M .*NVIDIA .*N11M.* 2 1 0 +NVIDIA RIVA TNT .*RIVA TNT.* 0 0 0 +S3 .*S3 Graphics.* 0 0 1 +SiS SiS.* 0 0 1 +Trident Trident.* 0 0 0 +Tungsten Graphics Tungsten.* 0 0 0 +XGI XGI.* 0 0 0 +VIA VIA.* 0 0 0 +Apple Generic Apple.*Generic.* 0 0 0 +Apple Software Renderer Apple.*Software Renderer.* 0 0 0 +Humper Humper.* 0 1 1 +PowerVR SGX545 .*PowerVR SGX.* 1 1 1 -- cgit v1.2.3 From 1779db5fa09ff4680b04f33cc460a8d63c8d15c2 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Wed, 29 Aug 2012 14:07:29 -0500 Subject: MAINT-1497 Remove "ATI Geforce" lines (also add "expected OpenGL version" field) --- indra/newview/gpu_table.txt | 1041 +++++++++++++++++++++---------------------- 1 file changed, 519 insertions(+), 522 deletions(-) diff --git a/indra/newview/gpu_table.txt b/indra/newview/gpu_table.txt index 4081e8848a..74501c9165 100644 --- a/indra/newview/gpu_table.txt +++ b/indra/newview/gpu_table.txt @@ -17,7 +17,7 @@ // // Format: // Fields are separated by one or more tab (not space) characters -// +// // // Class Numbers: // 0 - Defaults to low graphics settings. No shaders on by default @@ -32,525 +32,522 @@ // 1 - We claim to support this card. // -3Dfx .*3Dfx.* 0 0 0 -3Dlabs .*3Dlabs.* 0 0 0 -ATI 3D-Analyze .*ATI.*3D-Analyze.* 0 0 0 -ATI All-in-Wonder 7500 .*ATI.*All-in-Wonder 75.* 0 1 0 -ATI All-in-Wonder 8500 .*ATI.*All-in-Wonder 85.* 0 1 0 -ATI All-in-Wonder 9200 .*ATI.*All-in-Wonder 92.* 0 1 0 -ATI All-in-Wonder 9xxx .*ATI.*All-in-Wonder 9.* 1 1 0 -ATI All-in-Wonder HD .*ATI.*All-in-Wonder HD.* 1 1 1 -ATI All-in-Wonder X600 .*ATI.*All-in-Wonder X6.* 1 1 0 -ATI All-in-Wonder X800 .*ATI.*All-in-Wonder X8.* 1 1 1 -ATI All-in-Wonder X1800 .*ATI.*All-in-Wonder X18.* 3 1 0 -ATI All-in-Wonder X1900 .*ATI.*All-in-Wonder X19.* 3 1 0 -ATI All-in-Wonder PCI-E .*ATI.*All-in-Wonder.*PCI-E.* 1 1 0 -ATI All-in-Wonder Radeon .*ATI.*All-in-Wonder Radeon.* 0 1 0 -ATI ASUS ARES .*ATI.*ASUS.*ARES.* 3 1 0 -ATI ASUS A9xxx .*ATI.*ASUS.*A9.* 1 1 0 -ATI ASUS AH24xx .*ATI.*ASUS.*AH24.* 1 1 1 -ATI ASUS AH26xx .*ATI.*ASUS.*AH26.* 1 1 1 -ATI ASUS AH34xx .*ATI.*ASUS.*AH34.* 1 1 1 -ATI ASUS AH36xx .*ATI.*ASUS.*AH36.* 1 1 1 -ATI ASUS AH46xx .*ATI.*ASUS.*AH46.* 2 1 1 -ATI ASUS AX3xx .*ATI.*ASUS.*AX3.* 1 1 0 -ATI ASUS AX5xx .*ATI.*ASUS.*AX5.* 1 1 0 -ATI ASUS AX8xx .*ATI.*ASUS.*AX8.* 2 1 0 -ATI ASUS EAH24xx .*ATI.*ASUS.*EAH24.* 2 1 0 -ATI ASUS EAH26xx .*ATI.*ASUS.*EAH26.* 3 1 0 -ATI ASUS EAH29xx .*ATI.*ASUS.*EAH29.* 3 1 0 -ATI ASUS EAH34xx .*ATI.*ASUS.*EAH34.* 1 1 0 -ATI ASUS EAH36xx .*ATI.*ASUS.*EAH36.* 3 1 0 -ATI ASUS EAH38xx .*ATI.*ASUS.*EAH38.* 2 1 1 -ATI ASUS EAH43xx .*ATI.*ASUS.*EAH43.* 2 1 1 -ATI ASUS EAH45xx .*ATI.*ASUS.*EAH45.* 1 1 0 -ATI ASUS EAH48xx .*ATI.*ASUS.*EAH48.* 3 1 1 -ATI ASUS EAH57xx .*ATI.*ASUS.*EAH57.* 3 1 1 -ATI ASUS EAH58xx .*ATI.*ASUS.*EAH58.* 5 1 1 -ATI ASUS EAH6xxx .*ATI.*ASUS.*EAH6.* 3 1 1 -ATI ASUS Radeon X1xxx .*ATI.*ASUS.*X1.* 2 1 1 -ATI Radeon X7xx .*ATI.*ASUS.*X7.* 1 1 0 -ATI Radeon X19xx .*ATI.*(Radeon|Diamond) X19.* ?.* 2 1 1 -ATI Radeon X18xx .*ATI.*(Radeon|Diamond) X18.* ?.* 3 1 1 -ATI Radeon X17xx .*ATI.*(Radeon|Diamond) X17.* ?.* 1 1 1 -ATI Radeon X16xx .*ATI.*(Radeon|Diamond) X16.* ?.* 1 1 1 -ATI Radeon X15xx .*ATI.*(Radeon|Diamond) X15.* ?.* 1 1 1 -ATI Radeon X13xx .*ATI.*(Radeon|Diamond) X13.* ?.* 1 1 1 -ATI Radeon X1xxx .*ATI.*(Radeon|Diamond) X1.. ?.* 0 1 1 -ATI Radeon X2xxx .*ATI.*(Radeon|Diamond) X2.. ?.* 1 1 1 -ATI Display Adapter .*ATI.*display adapter.* 1 1 1 -ATI FireGL 5200 .*ATI.*FireGL V52.* 1 1 1 -ATI FireGL 5xxx .*ATI.*FireGL V5.* 3 1 1 -ATI FireGL .*ATI.*Fire.*GL.* 4 1 1 -ATI FirePro M3900 .*ATI.*FirePro.*M39.* 2 1 0 -ATI FirePro M5800 .*ATI.*FirePro.*M58.* 3 1 0 -ATI FirePro M7740 .*ATI.*FirePro.*M77.* 3 1 0 -ATI FirePro M7820 .*ATI.*FirePro.*M78.* 5 1 1 -ATI FireMV .*ATI.*FireMV.* 0 1 1 -ATI Geforce 9500 GT .*ATI.*Geforce 9500 *GT.* 3 1 1 -ATI Geforce 9600 GT .*ATI.*Geforce 9600 *GT.* 3 1 1 -ATI Geforce 9800 GT .*ATI.*Geforce 9800 *GT.* 3 1 1 -ATI Generic .*ATI.*Generic.* 0 0 0 -ATI Hercules 9800 .*ATI.*Hercules.*9800.* 1 1 0 -ATI IGP 340M .*ATI.*IGP.*340M.* 0 0 0 -ATI M52 .*ATI.*M52.* 1 1 0 -ATI M54 .*ATI.*M54.* 1 1 0 -ATI M56 .*ATI.*M56.* 1 1 0 -ATI M71 .*ATI.*M71.* 1 1 0 -ATI M72 .*ATI.*M72.* 1 1 0 -ATI M76 .*ATI.*M76.* 3 1 0 -ATI Radeon HD 64xx .*ATI.*AMD Radeon.* HD [67]4..[MG] 2 1 1 -ATI Radeon HD 65xx .*ATI.*AMD Radeon.* HD [67]5..[MG] 2 1 1 -ATI Radeon HD 66xx .*ATI.*AMD Radeon.* HD [67]6..[MG] 3 1 1 -ATI Mobility Radeon 4100 .*ATI.*Mobility.*41.. 1 1 1 -ATI Mobility Radeon 7xxx .*ATI.*Mobility.*Radeon 7.* 0 1 1 -ATI Mobility Radeon 8xxx .*ATI.*Mobility.*Radeon 8.* 0 1 0 -ATI Mobility Radeon 9800 .*ATI.*Mobility.*98.* 1 1 0 -ATI Mobility Radeon 9700 .*ATI.*Mobility.*97.* 0 1 1 -ATI Mobility Radeon 9600 .*ATI.*Mobility.*96.* 1 1 1 -ATI Mobility Radeon HD 530v .*ATI.*Mobility.*HD *530v.* 1 1 1 -ATI Mobility Radeon HD 540v .*ATI.*Mobility.*HD *540v.* 1 1 1 -ATI Mobility Radeon HD 545v .*ATI.*Mobility.*HD *545v.* 2 1 1 -ATI Mobility Radeon HD 550v .*ATI.*Mobility.*HD *550v.* 3 1 1 -ATI Mobility Radeon HD 560v .*ATI.*Mobility.*HD *560v.* 3 1 1 -ATI Mobility Radeon HD 565v .*ATI.*Mobility.*HD *565v.* 3 1 1 -ATI Mobility Radeon HD 2300 .*ATI.*Mobility.*HD *23.* 0 1 1 -ATI Mobility Radeon HD 2400 .*ATI.*Mobility.*HD *24.* 1 1 1 -ATI Mobility Radeon HD 2600 .*ATI.*Mobility.*HD *26.* 0 1 1 -ATI Mobility Radeon HD 2700 .*ATI.*Mobility.*HD *27.* 3 1 0 -ATI Mobility Radeon HD 3100 .*ATI.*Mobility.*HD *31.* 0 1 0 -ATI Mobility Radeon HD 3200 .*ATI.*Mobility.*HD *32.* 0 1 0 -ATI Mobility Radeon HD 3400 .*ATI.*Mobility.*HD *34.* 1 1 1 -ATI Mobility Radeon HD 3600 .*ATI.*Mobility.*HD *36.* 1 1 1 -ATI Mobility Radeon HD 3800 .*ATI.*Mobility.*HD *38.* 3 1 1 -ATI Mobility Radeon HD 4200 .*ATI.*Mobility.*HD *42.* 1 1 1 -ATI Mobility Radeon HD 4300 .*ATI.*Mobility.*HD *43.* 1 1 1 -ATI Mobility Radeon HD 4500 .*ATI.*Mobility.*HD *45.* 1 1 1 -ATI Mobility Radeon HD 4600 .*ATI.*Mobility.*HD *46.* 2 1 1 -ATI Mobility Radeon HD 4800 .*ATI.*Mobility.*HD *48.* 3 1 1 -ATI Mobility Radeon HD 5100 .*ATI.*Mobility.*HD *51.* 3 1 1 -ATI Mobility Radeon HD 5300 .*ATI.*Mobility.*HD *53.* 3 1 0 -ATI Mobility Radeon HD 5400 .*ATI.*Mobility.*HD *54.* 2 1 1 -ATI Mobility Radeon HD 5500 .*ATI.*Mobility.*HD *55.* 3 1 0 -ATI Mobility Radeon HD 5600 .*ATI.*Mobility.*HD *56.* 3 1 1 -ATI Mobility Radeon HD 5700 .*ATI.*Mobility.*HD *57.* 1 1 1 -ATI Mobility Radeon HD 6200 .*ATI.*Mobility.*HD *62.* 3 1 0 -ATI Mobility Radeon HD 6300 .*ATI.*Mobility.*HD *63.* 3 1 1 -ATI Mobility Radeon HD 6400M .*ATI.*Mobility.*HD *64.* 3 1 0 -ATI Mobility Radeon HD 6500M .*ATI.*Mobility.*HD *65.* 5 1 1 -ATI Mobility Radeon HD 6600M .*ATI.*Mobility.*HD *66.* 3 1 0 -ATI Mobility Radeon HD 6700M .*ATI.*Mobility.*HD *67.* 3 1 0 -ATI Mobility Radeon HD 6800M .*ATI.*Mobility.*HD *68.* 3 1 0 -ATI Mobility Radeon HD 6900M .*ATI.*Mobility.*HD *69.* 3 1 0 -ATI Radeon HD 2300 .*ATI.*Radeon HD *23.. 0 1 1 -ATI Radeon HD 2400 .*ATI.*Radeon HD *24.. 1 1 1 -ATI Radeon HD 2600 .*ATI.*Radeon HD *26.. 2 1 1 -ATI Radeon HD 2900 .*ATI.*Radeon HD *29.. 3 1 1 -ATI Radeon HD 3000 .*ATI.*Radeon HD *30.. 0 1 0 -ATI Radeon HD 3100 .*ATI.*Radeon HD *31.. 1 1 0 -ATI Radeon HD 3200 .*ATI.*Radeon HD *32.. 1 1 1 -ATI Radeon HD 3300 .*ATI.*Radeon HD *33.. 1 1 1 -ATI Radeon HD 3400 .*ATI.*Radeon HD *34.. 1 1 1 -ATI Radeon HD 3500 .*ATI.*Radeon HD *35.. 2 1 0 -ATI Radeon HD 3600 .*ATI.*Radeon HD *36.. 3 1 1 -ATI Radeon HD 3700 .*ATI.*Radeon HD *37.. 3 1 0 -ATI Radeon HD 3800 .*ATI.*Radeon HD *38.. 3 1 1 -ATI Radeon HD 4100 .*ATI.*Radeon HD *41.. 1 1 0 -ATI Radeon HD 4200 .*ATI.*Radeon HD *42.. 1 1 1 -ATI Radeon HD 4300 .*ATI.*Radeon HD *43.. 2 1 1 -ATI Radeon HD 4400 .*ATI.*Radeon HD *44.. 2 1 0 -ATI Radeon HD 4500 .*ATI.*Radeon HD *45.. 2 1 1 -ATI Radeon HD 4600 .*ATI.*Radeon HD *46.. 3 1 1 -ATI Radeon HD 4700 .*ATI.*Radeon HD *47.. 3 1 1 -ATI Radeon HD 4800 .*ATI.*Radeon HD *48.. 3 1 1 -ATI Radeon HD 5400 .*ATI.*Radeon HD *54.. 3 1 1 -ATI Radeon HD 5500 .*ATI.*Radeon HD *55.. 3 1 1 -ATI Radeon HD 5600 .*ATI.*Radeon HD *56.. 3 1 1 -ATI Radeon HD 5700 .*ATI.*Radeon HD *57.. 3 1 1 -ATI Radeon HD 5800 .*ATI.*Radeon HD *58.. 4 1 1 -ATI Radeon HD 5900 .*ATI.*Radeon HD *59.. 4 1 1 -ATI Radeon HD 6200 .*ATI.*Radeon HD *62.. 0 1 1 -ATI Radeon HD 6300 .*ATI.*Radeon HD *63.. 1 1 1 -ATI Radeon HD 6400 .*ATI.*Radeon HD *64.. 3 1 1 -ATI Radeon HD 6500 .*ATI.*Radeon HD *65.. 3 1 1 -ATI Radeon HD 6600 .*ATI.*Radeon HD *66.. 3 1 1 -ATI Radeon HD 6700 .*ATI.*Radeon HD *67.. 3 1 1 -ATI Radeon HD 6800 .*ATI.*Radeon HD *68.. 4 1 1 -ATI Radeon HD 6900 .*ATI.*Radeon HD *69.. 5 1 1 -ATI Radeon OpenGL .*ATI.*Radeon OpenGL.* 0 0 0 -ATI Radeon 2100 .*ATI.*Radeon 21.. 0 1 1 -ATI Radeon 3000 .*ATI.*Radeon 30.. 1 1 1 -ATI Radeon 3100 .*ATI.*Radeon 31.. 0 1 1 -ATI Radeon 5xxx .*ATI.*Radeon 5... 3 1 0 -ATI Radeon 7xxx .*ATI.*Radeon 7... 0 1 1 -ATI Radeon 8xxx .*ATI.*Radeon 8... 0 1 0 -ATI Radeon 9000 .*ATI.*Radeon 90.. 0 1 1 -ATI Radeon 9100 .*ATI.*Radeon 91.. 0 1 0 -ATI Radeon 9200 .*ATI.*Radeon 92.. 0 1 1 -ATI Radeon 9500 .*ATI.*Radeon 95.. 0 1 1 -ATI Radeon 9600 .*ATI.*Radeon 96.. 0 1 1 -ATI Radeon 9700 .*ATI.*Radeon 97.. 1 1 0 -ATI Radeon 9800 .*ATI.*Radeon 98.. 1 1 1 -ATI Radeon RV250 .*ATI.*RV250.* 0 1 0 -ATI Radeon RV600 .*ATI.*RV6.* 1 1 0 -ATI Radeon RX700 .*ATI.*RX70.* 1 1 0 -ATI Radeon RX800 .*ATI.*Radeon *RX80.* 2 1 0 -ATI RS880M .*ATI.*RS880M 1 1 0 -ATI Radeon RX9550 .*ATI.*RX9550.* 1 1 0 -ATI Radeon VE .*ATI.*Radeon.*VE.* 0 0 0 -ATI Radeon X300 .*ATI.*Radeon *X3.* 1 1 1 -ATI Radeon X400 .*ATI.*Radeon ?X4.* 0 1 0 -ATI Radeon X500 .*ATI.*Radeon ?X5.* 1 1 1 -ATI Radeon X600 .*ATI.*Radeon ?X6.* 1 1 1 -ATI Radeon X700 .*ATI.*Radeon ?X7.* 2 1 1 -ATI Radeon X800 .*ATI.*Radeon ?X8.* 1 1 1 -ATI Radeon X900 .*ATI.*Radeon ?X9.* 2 1 0 -ATI Radeon Xpress .*ATI.*Radeon Xpress.* 0 1 1 -ATI Rage 128 .*ATI.*Rage 128.* 0 1 0 -ATI R300 (9700) .*R300.* 0 1 1 -ATI R350 (9800) .*R350.* 1 1 0 -ATI R580 (X1900) .*R580.* 3 1 0 -ATI RC410 (Xpress 200) .*RC410.* 0 0 0 -ATI RS48x (Xpress 200x) .*RS48.* 0 0 0 -ATI RS600 (Xpress 3200) .*RS600.* 0 0 0 -ATI RV350 (9600) .*RV350.* 0 1 0 -ATI RV370 (X300) .*RV370.* 0 1 0 -ATI RV410 (X700) .*RV410.* 1 1 0 -ATI RV515 .*RV515.* 1 1 0 -ATI RV570 (X1900 GT/PRO) .*RV570.* 3 1 0 -ATI RV380 .*RV380.* 0 1 0 -ATI RV530 .*RV530.* 1 1 0 -ATI RX480 (Xpress 200P) .*RX480.* 0 1 0 -ATI RX700 .*RX700.* 1 1 0 -AMD ANTILLES (HD 6990) .*(AMD|ATI).*Antilles.* 3 1 0 -AMD BARTS (HD 6800) .*(AMD|ATI).*Barts.* 3 1 1 -AMD CAICOS (HD 6400) .*(AMD|ATI).*Caicos.* 3 1 0 -AMD CAYMAN (HD 6900) .*(AMD|ATI).*(Cayman|CAYMAM).* 3 1 0 -AMD CEDAR (HD 5450) .*(AMD|ATI).*Cedar.* 2 1 0 -AMD CYPRESS (HD 5800) .*(AMD|ATI).*Cypress.* 3 1 0 -AMD HEMLOCK (HD 5970) .*(AMD|ATI).*Hemlock.* 3 1 0 -AMD JUNIPER (HD 5700) .*(AMD|ATI).*Juniper.* 3 1 0 -AMD PARK .*(AMD|ATI).*Park.* 3 1 0 -AMD REDWOOD (HD 5500/5600) .*(AMD|ATI).*Redwood.* 3 1 0 -AMD TURKS (HD 6500/6600) .*(AMD|ATI).*Turks.* 3 1 0 -AMD RS780 (HD 3200) .*RS780.* 0 1 1 -AMD RS880 (HD 4200) .*RS880.* 0 1 1 -AMD RV610 (HD 2400) .*RV610.* 1 1 0 -AMD RV620 (HD 3400) .*RV620.* 1 1 0 -AMD RV630 (HD 2600) .*RV630.* 2 1 0 -AMD RV635 (HD 3600) .*RV635.* 3 1 0 -AMD RV670 (HD 3800) .*RV670.* 3 1 0 -AMD R680 (HD 3870 X2) .*R680.* 3 1 0 -AMD R700 (HD 4800 X2) .*R700.* 3 1 0 -AMD RV710 (HD 4300) .*RV710.* 0 1 1 -AMD RV730 (HD 4600) .*RV730.* 3 1 0 -AMD RV740 (HD 4700) .*RV740.* 3 1 0 -AMD RV770 (HD 4800) .*RV770.* 3 1 0 -AMD RV790 (HD 4800) .*RV790.* 3 1 0 -ATI 760G/Radeon 3000 .*ATI.*AMD 760G.* 1 1 1 -ATI 780L/Radeon 3000 .*ATI.*AMD 780L.* 1 1 0 -ATI Radeon DDR .*ATI.*Radeon ?DDR.* 0 1 0 -ATI FirePro 2000 .*ATI.*FirePro 2.* 2 1 1 -ATI FirePro 3000 .*ATI.*FirePro V3.* 1 1 0 -ATI FirePro 4000 .*ATI.*FirePro V4.* 2 1 0 -ATI FirePro 5000 .*ATI.*FirePro V5.* 3 1 0 -ATI FirePro 7000 .*ATI.*FirePro V7.* 3 1 0 -ATI FirePro M .*ATI.*FirePro M.* 3 1 1 -ATI Technologies .*ATI *Technologies.* 4 1 1 -ATI R300 (9700) .*R300.* 0 1 1 -ATI Radeon .*ATI.*(Diamond|Radeon).* 0 1 0 -Intel X3100 .*Intel.*X3100.* 1 1 1 -Intel GMA 3600 .*Intel.* 3600.* 0 1 1 -Intel 830M .*Intel.*830M 0 0 0 -Intel 845G .*Intel.*845G 0 0 1 -Intel 855GM .*Intel.*855GM 0 0 1 -Intel 865G .*Intel.*865G 0 0 1 -Intel 900 .*Intel.*900.*900 0 0 0 -Intel 915GM .*Intel.*915GM 0 0 1 -Intel 915G .*Intel.*915G 0 0 1 -Intel 945GM .*Intel.*945GM.* 0 1 1 -Intel 945G .*Intel.*945G.* 0 1 1 -Intel 950 .*Intel.*950.* 0 1 1 -Intel 965 .*Intel.*965.* 0 1 1 -Intel G33 .*Intel.*G33.* 1 0 1 -Intel G41 .*Intel.*G41.* 1 1 1 -Intel G45 .*Intel.*G45.* 1 1 1 -Intel Bear Lake .*Intel.*Bear Lake.* 1 0 1 -Intel Broadwater .*Intel.*Broadwater.* 0 0 1 -Intel Brookdale .*Intel.*Brookdale.* 0 0 1 -Intel Cantiga .*Intel.*Cantiga.* 0 0 1 -Intel Eaglelake .*Intel.*Eaglelake.* 1 1 1 -Intel Graphics Media HD .*Intel.*Graphics Media.*HD.* 1 1 1 -Intel HD Graphics .*Intel.*HD Graphics.* 2 1 1 -Intel Mobile 4 Series .*Intel.*Mobile.* 4 Series.* 0 1 1 -Intel 4 Series Internal .*Intel.* 4 Series Internal.* 1 1 1 -Intel Media Graphics HD .*Intel.*Media Graphics HD.* 0 1 0 -Intel Montara .*Intel.*Montara.* 0 0 1 -Intel Pineview .*Intel.*Pineview.* 0 1 1 -Intel Springdale .*Intel.*Springdale.* 0 0 1 -Intel Grantsdale .*Intel.*Grantsdale.* 1 1 0 -Intel HD Graphics 2000 .*Intel.*HD2000.* 1 1 0 -Intel HD Graphics 3000 .*Intel.*HD3000.* 2 1 0 -Intel Q45/Q43 .*Intel.*Q4.* 1 1 1 -Intel B45/B43 .*Intel.*B4.* 1 1 1 -Matrox .*Matrox.* 0 0 0 -Mesa .*Mesa.* 1 0 1 -Gallium .*Gallium.* 1 1 1 -NVIDIA 205 .*NVIDIA .*GeForce 205.* 2 1 1 -NVIDIA 210 .*NVIDIA .*GeForce 210.* 3 1 1 -NVIDIA 310M .*NVIDIA .*GeForce 310M.* 3 1 1 -NVIDIA 310 .*NVIDIA .*GeForce 310.* 3 1 1 -NVIDIA 315M .*NVIDIA .*GeForce 315M.* 3 1 1 -NVIDIA 315 .*NVIDIA .*GeForce 315.* 3 1 1 -NVIDIA 320M .*NVIDIA .*GeForce 320M.* 3 1 1 -NVIDIA G100M .*NVIDIA .*100M.* 4 1 1 -NVIDIA G100 .*NVIDIA .*100.* 3 1 1 -NVIDIA G102M .*NVIDIA .*102M.* 1 1 1 -NVIDIA G103M .*NVIDIA .*103M.* 2 1 1 -NVIDIA G105M .*NVIDIA .*105M.* 2 1 1 -NVIDIA G 110M .*NVIDIA .*110M.* 1 1 1 -NVIDIA G 120M .*NVIDIA .*120M.* 1 1 1 -NVIDIA G 200 .*NVIDIA .*200(M)?.* 1 1 1 -NVIDIA G 205M .*NVIDIA .*205(M)?.* 0 1 0 -NVIDIA G 210 .*NVIDIA .*210(M)?.* 2 1 1 -NVIDIA 305M .*NVIDIA .*305(M)?.* 1 1 0 -NVIDIA G 310M .*NVIDIA .*310(M)?.* 2 1 0 -NVIDIA G 315 .*NVIDIA .*315(M)?.* 2 1 0 -NVIDIA G 320M .*NVIDIA .*320(M)?.* 3 1 1 -NVIDIA G 405 .*NVIDIA .*405(M)?.* 3 1 1 -NVIDIA G 410M .*NVIDIA .*410(M)?.* 3 1 1 -NVIDIA GT 120M .*NVIDIA .*GT *120(M)?.* 3 1 1 -NVIDIA GT 120 .*NVIDIA .*GT.*120 2 1 0 -NVIDIA GT 130M .*NVIDIA .*GT *130(M)?.* 3 1 1 -NVIDIA GT 140M .*NVIDIA .*GT *140(M)?.* 3 1 1 -NVIDIA GT 150M .*NVIDIA .*GT(S)? *150(M)?.* 2 1 0 -NVIDIA GT 160M .*NVIDIA .*GT *160(M)?.* 2 1 0 -NVIDIA GT 220M .*NVIDIA .*GT *220(M)?.* 3 1 1 -NVIDIA GT 230M .*NVIDIA .*GT *230(M)?.* 3 1 1 -NVIDIA GT 240M .*NVIDIA .*GT *240(M)?.* 3 1 1 -NVIDIA GT 250M .*NVIDIA .*GT *250(M)?.* 2 1 0 -NVIDIA GT 260M .*NVIDIA .*GT *260(M)?.* 2 1 0 -NVIDIA GT 320M .*NVIDIA .*GT *320(M)?.* 2 1 0 -NVIDIA GT 325M .*NVIDIA .*GT *325(M)?.* 3 1 1 -NVIDIA GT 330M .*NVIDIA .*GT *330(M)?.* 3 1 1 -NVIDIA GT 335M .*NVIDIA .*GT *335(M)?.* 3 1 1 -NVIDIA GT 340M .*NVIDIA .*GT *340(M)?.* 4 1 1 -NVIDIA GT 415M .*NVIDIA .*GT *415(M)?.* 3 1 1 -NVIDIA GT 420M .*NVIDIA .*GT *420(M)?.* 3 1 1 -NVIDIA GT 425M .*NVIDIA .*GT *425(M)?.* 4 1 1 -NVIDIA GT 430M .*NVIDIA .*GT *430(M)?.* 3 1 1 -NVIDIA GT 435M .*NVIDIA .*GT *435(M)?.* 4 1 1 -NVIDIA GT 440M .*NVIDIA .*GT *440(M)?.* 3 1 1 -NVIDIA GT 445M .*NVIDIA .*GT *445(M)?.* 3 1 1 -NVIDIA GT 450M .*NVIDIA .*GT *450(M)?.* 3 1 0 -NVIDIA GT 520M .*NVIDIA .*GT *52.(M)?.* 3 1 1 -NVIDIA GT 530M .*NVIDIA .*GT *530(M)?.* 3 1 1 -NVIDIA GT 540M .*NVIDIA .*GT *54.(M)?.* 3 1 1 -NVIDIA GT 550M .*NVIDIA .*GT *550(M)?.* 3 1 1 -NVIDIA GT 555M .*NVIDIA .*GT *555(M)?.* 3 1 1 -NVIDIA GT 610 .*NVIDIA .*GT *61.* 3 1 1 -NVIDIA GT 620 .*NVIDIA .*GT *62.* 3 1 0 -NVIDIA GT 630 .*NVIDIA .*GT *63.* 3 1 1 -NVIDIA GT 640 .*NVIDIA .*GT *64.* 3 1 0 -NVIDIA GT 650 .*NVIDIA .*GT *65.* 3 1 1 -NVIDIA GTX 660 .*NVIDIA .*GTX *66.* 5 1 0 -NVIDIA GTX 670 .*NVIDIA .*GTX *67.* 5 1 1 -NVIDIA GTX 680 .*NVIDIA .*GTX *68.* 5 1 1 -NVIDIA GTX 690 .*NVIDIA .*GTX *69.* 5 1 1 -NVIDIA GTS 160M .*NVIDIA .*GT(S)? *160(M)?.* 2 1 0 -NVIDIA GTS 240 .*NVIDIA .*GTS *24.* 3 1 1 -NVIDIA GTS 250 .*NVIDIA .*GTS *25.* 4 1 1 -NVIDIA GTS 350M .*NVIDIA .*GTS *350M.* 4 1 1 -NVIDIA GTS 360M .*NVIDIA .*GTS *360M.* 5 1 1 -NVIDIA GTS 360 .*NVIDIA .*GTS *360.* 3 1 0 -NVIDIA GTS 450 .*NVIDIA .*GTS *45.* 4 1 1 -NVIDIA GTX 260 .*NVIDIA .*GTX *26.* 4 1 1 -NVIDIA GTX 275 .*NVIDIA .*GTX *275.* 4 1 1 -NVIDIA GTX 270 .*NVIDIA .*GTX *27.* 3 1 0 -NVIDIA GTX 285 .*NVIDIA .*GTX *285.* 5 1 1 -NVIDIA GTX 280 .*NVIDIA .*GTX *280.* 4 1 1 -NVIDIA GTX 290 .*NVIDIA .*GTX *290.* 3 1 0 -NVIDIA GTX 295 .*NVIDIA .*GTX *295.* 5 1 1 -NVIDIA GTX 460M .*NVIDIA .*GTX *460M.* 4 1 1 -NVIDIA GTX 465 .*NVIDIA .*GTX *465.* 5 1 1 -NVIDIA GTX 460 .*NVIDIA .*GTX *46.* 5 1 1 -NVIDIA GTX 470M .*NVIDIA .*GTX *470M.* 3 1 0 -NVIDIA GTX 470 .*NVIDIA .*GTX *47.* 5 1 1 -NVIDIA GTX 480M .*NVIDIA .*GTX *480M.* 3 1 1 -NVIDIA GTX 485M .*NVIDIA .*GTX *485M.* 3 1 0 -NVIDIA GTX 480 .*NVIDIA .*GTX *48.* 5 1 1 -NVIDIA GTX 530 .*NVIDIA .*GTX *53.* 3 1 0 -NVIDIA GTX 550 .*NVIDIA .*GTX *55.* 5 1 1 -NVIDIA GTX 560 .*NVIDIA .*GTX *56.* 5 1 1 -NVIDIA GTX 570 .*NVIDIA .*GTX *57.* 5 1 1 -NVIDIA GTX 580M .*NVIDIA .*GTX *580M.* 5 1 1 -NVIDIA GTX 580 .*NVIDIA .*GTX *58.* 5 1 1 -NVIDIA GTX 590 .*NVIDIA .*GTX *59.* 5 1 1 -NVIDIA C51 .*NVIDIA .*C51.* 0 1 1 -NVIDIA G72 .*NVIDIA .*G72.* 1 1 0 -NVIDIA G73 .*NVIDIA .*G73.* 1 1 0 -NVIDIA G84 .*NVIDIA .*G84.* 2 1 0 -NVIDIA G86 .*NVIDIA .*G86.* 3 1 0 -NVIDIA G92 .*NVIDIA .*G92.* 3 1 0 -NVIDIA GeForce .*GeForce 256.* 0 0 0 -NVIDIA GeForce 2 .*GeForce ?2 ?.* 0 1 1 -NVIDIA GeForce 3 .*GeForce ?3 ?.* 0 1 1 -NVIDIA GeForce 3 Ti .*GeForce ?3 Ti.* 0 1 0 -NVIDIA GeForce 4 .*NVIDIA .*GeForce ?4.* 0 1 1 -NVIDIA GeForce 4 Go .*NVIDIA .*GeForce ?4.*Go.* 0 1 0 -NVIDIA GeForce 4 MX .*NVIDIA .*GeForce ?4 MX.* 0 1 0 -NVIDIA GeForce 4 PCX .*NVIDIA .*GeForce ?4 PCX.* 0 1 0 -NVIDIA GeForce 4 Ti .*NVIDIA .*GeForce ?4 Ti.* 0 1 0 -NVIDIA GeForce 6100 .*NVIDIA .*GeForce 61.* 3 1 1 -NVIDIA GeForce 6200 .*NVIDIA .*GeForce 62.* 0 1 0 -NVIDIA GeForce 6500 .*NVIDIA .*GeForce 65.* 1 1 1 -NVIDIA GeForce 6600 .*NVIDIA .*GeForce 66.* 2 1 1 -NVIDIA GeForce 6700 .*NVIDIA .*GeForce 67.* 2 1 1 -NVIDIA GeForce 6800 .*NVIDIA .*GeForce 68.* 1 1 1 -NVIDIA GeForce 7000 .*NVIDIA .*GeForce 70.* 1 1 1 -NVIDIA GeForce 7100 .*NVIDIA .*GeForce 71.* 1 1 1 -NVIDIA GeForce 7200 .*NVIDIA .*GeForce 72.* 1 1 0 -NVIDIA GeForce 7300 .*NVIDIA .*GeForce 73.* 1 1 1 -NVIDIA GeForce 7500 .*NVIDIA .*GeForce 75.* 2 1 1 -NVIDIA GeForce 7600 .*NVIDIA .*GeForce 76.* 2 1 1 -NVIDIA GeForce 7800 .*NVIDIA .*GeForce 78.* 2 1 1 -NVIDIA GeForce 7900 .*NVIDIA .*GeForce 79.* 3 1 1 -NVIDIA GeForce 8100 .*NVIDIA .*GeForce 81.* 1 1 0 -NVIDIA GeForce 8200M .*NVIDIA .*GeForce 8200M.* 1 1 0 -NVIDIA GeForce 8200 .*NVIDIA .*GeForce 82.* 1 1 0 -NVIDIA GeForce 8300 .*NVIDIA .*GeForce 83.* 3 1 1 -NVIDIA GeForce 8400M .*NVIDIA .*GeForce 8400M.* 1 1 1 -NVIDIA GeForce 8400 .*NVIDIA .*GeForce 84.* 2 1 1 -NVIDIA GeForce 8500 .*NVIDIA .*GeForce 85.* 2 1 1 -NVIDIA GeForce 8600M .*NVIDIA .*GeForce 8600M.* 2 1 1 -NVIDIA GeForce 8600 .*NVIDIA .*GeForce 86.* 3 1 1 -NVIDIA GeForce 8700M .*NVIDIA .*GeForce 8700M.* 2 1 1 -NVIDIA GeForce 8700 .*NVIDIA .*GeForce 87.* 3 1 0 -NVIDIA GeForce 8800M .*NVIDIA .*GeForce 8800M.* 2 1 1 -NVIDIA GeForce 8800 .*NVIDIA .*GeForce 88.* 3 1 1 -NVIDIA GeForce 9100M .*NVIDIA .*GeForce 9100M.* 0 1 0 -NVIDIA GeForce 9100 .*NVIDIA .*GeForce 91.* 0 1 0 -NVIDIA GeForce 9200M .*NVIDIA .*GeForce 9200M.* 1 1 0 -NVIDIA GeForce 9200 .*NVIDIA .*GeForce 92.* 1 1 0 -NVIDIA GeForce 9300M .*NVIDIA .*GeForce 9300M.* 1 1 1 -NVIDIA GeForce 9300 .*NVIDIA .*GeForce 93.* 1 1 1 -NVIDIA GeForce 9400M .*NVIDIA .*GeForce 9400M.* 2 1 1 -NVIDIA GeForce 9400 .*NVIDIA .*GeForce 94.* 3 1 1 -NVIDIA GeForce 9500M .*NVIDIA .*GeForce 9500M.* 1 1 1 -NVIDIA GeForce 9500 .*NVIDIA .*GeForce 95.* 3 1 1 -NVIDIA GeForce 9600M .*NVIDIA .*GeForce 9600M.* 2 1 1 -NVIDIA GeForce 9600 .*NVIDIA .*GeForce 96.* 3 1 1 -NVIDIA GeForce 9700M .*NVIDIA .*GeForce 9700M.* 0 1 1 -NVIDIA GeForce 9800M .*NVIDIA .*GeForce 9800M.* 2 1 1 -NVIDIA GeForce 9800 .*NVIDIA .*GeForce 98.* 3 1 1 -NVIDIA GeForce FX 5100 .*NVIDIA .*GeForce FX 51.* 0 1 0 -NVIDIA GeForce FX 5200 .*NVIDIA .*GeForce FX 52.* 0 1 0 -NVIDIA GeForce FX 5300 .*NVIDIA .*GeForce FX 53.* 0 1 0 -NVIDIA GeForce FX 5500 .*NVIDIA .*GeForce FX 55.* 0 1 1 -NVIDIA GeForce FX 5600 .*NVIDIA .*GeForce FX 56.* 1 1 1 -NVIDIA GeForce FX 5700 .*NVIDIA .*GeForce FX 57.* 0 1 1 -NVIDIA GeForce FX 5800 .*NVIDIA .*GeForce FX 58.* 1 1 0 -NVIDIA GeForce FX 5900 .*NVIDIA .*GeForce FX 59.* 1 1 1 -NVIDIA GeForce FX Go5100 .*NVIDIA .*GeForce FX Go51.* 0 1 0 -NVIDIA GeForce FX Go5200 .*NVIDIA .*GeForce FX Go52.* 0 1 0 -NVIDIA GeForce FX Go5300 .*NVIDIA .*GeForce FX Go53.* 0 1 0 -NVIDIA GeForce FX Go5500 .*NVIDIA .*GeForce FX Go55.* 0 1 0 -NVIDIA GeForce FX Go5600 .*NVIDIA .*GeForce FX Go56.* 0 1 1 -NVIDIA GeForce FX Go5700 .*NVIDIA .*GeForce FX Go57.* 1 1 1 -NVIDIA GeForce FX Go5800 .*NVIDIA .*GeForce FX Go58.* 1 1 0 -NVIDIA GeForce FX Go5900 .*NVIDIA .*GeForce FX Go59.* 1 1 0 -NVIDIA GeForce FX Go5xxx .*NVIDIA .*GeForce FX Go.* 0 1 0 -NVIDIA GeForce Go 6100 .*NVIDIA .*GeForce Go 61.* 0 1 1 -NVIDIA GeForce Go 6200 .*NVIDIA .*GeForce Go 62.* 0 1 0 -NVIDIA GeForce Go 6400 .*NVIDIA .*GeForce Go 64.* 1 1 1 -NVIDIA GeForce Go 6500 .*NVIDIA .*GeForce Go 65.* 1 1 0 -NVIDIA GeForce Go 6600 .*NVIDIA .*GeForce Go 66.* 1 1 1 -NVIDIA GeForce Go 6700 .*NVIDIA .*GeForce Go 67.* 1 1 0 -NVIDIA GeForce Go 6800 .*NVIDIA .*GeForce Go 68.* 0 1 1 -NVIDIA GeForce Go 7200 .*NVIDIA .*GeForce Go 72.* 1 1 0 -NVIDIA GeForce Go 7300 LE .*NVIDIA .*GeForce Go 73.*LE.* 0 1 0 -NVIDIA GeForce Go 7300 .*NVIDIA .*GeForce Go 73.* 1 1 1 -NVIDIA GeForce Go 7400 .*NVIDIA .*GeForce Go 74.* 1 1 1 -NVIDIA GeForce Go 7600 .*NVIDIA .*GeForce Go 76.* 1 1 1 -NVIDIA GeForce Go 7700 .*NVIDIA .*GeForce Go 77.* 0 1 1 -NVIDIA GeForce Go 7800 .*NVIDIA .*GeForce Go 78.* 2 1 0 -NVIDIA GeForce Go 7900 .*NVIDIA .*GeForce Go 79.* 1 1 1 -NVIDIA D9M .*NVIDIA .*D9M.* 1 1 0 -NVIDIA G94 .*NVIDIA .*G94.* 3 1 0 -NVIDIA GeForce Go 6 .*GeForce Go 6.* 1 1 0 -NVIDIA ION 2 .*NVIDIA .*ION 2.* 2 1 0 -NVIDIA ION .*NVIDIA Corporation.*ION.* 2 1 1 -NVIDIA NB8M .*NVIDIA .*NB8M.* 1 1 0 -NVIDIA NB8P .*NVIDIA .*NB8P.* 2 1 0 -NVIDIA NB9E .*NVIDIA .*NB9E.* 3 1 0 -NVIDIA NB9M .*NVIDIA .*NB9M.* 1 1 0 -NVIDIA NB9P .*NVIDIA .*NB9P.* 2 1 0 -NVIDIA N10 .*NVIDIA .*N10.* 1 1 0 -NVIDIA GeForce PCX .*GeForce PCX.* 0 1 0 -NVIDIA Generic .*NVIDIA .*Unknown.* 0 0 0 -NVIDIA NV17 .*NVIDIA .*NV17.* 0 1 0 -NVIDIA NV34 .*NVIDIA .*NV34.* 0 1 0 -NVIDIA NV35 .*NVIDIA .*NV35.* 0 1 0 -NVIDIA NV36 .*NVIDIA .*NV36.* 1 1 0 -NVIDIA NV41 .*NVIDIA .*NV41.* 1 1 0 -NVIDIA NV43 .*NVIDIA .*NV43.* 1 1 0 -NVIDIA NV44 .*NVIDIA .*NV44.* 1 1 0 -NVIDIA nForce .*NVIDIA .*nForce.* 0 0 0 -NVIDIA MCP51 .*NVIDIA .*MCP51.* 1 1 0 -NVIDIA MCP61 .*NVIDIA .*MCP61.* 1 1 0 -NVIDIA MCP67 .*NVIDIA .*MCP67.* 1 1 0 -NVIDIA MCP68 .*NVIDIA .*MCP68.* 1 1 0 -NVIDIA MCP73 .*NVIDIA .*MCP73.* 1 1 0 -NVIDIA MCP77 .*NVIDIA .*MCP77.* 1 1 0 -NVIDIA MCP78 .*NVIDIA .*MCP78.* 1 1 0 -NVIDIA MCP79 .*NVIDIA .*MCP79.* 1 1 0 -NVIDIA MCP7A .*NVIDIA .*MCP7A.* 1 1 0 -NVIDIA Quadro2 .*Quadro2.* 0 1 0 -NVIDIA Quadro 1000M .*Quadro.*1000M.* 2 1 0 -NVIDIA Quadro 2000 M/D .*Quadro.*2000.* 3 1 0 -NVIDIA Quadro 3000M .*Quadro.*3000M.* 3 1 0 -NVIDIA Quadro 4000M .*Quadro.*4000M.* 3 1 0 -NVIDIA Quadro 4000 .*Quadro *4000.* 3 1 0 -NVIDIA Quadro 50x0 M .*Quadro.*50.0.* 3 1 0 -NVIDIA Quadro 6000 .*Quadro.*6000.* 3 1 0 -NVIDIA Quadro 400 .*Quadro.*400.* 2 1 0 -NVIDIA Quadro 600 .*Quadro.*600.* 2 1 0 -NVIDIA Quadro4 .*Quadro4.* 0 1 0 -NVIDIA Quadro DCC .*Quadro DCC.* 0 1 0 -NVIDIA Quadro CX .*Quadro.*CX.* 3 1 0 -NVIDIA Quadro FX 770M .*Quadro.*FX *770M.* 2 1 0 -NVIDIA Quadro FX 1500M .*Quadro.*FX *1500M.* 1 1 0 -NVIDIA Quadro FX 1600M .*Quadro.*FX *1600M.* 2 1 0 -NVIDIA Quadro FX 2500M .*Quadro.*FX *2500M.* 2 1 0 -NVIDIA Quadro FX 2700M .*Quadro.*FX *2700M.* 3 1 0 -NVIDIA Quadro FX 2800M .*Quadro.*FX *2800M.* 3 1 0 -NVIDIA Quadro FX 3500 .*Quadro.*FX *3500.* 2 1 0 -NVIDIA Quadro FX 3600 .*Quadro.*FX *3600.* 3 1 0 -NVIDIA Quadro FX 3700 .*Quadro.*FX *3700.* 3 1 0 -NVIDIA Quadro FX 3800 .*Quadro.*FX *3800.* 3 1 0 -NVIDIA Quadro FX 4500 .*Quadro.*FX *45.* 3 1 0 -NVIDIA Quadro FX 880M .*Quadro.*FX *880M.* 3 1 0 -NVIDIA Quadro FX 4800 .*NVIDIA .*Quadro *FX *4800.* 3 1 0 -NVIDIA Quadro FX .*Quadro FX.* 1 1 0 -NVIDIA Quadro NVS 1xxM .*Quadro NVS *1.[05]M.* 2 1 1 -NVIDIA Quadro NVS 300M .*NVIDIA .*NVS *300M.* 2 1 0 -NVIDIA Quadro NVS 320M .*NVIDIA .*NVS *320M.* 2 1 0 -NVIDIA Quadro NVS 2100M .*NVIDIA .*NVS *2100M.* 2 1 0 -NVIDIA Quadro NVS 3100M .*NVIDIA .*NVS *3100M.* 2 1 0 -NVIDIA Quadro NVS 4200M .*NVIDIA .*NVS *4200M.* 2 1 0 -NVIDIA Quadro NVS 5100M .*NVIDIA .*NVS *5100M.* 2 1 0 -NVIDIA Quadro NVS .*NVIDIA .*NVS 0 1 0 -NVIDIA Corporation N12P .*NVIDIA .*N12P.* 1 1 1 -NVIDIA Corporation N11M .*NVIDIA .*N11M.* 2 1 0 -NVIDIA RIVA TNT .*RIVA TNT.* 0 0 0 -S3 .*S3 Graphics.* 0 0 1 -SiS SiS.* 0 0 1 -Trident Trident.* 0 0 0 -Tungsten Graphics Tungsten.* 0 0 0 -XGI XGI.* 0 0 0 -VIA VIA.* 0 0 0 -Apple Generic Apple.*Generic.* 0 0 0 -Apple Software Renderer Apple.*Software Renderer.* 0 0 0 -Humper Humper.* 0 1 1 -PowerVR SGX545 .*PowerVR SGX.* 1 1 1 +3Dfx .*3Dfx.* 0 0 0 0 +3Dlabs .*3Dlabs.* 0 0 0 0 +ATI 3D-Analyze .*ATI.*3D-Analyze.* 0 0 0 0 +ATI All-in-Wonder 7500 .*ATI.*All-in-Wonder 75.* 0 1 0 0 +ATI All-in-Wonder 8500 .*ATI.*All-in-Wonder 85.* 0 1 0 0 +ATI All-in-Wonder 9200 .*ATI.*All-in-Wonder 92.* 0 1 0 0 +ATI All-in-Wonder 9xxx .*ATI.*All-in-Wonder 9.* 1 1 0 0 +ATI All-in-Wonder HD .*ATI.*All-in-Wonder HD.* 1 1 1 3.3 +ATI All-in-Wonder X600 .*ATI.*All-in-Wonder X6.* 1 1 0 0 +ATI All-in-Wonder X800 .*ATI.*All-in-Wonder X8.* 1 1 1 2.1 +ATI All-in-Wonder X1800 .*ATI.*All-in-Wonder X18.* 3 1 0 0 +ATI All-in-Wonder X1900 .*ATI.*All-in-Wonder X19.* 3 1 0 0 +ATI All-in-Wonder PCI-E .*ATI.*All-in-Wonder.*PCI-E.* 1 1 0 0 +ATI All-in-Wonder Radeon .*ATI.*All-in-Wonder Radeon.* 0 1 0 0 +ATI ASUS ARES .*ATI.*ASUS.*ARES.* 3 1 0 0 +ATI ASUS A9xxx .*ATI.*ASUS.*A9.* 1 1 0 0 +ATI ASUS AH24xx .*ATI.*ASUS.*AH24.* 1 1 1 3.3 +ATI ASUS AH26xx .*ATI.*ASUS.*AH26.* 1 1 1 3.3 +ATI ASUS AH34xx .*ATI.*ASUS.*AH34.* 1 1 1 3.3 +ATI ASUS AH36xx .*ATI.*ASUS.*AH36.* 1 1 1 3.3 +ATI ASUS AH46xx .*ATI.*ASUS.*AH46.* 2 1 1 3.3 +ATI ASUS AX3xx .*ATI.*ASUS.*AX3.* 1 1 0 0 +ATI ASUS AX5xx .*ATI.*ASUS.*AX5.* 1 1 0 0 +ATI ASUS AX8xx .*ATI.*ASUS.*AX8.* 2 1 0 0 +ATI ASUS EAH24xx .*ATI.*ASUS.*EAH24.* 2 1 0 0 +ATI ASUS EAH26xx .*ATI.*ASUS.*EAH26.* 3 1 0 0 +ATI ASUS EAH29xx .*ATI.*ASUS.*EAH29.* 3 1 0 0 +ATI ASUS EAH34xx .*ATI.*ASUS.*EAH34.* 1 1 0 0 +ATI ASUS EAH36xx .*ATI.*ASUS.*EAH36.* 3 1 0 0 +ATI ASUS EAH38xx .*ATI.*ASUS.*EAH38.* 2 1 1 3.3 +ATI ASUS EAH43xx .*ATI.*ASUS.*EAH43.* 2 1 1 3.3 +ATI ASUS EAH45xx .*ATI.*ASUS.*EAH45.* 1 1 0 0 +ATI ASUS EAH48xx .*ATI.*ASUS.*EAH48.* 3 1 1 3.3 +ATI ASUS EAH57xx .*ATI.*ASUS.*EAH57.* 3 1 1 4.1 +ATI ASUS EAH58xx .*ATI.*ASUS.*EAH58.* 5 1 1 4.1 +ATI ASUS EAH6xxx .*ATI.*ASUS.*EAH6.* 3 1 1 4.1 +ATI ASUS Radeon X1xxx .*ATI.*ASUS.*X1.* 2 1 1 2.1 +ATI Radeon X7xx .*ATI.*ASUS.*X7.* 1 1 0 0 +ATI Radeon X19xx .*ATI.*(Radeon|Diamond) X19.* ?.* 2 1 1 2.1 +ATI Radeon X18xx .*ATI.*(Radeon|Diamond) X18.* ?.* 3 1 1 2.1 +ATI Radeon X17xx .*ATI.*(Radeon|Diamond) X17.* ?.* 1 1 1 2.1 +ATI Radeon X16xx .*ATI.*(Radeon|Diamond) X16.* ?.* 1 1 1 2.1 +ATI Radeon X15xx .*ATI.*(Radeon|Diamond) X15.* ?.* 1 1 1 2.1 +ATI Radeon X13xx .*ATI.*(Radeon|Diamond) X13.* ?.* 1 1 1 2.1 +ATI Radeon X1xxx .*ATI.*(Radeon|Diamond) X1.. ?.* 0 1 1 2.1 +ATI Radeon X2xxx .*ATI.*(Radeon|Diamond) X2.. ?.* 1 1 1 2.1 +ATI Display Adapter .*ATI.*display adapter.* 1 1 1 4.1 +ATI FireGL 5200 .*ATI.*FireGL V52.* 1 1 1 2.1 +ATI FireGL 5xxx .*ATI.*FireGL V5.* 3 1 1 3.3 +ATI FireGL .*ATI.*Fire.*GL.* 4 1 1 4.2 +ATI FirePro M3900 .*ATI.*FirePro.*M39.* 2 1 0 0 +ATI FirePro M5800 .*ATI.*FirePro.*M58.* 3 1 0 0 +ATI FirePro M7740 .*ATI.*FirePro.*M77.* 3 1 0 0 +ATI FirePro M7820 .*ATI.*FirePro.*M78.* 5 1 1 4.2 +ATI FireMV .*ATI.*FireMV.* 0 1 1 1.3 +ATI Generic .*ATI.*Generic.* 0 0 0 0 +ATI Hercules 9800 .*ATI.*Hercules.*9800.* 1 1 0 0 +ATI IGP 340M .*ATI.*IGP.*340M.* 0 0 0 0 +ATI M52 .*ATI.*M52.* 1 1 0 0 +ATI M54 .*ATI.*M54.* 1 1 0 0 +ATI M56 .*ATI.*M56.* 1 1 0 0 +ATI M71 .*ATI.*M71.* 1 1 0 0 +ATI M72 .*ATI.*M72.* 1 1 0 0 +ATI M76 .*ATI.*M76.* 3 1 0 0 +ATI Radeon HD 64xx .*ATI.*AMD Radeon.* HD [67]4..[MG] 2 1 1 4.2 +ATI Radeon HD 65xx .*ATI.*AMD Radeon.* HD [67]5..[MG] 2 1 1 4.2 +ATI Radeon HD 66xx .*ATI.*AMD Radeon.* HD [67]6..[MG] 3 1 1 4.2 +ATI Mobility Radeon 4100 .*ATI.*Mobility.*41.. 1 1 1 3.3 +ATI Mobility Radeon 7xxx .*ATI.*Mobility.*Radeon 7.* 0 1 1 1.3 +ATI Mobility Radeon 8xxx .*ATI.*Mobility.*Radeon 8.* 0 1 0 0 +ATI Mobility Radeon 9800 .*ATI.*Mobility.*98.* 1 1 0 0 +ATI Mobility Radeon 9700 .*ATI.*Mobility.*97.* 0 1 1 2.1 +ATI Mobility Radeon 9600 .*ATI.*Mobility.*96.* 1 1 1 2.1 +ATI Mobility Radeon HD 530v .*ATI.*Mobility.*HD *530v.* 1 1 1 3.3 +ATI Mobility Radeon HD 540v .*ATI.*Mobility.*HD *540v.* 1 1 1 3.3 +ATI Mobility Radeon HD 545v .*ATI.*Mobility.*HD *545v.* 2 1 1 4 +ATI Mobility Radeon HD 550v .*ATI.*Mobility.*HD *550v.* 3 1 1 4 +ATI Mobility Radeon HD 560v .*ATI.*Mobility.*HD *560v.* 3 1 1 3.2 +ATI Mobility Radeon HD 565v .*ATI.*Mobility.*HD *565v.* 3 1 1 3.3 +ATI Mobility Radeon HD 2300 .*ATI.*Mobility.*HD *23.* 0 1 1 2.1 +ATI Mobility Radeon HD 2400 .*ATI.*Mobility.*HD *24.* 1 1 1 3.3 +ATI Mobility Radeon HD 2600 .*ATI.*Mobility.*HD *26.* 0 1 1 3.3 +ATI Mobility Radeon HD 2700 .*ATI.*Mobility.*HD *27.* 3 1 0 0 +ATI Mobility Radeon HD 3100 .*ATI.*Mobility.*HD *31.* 0 1 0 0 +ATI Mobility Radeon HD 3200 .*ATI.*Mobility.*HD *32.* 0 1 0 0 +ATI Mobility Radeon HD 3400 .*ATI.*Mobility.*HD *34.* 1 1 1 3.3 +ATI Mobility Radeon HD 3600 .*ATI.*Mobility.*HD *36.* 1 1 1 4 +ATI Mobility Radeon HD 3800 .*ATI.*Mobility.*HD *38.* 3 1 1 3.3 +ATI Mobility Radeon HD 4200 .*ATI.*Mobility.*HD *42.* 1 1 1 4 +ATI Mobility Radeon HD 4300 .*ATI.*Mobility.*HD *43.* 1 1 1 4 +ATI Mobility Radeon HD 4500 .*ATI.*Mobility.*HD *45.* 1 1 1 4 +ATI Mobility Radeon HD 4600 .*ATI.*Mobility.*HD *46.* 2 1 1 3.3 +ATI Mobility Radeon HD 4800 .*ATI.*Mobility.*HD *48.* 3 1 1 3.3 +ATI Mobility Radeon HD 5100 .*ATI.*Mobility.*HD *51.* 3 1 1 3.2 +ATI Mobility Radeon HD 5300 .*ATI.*Mobility.*HD *53.* 3 1 0 0 +ATI Mobility Radeon HD 5400 .*ATI.*Mobility.*HD *54.* 2 1 1 4.2 +ATI Mobility Radeon HD 5500 .*ATI.*Mobility.*HD *55.* 3 1 0 0 +ATI Mobility Radeon HD 5600 .*ATI.*Mobility.*HD *56.* 3 1 1 4.2 +ATI Mobility Radeon HD 5700 .*ATI.*Mobility.*HD *57.* 1 1 1 4.1 +ATI Mobility Radeon HD 6200 .*ATI.*Mobility.*HD *62.* 3 1 0 0 +ATI Mobility Radeon HD 6300 .*ATI.*Mobility.*HD *63.* 3 1 1 4.2 +ATI Mobility Radeon HD 6400M .*ATI.*Mobility.*HD *64.* 3 1 0 0 +ATI Mobility Radeon HD 6500M .*ATI.*Mobility.*HD *65.* 5 1 1 4.2 +ATI Mobility Radeon HD 6600M .*ATI.*Mobility.*HD *66.* 3 1 0 0 +ATI Mobility Radeon HD 6700M .*ATI.*Mobility.*HD *67.* 3 1 0 0 +ATI Mobility Radeon HD 6800M .*ATI.*Mobility.*HD *68.* 3 1 0 0 +ATI Mobility Radeon HD 6900M .*ATI.*Mobility.*HD *69.* 3 1 0 0 +ATI Radeon HD 2300 .*ATI.*Radeon HD *23.. 0 1 1 3.3 +ATI Radeon HD 2400 .*ATI.*Radeon HD *24.. 1 1 1 4 +ATI Radeon HD 2600 .*ATI.*Radeon HD *26.. 2 1 1 3.3 +ATI Radeon HD 2900 .*ATI.*Radeon HD *29.. 3 1 1 3.3 +ATI Radeon HD 3000 .*ATI.*Radeon HD *30.. 0 1 0 0 +ATI Radeon HD 3100 .*ATI.*Radeon HD *31.. 1 1 0 0 +ATI Radeon HD 3200 .*ATI.*Radeon HD *32.. 1 1 1 4 +ATI Radeon HD 3300 .*ATI.*Radeon HD *33.. 1 1 1 3.3 +ATI Radeon HD 3400 .*ATI.*Radeon HD *34.. 1 1 1 4 +ATI Radeon HD 3500 .*ATI.*Radeon HD *35.. 2 1 0 0 +ATI Radeon HD 3600 .*ATI.*Radeon HD *36.. 3 1 1 3.3 +ATI Radeon HD 3700 .*ATI.*Radeon HD *37.. 3 1 0 0 +ATI Radeon HD 3800 .*ATI.*Radeon HD *38.. 3 1 1 4 +ATI Radeon HD 4100 .*ATI.*Radeon HD *41.. 1 1 0 0 +ATI Radeon HD 4200 .*ATI.*Radeon HD *42.. 1 1 1 4 +ATI Radeon HD 4300 .*ATI.*Radeon HD *43.. 2 1 1 4 +ATI Radeon HD 4400 .*ATI.*Radeon HD *44.. 2 1 0 0 +ATI Radeon HD 4500 .*ATI.*Radeon HD *45.. 2 1 1 3.3 +ATI Radeon HD 4600 .*ATI.*Radeon HD *46.. 3 1 1 4 +ATI Radeon HD 4700 .*ATI.*Radeon HD *47.. 3 1 1 3.3 +ATI Radeon HD 4800 .*ATI.*Radeon HD *48.. 3 1 1 4 +ATI Radeon HD 5400 .*ATI.*Radeon HD *54.. 3 1 1 4.2 +ATI Radeon HD 5500 .*ATI.*Radeon HD *55.. 3 1 1 4.2 +ATI Radeon HD 5600 .*ATI.*Radeon HD *56.. 3 1 1 4.2 +ATI Radeon HD 5700 .*ATI.*Radeon HD *57.. 3 1 1 4.2 +ATI Radeon HD 5800 .*ATI.*Radeon HD *58.. 4 1 1 4.2 +ATI Radeon HD 5900 .*ATI.*Radeon HD *59.. 4 1 1 4.2 +ATI Radeon HD 6200 .*ATI.*Radeon HD *62.. 0 1 1 4.2 +ATI Radeon HD 6300 .*ATI.*Radeon HD *63.. 1 1 1 4.2 +ATI Radeon HD 6400 .*ATI.*Radeon HD *64.. 3 1 1 4.2 +ATI Radeon HD 6500 .*ATI.*Radeon HD *65.. 3 1 1 4.2 +ATI Radeon HD 6600 .*ATI.*Radeon HD *66.. 3 1 1 4.2 +ATI Radeon HD 6700 .*ATI.*Radeon HD *67.. 3 1 1 4.2 +ATI Radeon HD 6800 .*ATI.*Radeon HD *68.. 4 1 1 4.2 +ATI Radeon HD 6900 .*ATI.*Radeon HD *69.. 5 1 1 4.2 +ATI Radeon OpenGL .*ATI.*Radeon OpenGL.* 0 0 0 0 +ATI Radeon 2100 .*ATI.*Radeon 21.. 0 1 1 2.1 +ATI Radeon 3000 .*ATI.*Radeon 30.. 1 1 1 4 +ATI Radeon 3100 .*ATI.*Radeon 31.. 0 1 1 3.3 +ATI Radeon 5xxx .*ATI.*Radeon 5... 3 1 0 0 +ATI Radeon 7xxx .*ATI.*Radeon 7... 0 1 1 2 +ATI Radeon 8xxx .*ATI.*Radeon 8... 0 1 0 0 +ATI Radeon 9000 .*ATI.*Radeon 90.. 0 1 1 1.3 +ATI Radeon 9100 .*ATI.*Radeon 91.. 0 1 0 0 +ATI Radeon 9200 .*ATI.*Radeon 92.. 0 1 1 1.3 +ATI Radeon 9500 .*ATI.*Radeon 95.. 0 1 1 2.1 +ATI Radeon 9600 .*ATI.*Radeon 96.. 0 1 1 2.1 +ATI Radeon 9700 .*ATI.*Radeon 97.. 1 1 0 0 +ATI Radeon 9800 .*ATI.*Radeon 98.. 1 1 1 2.1 +ATI Radeon RV250 .*ATI.*RV250.* 0 1 0 0 +ATI Radeon RV600 .*ATI.*RV6.* 1 1 0 0 +ATI Radeon RX700 .*ATI.*RX70.* 1 1 0 0 +ATI Radeon RX800 .*ATI.*Radeon *RX80.* 2 1 0 0 +ATI RS880M .*ATI.*RS880M 1 1 0 0 +ATI Radeon RX9550 .*ATI.*RX9550.* 1 1 0 0 +ATI Radeon VE .*ATI.*Radeon.*VE.* 0 0 0 0 +ATI Radeon X300 .*ATI.*Radeon *X3.* 1 1 1 2.1 +ATI Radeon X400 .*ATI.*Radeon ?X4.* 0 1 0 0 +ATI Radeon X500 .*ATI.*Radeon ?X5.* 1 1 1 2.1 +ATI Radeon X600 .*ATI.*Radeon ?X6.* 1 1 1 2.1 +ATI Radeon X700 .*ATI.*Radeon ?X7.* 2 1 1 2.1 +ATI Radeon X800 .*ATI.*Radeon ?X8.* 1 1 1 2.1 +ATI Radeon X900 .*ATI.*Radeon ?X9.* 2 1 0 0 +ATI Radeon Xpress .*ATI.*Radeon Xpress.* 0 1 1 2.1 +ATI Rage 128 .*ATI.*Rage 128.* 0 1 0 0 +ATI R300 (9700) .*R300.* 0 1 1 2.1 +ATI R350 (9800) .*R350.* 1 1 0 0 +ATI R580 (X1900) .*R580.* 3 1 0 0 +ATI RC410 (Xpress 200) .*RC410.* 0 0 0 0 +ATI RS48x (Xpress 200x) .*RS48.* 0 0 0 0 +ATI RS600 (Xpress 3200) .*RS600.* 0 0 0 0 +ATI RV350 (9600) .*RV350.* 0 1 0 0 +ATI RV370 (X300) .*RV370.* 0 1 0 0 +ATI RV410 (X700) .*RV410.* 1 1 0 0 +ATI RV515 .*RV515.* 1 1 0 0 +ATI RV570 (X1900 GT/PRO) .*RV570.* 3 1 0 0 +ATI RV380 .*RV380.* 0 1 0 0 +ATI RV530 .*RV530.* 1 1 0 0 +ATI RX480 (Xpress 200P) .*RX480.* 0 1 0 0 +ATI RX700 .*RX700.* 1 1 0 0 +AMD ANTILLES (HD 6990) .*(AMD|ATI).*Antilles.* 3 1 0 0 +AMD BARTS (HD 6800) .*(AMD|ATI).*Barts.* 3 1 1 2.1 +AMD CAICOS (HD 6400) .*(AMD|ATI).*Caicos.* 3 1 0 0 +AMD CAYMAN (HD 6900) .*(AMD|ATI).*(Cayman|CAYMAM).* 3 1 0 0 +AMD CEDAR (HD 5450) .*(AMD|ATI).*Cedar.* 2 1 0 0 +AMD CYPRESS (HD 5800) .*(AMD|ATI).*Cypress.* 3 1 0 0 +AMD HEMLOCK (HD 5970) .*(AMD|ATI).*Hemlock.* 3 1 0 0 +AMD JUNIPER (HD 5700) .*(AMD|ATI).*Juniper.* 3 1 0 0 +AMD PARK .*(AMD|ATI).*Park.* 3 1 0 0 +AMD REDWOOD (HD 5500/5600) .*(AMD|ATI).*Redwood.* 3 1 0 0 +AMD TURKS (HD 6500/6600) .*(AMD|ATI).*Turks.* 3 1 0 0 +AMD RS780 (HD 3200) .*RS780.* 0 1 1 2.1 +AMD RS880 (HD 4200) .*RS880.* 0 1 1 3.2 +AMD RV610 (HD 2400) .*RV610.* 1 1 0 0 +AMD RV620 (HD 3400) .*RV620.* 1 1 0 0 +AMD RV630 (HD 2600) .*RV630.* 2 1 0 0 +AMD RV635 (HD 3600) .*RV635.* 3 1 0 0 +AMD RV670 (HD 3800) .*RV670.* 3 1 0 0 +AMD R680 (HD 3870 X2) .*R680.* 3 1 0 0 +AMD R700 (HD 4800 X2) .*R700.* 3 1 0 0 +AMD RV710 (HD 4300) .*RV710.* 0 1 1 1.4 +AMD RV730 (HD 4600) .*RV730.* 3 1 0 0 +AMD RV740 (HD 4700) .*RV740.* 3 1 0 0 +AMD RV770 (HD 4800) .*RV770.* 3 1 0 0 +AMD RV790 (HD 4800) .*RV790.* 3 1 0 0 +ATI 760G/Radeon 3000 .*ATI.*AMD 760G.* 1 1 1 3.3 +ATI 780L/Radeon 3000 .*ATI.*AMD 780L.* 1 1 0 0 +ATI Radeon DDR .*ATI.*Radeon ?DDR.* 0 1 0 0 +ATI FirePro 2000 .*ATI.*FirePro 2.* 2 1 1 4.1 +ATI FirePro 3000 .*ATI.*FirePro V3.* 1 1 0 0 +ATI FirePro 4000 .*ATI.*FirePro V4.* 2 1 0 0 +ATI FirePro 5000 .*ATI.*FirePro V5.* 3 1 0 0 +ATI FirePro 7000 .*ATI.*FirePro V7.* 3 1 0 0 +ATI FirePro M .*ATI.*FirePro M.* 3 1 1 4.2 +ATI Technologies .*ATI *Technologies.* 4 1 1 4.2 +ATI R300 (9700) .*R300.* 0 1 1 2.1 +ATI Radeon .*ATI.*(Diamond|Radeon).* 0 1 0 0 +Intel X3100 .*Intel.*X3100.* 1 1 1 2.1 +Intel GMA 3600 .*Intel.* 3600.* 0 1 1 3 +Intel 830M .*Intel.*830M 0 0 0 0 +Intel 845G .*Intel.*845G 0 0 1 1.4 +Intel 855GM .*Intel.*855GM 0 0 1 1.4 +Intel 865G .*Intel.*865G 0 0 1 1.4 +Intel 900 .*Intel.*900.*900 0 0 0 0 +Intel 915GM .*Intel.*915GM 0 0 1 1.4 +Intel 915G .*Intel.*915G 0 0 1 1.4 +Intel 945GM .*Intel.*945GM.* 0 1 1 1.4 +Intel 945G .*Intel.*945G.* 0 1 1 1.4 +Intel 950 .*Intel.*950.* 0 1 1 1.4 +Intel 965 .*Intel.*965.* 0 1 1 2.1 +Intel G33 .*Intel.*G33.* 1 0 1 1.4 +Intel G41 .*Intel.*G41.* 1 1 1 2.1 +Intel G45 .*Intel.*G45.* 1 1 1 2.1 +Intel Bear Lake .*Intel.*Bear Lake.* 1 0 1 1.4 +Intel Broadwater .*Intel.*Broadwater.* 0 0 1 1.4 +Intel Brookdale .*Intel.*Brookdale.* 0 0 1 1.3 +Intel Cantiga .*Intel.*Cantiga.* 0 0 1 2 +Intel Eaglelake .*Intel.*Eaglelake.* 1 1 1 2 +Intel Graphics Media HD .*Intel.*Graphics Media.*HD.* 1 1 1 2.1 +Intel HD Graphics .*Intel.*HD Graphics.* 2 1 1 4 +Intel Mobile 4 Series .*Intel.*Mobile.* 4 Series.* 0 1 1 2.1 +Intel 4 Series Internal .*Intel.* 4 Series Internal.* 1 1 1 2.1 +Intel Media Graphics HD .*Intel.*Media Graphics HD.* 0 1 0 0 +Intel Montara .*Intel.*Montara.* 0 0 1 1.3 +Intel Pineview .*Intel.*Pineview.* 0 1 1 1.4 +Intel Springdale .*Intel.*Springdale.* 0 0 1 1.3 +Intel Grantsdale .*Intel.*Grantsdale.* 1 1 0 0 +Intel HD Graphics 2000 .*Intel.*HD2000.* 1 1 0 0 +Intel HD Graphics 3000 .*Intel.*HD3000.* 2 1 0 0 +Intel Q45/Q43 .*Intel.*Q4.* 1 1 1 2.1 +Intel B45/B43 .*Intel.*B4.* 1 1 1 2.1 +Matrox .*Matrox.* 0 0 0 0 +Mesa .*Mesa.* 1 0 1 2.1 +Gallium .*Gallium.* 1 1 1 2.1 +NVIDIA 205 .*NVIDIA .*GeForce 205.* 2 1 1 3.3 +NVIDIA 210 .*NVIDIA .*GeForce 210.* 3 1 1 3.3 +NVIDIA 310M .*NVIDIA .*GeForce 310M.* 3 1 1 3.3 +NVIDIA 310 .*NVIDIA .*GeForce 310.* 3 1 1 3.3 +NVIDIA 315M .*NVIDIA .*GeForce 315M.* 3 1 1 3.3 +NVIDIA 315 .*NVIDIA .*GeForce 315.* 3 1 1 3.3 +NVIDIA 320M .*NVIDIA .*GeForce 320M.* 3 1 1 3.3 +NVIDIA G100M .*NVIDIA .*100M.* 4 1 1 3.3 +NVIDIA G100 .*NVIDIA .*100.* 3 1 1 4.2 +NVIDIA G102M .*NVIDIA .*102M.* 1 1 1 3.3 +NVIDIA G103M .*NVIDIA .*103M.* 2 1 1 3.3 +NVIDIA G105M .*NVIDIA .*105M.* 2 1 1 3.3 +NVIDIA G 110M .*NVIDIA .*110M.* 1 1 1 3.3 +NVIDIA G 120M .*NVIDIA .*120M.* 1 1 1 3.3 +NVIDIA G 200 .*NVIDIA .*200(M)?.* 1 1 1 4.2 +NVIDIA G 205M .*NVIDIA .*205(M)?.* 0 1 0 0 +NVIDIA G 210 .*NVIDIA .*210(M)?.* 2 1 1 3.3 +NVIDIA 305M .*NVIDIA .*305(M)?.* 1 1 0 0 +NVIDIA G 310M .*NVIDIA .*310(M)?.* 2 1 0 0 +NVIDIA G 315 .*NVIDIA .*315(M)?.* 2 1 0 0 +NVIDIA G 320M .*NVIDIA .*320(M)?.* 3 1 1 3.3 +NVIDIA G 405 .*NVIDIA .*405(M)?.* 3 1 1 3.3 +NVIDIA G 410M .*NVIDIA .*410(M)?.* 3 1 1 4.2 +NVIDIA GT 120M .*NVIDIA .*GT *120(M)?.* 3 1 1 3.3 +NVIDIA GT 120 .*NVIDIA .*GT.*120 2 1 0 0 +NVIDIA GT 130M .*NVIDIA .*GT *130(M)?.* 3 1 1 3.3 +NVIDIA GT 140M .*NVIDIA .*GT *140(M)?.* 3 1 1 3.3 +NVIDIA GT 150M .*NVIDIA .*GT(S)? *150(M)?.* 2 1 0 0 +NVIDIA GT 160M .*NVIDIA .*GT *160(M)?.* 2 1 0 0 +NVIDIA GT 220M .*NVIDIA .*GT *220(M)?.* 3 1 1 3.3 +NVIDIA GT 230M .*NVIDIA .*GT *230(M)?.* 3 1 1 3.3 +NVIDIA GT 240M .*NVIDIA .*GT *240(M)?.* 3 1 1 3.3 +NVIDIA GT 250M .*NVIDIA .*GT *250(M)?.* 2 1 0 0 +NVIDIA GT 260M .*NVIDIA .*GT *260(M)?.* 2 1 0 0 +NVIDIA GT 320M .*NVIDIA .*GT *320(M)?.* 2 1 0 0 +NVIDIA GT 325M .*NVIDIA .*GT *325(M)?.* 3 1 1 3.3 +NVIDIA GT 330M .*NVIDIA .*GT *330(M)?.* 3 1 1 3.3 +NVIDIA GT 335M .*NVIDIA .*GT *335(M)?.* 3 1 1 3.3 +NVIDIA GT 340M .*NVIDIA .*GT *340(M)?.* 4 1 1 3.3 +NVIDIA GT 415M .*NVIDIA .*GT *415(M)?.* 3 1 1 4.2 +NVIDIA GT 420M .*NVIDIA .*GT *420(M)?.* 3 1 1 4.2 +NVIDIA GT 425M .*NVIDIA .*GT *425(M)?.* 4 1 1 4.2 +NVIDIA GT 430M .*NVIDIA .*GT *430(M)?.* 3 1 1 4.2 +NVIDIA GT 435M .*NVIDIA .*GT *435(M)?.* 4 1 1 4.2 +NVIDIA GT 440M .*NVIDIA .*GT *440(M)?.* 3 1 1 4.2 +NVIDIA GT 445M .*NVIDIA .*GT *445(M)?.* 3 1 1 4.2 +NVIDIA GT 450M .*NVIDIA .*GT *450(M)?.* 3 1 0 0 +NVIDIA GT 520M .*NVIDIA .*GT *52.(M)?.* 3 1 1 4.2 +NVIDIA GT 530M .*NVIDIA .*GT *530(M)?.* 3 1 1 4.2 +NVIDIA GT 540M .*NVIDIA .*GT *54.(M)?.* 3 1 1 4.2 +NVIDIA GT 550M .*NVIDIA .*GT *550(M)?.* 3 1 1 4.2 +NVIDIA GT 555M .*NVIDIA .*GT *555(M)?.* 3 1 1 4.2 +NVIDIA GT 610 .*NVIDIA .*GT *61.* 3 1 1 4.2 +NVIDIA GT 620 .*NVIDIA .*GT *62.* 3 1 0 0 +NVIDIA GT 630 .*NVIDIA .*GT *63.* 3 1 1 4.2 +NVIDIA GT 640 .*NVIDIA .*GT *64.* 3 1 0 0 +NVIDIA GT 650 .*NVIDIA .*GT *65.* 3 1 1 4.2 +NVIDIA GTX 660 .*NVIDIA .*GTX *66.* 5 1 0 0 +NVIDIA GTX 670 .*NVIDIA .*GTX *67.* 5 1 1 4.2 +NVIDIA GTX 680 .*NVIDIA .*GTX *68.* 5 1 1 4.2 +NVIDIA GTX 690 .*NVIDIA .*GTX *69.* 5 1 1 4.2 +NVIDIA GTS 160M .*NVIDIA .*GT(S)? *160(M)?.* 2 1 0 0 +NVIDIA GTS 240 .*NVIDIA .*GTS *24.* 3 1 1 3.3 +NVIDIA GTS 250 .*NVIDIA .*GTS *25.* 4 1 1 3.3 +NVIDIA GTS 350M .*NVIDIA .*GTS *350M.* 4 1 1 3.3 +NVIDIA GTS 360M .*NVIDIA .*GTS *360M.* 5 1 1 3.3 +NVIDIA GTS 360 .*NVIDIA .*GTS *360.* 3 1 0 0 +NVIDIA GTS 450 .*NVIDIA .*GTS *45.* 4 1 1 4.2 +NVIDIA GTX 260 .*NVIDIA .*GTX *26.* 4 1 1 3.3 +NVIDIA GTX 275 .*NVIDIA .*GTX *275.* 4 1 1 3.3 +NVIDIA GTX 270 .*NVIDIA .*GTX *27.* 3 1 0 0 +NVIDIA GTX 285 .*NVIDIA .*GTX *285.* 5 1 1 3.3 +NVIDIA GTX 280 .*NVIDIA .*GTX *280.* 4 1 1 3.3 +NVIDIA GTX 290 .*NVIDIA .*GTX *290.* 3 1 0 0 +NVIDIA GTX 295 .*NVIDIA .*GTX *295.* 5 1 1 3.3 +NVIDIA GTX 460M .*NVIDIA .*GTX *460M.* 4 1 1 4.2 +NVIDIA GTX 465 .*NVIDIA .*GTX *465.* 5 1 1 4.2 +NVIDIA GTX 460 .*NVIDIA .*GTX *46.* 5 1 1 4.2 +NVIDIA GTX 470M .*NVIDIA .*GTX *470M.* 3 1 0 0 +NVIDIA GTX 470 .*NVIDIA .*GTX *47.* 5 1 1 4.2 +NVIDIA GTX 480M .*NVIDIA .*GTX *480M.* 3 1 1 4.2 +NVIDIA GTX 485M .*NVIDIA .*GTX *485M.* 3 1 0 0 +NVIDIA GTX 480 .*NVIDIA .*GTX *48.* 5 1 1 4.2 +NVIDIA GTX 530 .*NVIDIA .*GTX *53.* 3 1 0 0 +NVIDIA GTX 550 .*NVIDIA .*GTX *55.* 5 1 1 4.2 +NVIDIA GTX 560 .*NVIDIA .*GTX *56.* 5 1 1 4.2 +NVIDIA GTX 570 .*NVIDIA .*GTX *57.* 5 1 1 4.2 +NVIDIA GTX 580M .*NVIDIA .*GTX *580M.* 5 1 1 4.2 +NVIDIA GTX 580 .*NVIDIA .*GTX *58.* 5 1 1 4.2 +NVIDIA GTX 590 .*NVIDIA .*GTX *59.* 5 1 1 4.2 +NVIDIA C51 .*NVIDIA .*C51.* 0 1 1 2 +NVIDIA G72 .*NVIDIA .*G72.* 1 1 0 0 +NVIDIA G73 .*NVIDIA .*G73.* 1 1 0 0 +NVIDIA G84 .*NVIDIA .*G84.* 2 1 0 0 +NVIDIA G86 .*NVIDIA .*G86.* 3 1 0 0 +NVIDIA G92 .*NVIDIA .*G92.* 3 1 0 0 +NVIDIA GeForce .*GeForce 256.* 0 0 0 0 +NVIDIA GeForce 2 .*GeForce ?2 ?.* 0 1 1 1.5 +NVIDIA GeForce 3 .*GeForce ?3 ?.* 0 1 1 1.5 +NVIDIA GeForce 3 Ti .*GeForce ?3 Ti.* 0 1 0 0 +NVIDIA GeForce 4 .*NVIDIA .*GeForce ?4.* 0 1 1 1.5 +NVIDIA GeForce 4 Go .*NVIDIA .*GeForce ?4.*Go.* 0 1 0 0 +NVIDIA GeForce 4 MX .*NVIDIA .*GeForce ?4 MX.* 0 1 0 0 +NVIDIA GeForce 4 PCX .*NVIDIA .*GeForce ?4 PCX.* 0 1 0 0 +NVIDIA GeForce 4 Ti .*NVIDIA .*GeForce ?4 Ti.* 0 1 0 0 +NVIDIA GeForce 6100 .*NVIDIA .*GeForce 61.* 3 1 1 4.2 +NVIDIA GeForce 6200 .*NVIDIA .*GeForce 62.* 0 1 0 0 +NVIDIA GeForce 6500 .*NVIDIA .*GeForce 65.* 1 1 1 2.1 +NVIDIA GeForce 6600 .*NVIDIA .*GeForce 66.* 2 1 1 2.1 +NVIDIA GeForce 6700 .*NVIDIA .*GeForce 67.* 2 1 1 2.1 +NVIDIA GeForce 6800 .*NVIDIA .*GeForce 68.* 1 1 1 2.1 +NVIDIA GeForce 7000 .*NVIDIA .*GeForce 70.* 1 1 1 2.1 +NVIDIA GeForce 7100 .*NVIDIA .*GeForce 71.* 1 1 1 2.1 +NVIDIA GeForce 7200 .*NVIDIA .*GeForce 72.* 1 1 0 0 +NVIDIA GeForce 7300 .*NVIDIA .*GeForce 73.* 1 1 1 2.1 +NVIDIA GeForce 7500 .*NVIDIA .*GeForce 75.* 2 1 1 2.1 +NVIDIA GeForce 7600 .*NVIDIA .*GeForce 76.* 2 1 1 2.1 +NVIDIA GeForce 7800 .*NVIDIA .*GeForce 78.* 2 1 1 2.1 +NVIDIA GeForce 7900 .*NVIDIA .*GeForce 79.* 3 1 1 2.1 +NVIDIA GeForce 8100 .*NVIDIA .*GeForce 81.* 1 1 0 0 +NVIDIA GeForce 8200M .*NVIDIA .*GeForce 8200M.* 1 1 0 0 +NVIDIA GeForce 8200 .*NVIDIA .*GeForce 82.* 1 1 0 0 +NVIDIA GeForce 8300 .*NVIDIA .*GeForce 83.* 3 1 1 3.3 +NVIDIA GeForce 8400M .*NVIDIA .*GeForce 8400M.* 1 1 1 3.3 +NVIDIA GeForce 8400 .*NVIDIA .*GeForce 84.* 2 1 1 3.3 +NVIDIA GeForce 8500 .*NVIDIA .*GeForce 85.* 2 1 1 3.3 +NVIDIA GeForce 8600M .*NVIDIA .*GeForce 8600M.* 2 1 1 3.3 +NVIDIA GeForce 8600 .*NVIDIA .*GeForce 86.* 3 1 1 3.3 +NVIDIA GeForce 8700M .*NVIDIA .*GeForce 8700M.* 2 1 1 3.3 +NVIDIA GeForce 8700 .*NVIDIA .*GeForce 87.* 3 1 0 0 +NVIDIA GeForce 8800M .*NVIDIA .*GeForce 8800M.* 2 1 1 3.3 +NVIDIA GeForce 8800 .*NVIDIA .*GeForce 88.* 3 1 1 3.3 +NVIDIA GeForce 9100M .*NVIDIA .*GeForce 9100M.* 0 1 0 0 +NVIDIA GeForce 9100 .*NVIDIA .*GeForce 91.* 0 1 0 0 +NVIDIA GeForce 9200M .*NVIDIA .*GeForce 9200M.* 1 1 0 0 +NVIDIA GeForce 9200 .*NVIDIA .*GeForce 92.* 1 1 0 0 +NVIDIA GeForce 9300M .*NVIDIA .*GeForce 9300M.* 1 1 1 3.3 +NVIDIA GeForce 9300 .*NVIDIA .*GeForce 93.* 1 1 1 3.3 +NVIDIA GeForce 9400M .*NVIDIA .*GeForce 9400M.* 2 1 1 3.3 +NVIDIA GeForce 9400 .*NVIDIA .*GeForce 94.* 3 1 1 3.3 +NVIDIA GeForce 9500M .*NVIDIA .*GeForce 9500M.* 1 1 1 3.3 +NVIDIA GeForce 9500 .*NVIDIA .*GeForce 95.* 3 1 1 3.3 +NVIDIA GeForce 9600M .*NVIDIA .*GeForce 9600M.* 2 1 1 3.3 +NVIDIA GeForce 9600 .*NVIDIA .*GeForce 96.* 3 1 1 3.3 +NVIDIA GeForce 9700M .*NVIDIA .*GeForce 9700M.* 0 1 1 3.3 +NVIDIA GeForce 9800M .*NVIDIA .*GeForce 9800M.* 2 1 1 3.3 +NVIDIA GeForce 9800 .*NVIDIA .*GeForce 98.* 3 1 1 3.3 +NVIDIA GeForce FX 5100 .*NVIDIA .*GeForce FX 51.* 0 1 0 0 +NVIDIA GeForce FX 5200 .*NVIDIA .*GeForce FX 52.* 0 1 0 0 +NVIDIA GeForce FX 5300 .*NVIDIA .*GeForce FX 53.* 0 1 0 0 +NVIDIA GeForce FX 5500 .*NVIDIA .*GeForce FX 55.* 0 1 1 2.1 +NVIDIA GeForce FX 5600 .*NVIDIA .*GeForce FX 56.* 1 1 1 2.1 +NVIDIA GeForce FX 5700 .*NVIDIA .*GeForce FX 57.* 0 1 1 2.1 +NVIDIA GeForce FX 5800 .*NVIDIA .*GeForce FX 58.* 1 1 0 0 +NVIDIA GeForce FX 5900 .*NVIDIA .*GeForce FX 59.* 1 1 1 2.1 +NVIDIA GeForce FX Go5100 .*NVIDIA .*GeForce FX Go51.* 0 1 0 0 +NVIDIA GeForce FX Go5200 .*NVIDIA .*GeForce FX Go52.* 0 1 0 0 +NVIDIA GeForce FX Go5300 .*NVIDIA .*GeForce FX Go53.* 0 1 0 0 +NVIDIA GeForce FX Go5500 .*NVIDIA .*GeForce FX Go55.* 0 1 0 0 +NVIDIA GeForce FX Go5600 .*NVIDIA .*GeForce FX Go56.* 0 1 1 2.1 +NVIDIA GeForce FX Go5700 .*NVIDIA .*GeForce FX Go57.* 1 1 1 1.5 +NVIDIA GeForce FX Go5800 .*NVIDIA .*GeForce FX Go58.* 1 1 0 0 +NVIDIA GeForce FX Go5900 .*NVIDIA .*GeForce FX Go59.* 1 1 0 0 +NVIDIA GeForce FX Go5xxx .*NVIDIA .*GeForce FX Go.* 0 1 0 0 +NVIDIA GeForce Go 6100 .*NVIDIA .*GeForce Go 61.* 0 1 1 2.1 +NVIDIA GeForce Go 6200 .*NVIDIA .*GeForce Go 62.* 0 1 0 0 +NVIDIA GeForce Go 6400 .*NVIDIA .*GeForce Go 64.* 1 1 1 2 +NVIDIA GeForce Go 6500 .*NVIDIA .*GeForce Go 65.* 1 1 0 0 +NVIDIA GeForce Go 6600 .*NVIDIA .*GeForce Go 66.* 1 1 1 2.1 +NVIDIA GeForce Go 6700 .*NVIDIA .*GeForce Go 67.* 1 1 0 0 +NVIDIA GeForce Go 6800 .*NVIDIA .*GeForce Go 68.* 0 1 1 2.1 +NVIDIA GeForce Go 7200 .*NVIDIA .*GeForce Go 72.* 1 1 0 0 +NVIDIA GeForce Go 7300 LE .*NVIDIA .*GeForce Go 73.*LE.* 0 1 0 0 +NVIDIA GeForce Go 7300 .*NVIDIA .*GeForce Go 73.* 1 1 1 2.1 +NVIDIA GeForce Go 7400 .*NVIDIA .*GeForce Go 74.* 1 1 1 2.1 +NVIDIA GeForce Go 7600 .*NVIDIA .*GeForce Go 76.* 1 1 1 2.1 +NVIDIA GeForce Go 7700 .*NVIDIA .*GeForce Go 77.* 0 1 1 2.1 +NVIDIA GeForce Go 7800 .*NVIDIA .*GeForce Go 78.* 2 1 0 0 +NVIDIA GeForce Go 7900 .*NVIDIA .*GeForce Go 79.* 1 1 1 2.1 +NVIDIA D9M .*NVIDIA .*D9M.* 1 1 0 0 +NVIDIA G94 .*NVIDIA .*G94.* 3 1 0 0 +NVIDIA GeForce Go 6 .*GeForce Go 6.* 1 1 0 0 +NVIDIA ION 2 .*NVIDIA .*ION 2.* 2 1 0 0 +NVIDIA ION .*NVIDIA Corporation.*ION.* 2 1 1 4.2 +NVIDIA NB8M .*NVIDIA .*NB8M.* 1 1 0 0 +NVIDIA NB8P .*NVIDIA .*NB8P.* 2 1 0 0 +NVIDIA NB9E .*NVIDIA .*NB9E.* 3 1 0 0 +NVIDIA NB9M .*NVIDIA .*NB9M.* 1 1 0 0 +NVIDIA NB9P .*NVIDIA .*NB9P.* 2 1 0 0 +NVIDIA N10 .*NVIDIA .*N10.* 1 1 0 0 +NVIDIA GeForce PCX .*GeForce PCX.* 0 1 0 0 +NVIDIA Generic .*NVIDIA .*Unknown.* 0 0 0 0 +NVIDIA NV17 .*NVIDIA .*NV17.* 0 1 0 0 +NVIDIA NV34 .*NVIDIA .*NV34.* 0 1 0 0 +NVIDIA NV35 .*NVIDIA .*NV35.* 0 1 0 0 +NVIDIA NV36 .*NVIDIA .*NV36.* 1 1 0 0 +NVIDIA NV41 .*NVIDIA .*NV41.* 1 1 0 0 +NVIDIA NV43 .*NVIDIA .*NV43.* 1 1 0 0 +NVIDIA NV44 .*NVIDIA .*NV44.* 1 1 0 0 +NVIDIA nForce .*NVIDIA .*nForce.* 0 0 0 0 +NVIDIA MCP51 .*NVIDIA .*MCP51.* 1 1 0 0 +NVIDIA MCP61 .*NVIDIA .*MCP61.* 1 1 0 0 +NVIDIA MCP67 .*NVIDIA .*MCP67.* 1 1 0 0 +NVIDIA MCP68 .*NVIDIA .*MCP68.* 1 1 0 0 +NVIDIA MCP73 .*NVIDIA .*MCP73.* 1 1 0 0 +NVIDIA MCP77 .*NVIDIA .*MCP77.* 1 1 0 0 +NVIDIA MCP78 .*NVIDIA .*MCP78.* 1 1 0 0 +NVIDIA MCP79 .*NVIDIA .*MCP79.* 1 1 0 0 +NVIDIA MCP7A .*NVIDIA .*MCP7A.* 1 1 0 0 +NVIDIA Quadro2 .*Quadro2.* 0 1 0 0 +NVIDIA Quadro 1000M .*Quadro.*1000M.* 2 1 0 0 +NVIDIA Quadro 2000 M/D .*Quadro.*2000.* 3 1 0 0 +NVIDIA Quadro 3000M .*Quadro.*3000M.* 3 1 0 0 +NVIDIA Quadro 4000M .*Quadro.*4000M.* 3 1 0 0 +NVIDIA Quadro 4000 .*Quadro *4000.* 3 1 0 0 +NVIDIA Quadro 50x0 M .*Quadro.*50.0.* 3 1 0 0 +NVIDIA Quadro 6000 .*Quadro.*6000.* 3 1 0 0 +NVIDIA Quadro 400 .*Quadro.*400.* 2 1 0 0 +NVIDIA Quadro 600 .*Quadro.*600.* 2 1 0 0 +NVIDIA Quadro4 .*Quadro4.* 0 1 0 0 +NVIDIA Quadro DCC .*Quadro DCC.* 0 1 0 0 +NVIDIA Quadro CX .*Quadro.*CX.* 3 1 0 0 +NVIDIA Quadro FX 770M .*Quadro.*FX *770M.* 2 1 0 0 +NVIDIA Quadro FX 1500M .*Quadro.*FX *1500M.* 1 1 0 0 +NVIDIA Quadro FX 1600M .*Quadro.*FX *1600M.* 2 1 0 0 +NVIDIA Quadro FX 2500M .*Quadro.*FX *2500M.* 2 1 0 0 +NVIDIA Quadro FX 2700M .*Quadro.*FX *2700M.* 3 1 0 0 +NVIDIA Quadro FX 2800M .*Quadro.*FX *2800M.* 3 1 0 0 +NVIDIA Quadro FX 3500 .*Quadro.*FX *3500.* 2 1 0 0 +NVIDIA Quadro FX 3600 .*Quadro.*FX *3600.* 3 1 0 0 +NVIDIA Quadro FX 3700 .*Quadro.*FX *3700.* 3 1 0 0 +NVIDIA Quadro FX 3800 .*Quadro.*FX *3800.* 3 1 0 0 +NVIDIA Quadro FX 4500 .*Quadro.*FX *45.* 3 1 0 0 +NVIDIA Quadro FX 880M .*Quadro.*FX *880M.* 3 1 0 0 +NVIDIA Quadro FX 4800 .*NVIDIA .*Quadro *FX *4800.* 3 1 0 0 +NVIDIA Quadro FX .*Quadro FX.* 1 1 0 0 +NVIDIA Quadro NVS 1xxM .*Quadro NVS *1.[05]M.* 2 1 1 2.1 +NVIDIA Quadro NVS 300M .*NVIDIA .*NVS *300M.* 2 1 0 0 +NVIDIA Quadro NVS 320M .*NVIDIA .*NVS *320M.* 2 1 0 0 +NVIDIA Quadro NVS 2100M .*NVIDIA .*NVS *2100M.* 2 1 0 0 +NVIDIA Quadro NVS 3100M .*NVIDIA .*NVS *3100M.* 2 1 0 0 +NVIDIA Quadro NVS 4200M .*NVIDIA .*NVS *4200M.* 2 1 0 0 +NVIDIA Quadro NVS 5100M .*NVIDIA .*NVS *5100M.* 2 1 0 0 +NVIDIA Quadro NVS .*NVIDIA .*NVS 0 1 0 0 +NVIDIA Corporation N12P .*NVIDIA .*N12P.* 1 1 1 4.1 +NVIDIA Corporation N11M .*NVIDIA .*N11M.* 2 1 0 0 +NVIDIA RIVA TNT .*RIVA TNT.* 0 0 0 0 +S3 .*S3 Graphics.* 0 0 1 1.4 +SiS SiS.* 0 0 1 1.5 +Trident Trident.* 0 0 0 0 +Tungsten Graphics Tungsten.* 0 0 0 0 +XGI XGI.* 0 0 0 0 +VIA VIA.* 0 0 0 0 +Apple Generic Apple.*Generic.* 0 0 0 0 +Apple Software Renderer Apple.*Software Renderer.* 0 0 0 0 +Humper Humper.* 0 1 1 2.1 +PowerVR SGX545 .*PowerVR SGX.* 1 1 1 3 -- cgit v1.2.3 From 15f619c4b948fb0327c59cb9b5ee73fdb6de9c49 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Wed, 29 Aug 2012 16:19:18 -0500 Subject: MAINT-1491 Add Radeon HD 7xxx chips, get rid of "ATI Technologies" catch all, split up NVIDIA GT(X) 6xx series into mobile/desktop. --- indra/newview/gpu_table.txt | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/indra/newview/gpu_table.txt b/indra/newview/gpu_table.txt index 74501c9165..587d237961 100644 --- a/indra/newview/gpu_table.txt +++ b/indra/newview/gpu_table.txt @@ -80,7 +80,7 @@ ATI Radeon X1xxx .*ATI.*(Radeon|Diamond) X1.. ?.* 0 1 1 2.1 ATI Radeon X2xxx .*ATI.*(Radeon|Diamond) X2.. ?.* 1 1 1 2.1 ATI Display Adapter .*ATI.*display adapter.* 1 1 1 4.1 ATI FireGL 5200 .*ATI.*FireGL V52.* 1 1 1 2.1 -ATI FireGL 5xxx .*ATI.*FireGL V5.* 3 1 1 3.3 +ATI FireGL 5xxx .*ATI.*FireGL V5.* 2 1 1 3.3 ATI FireGL .*ATI.*Fire.*GL.* 4 1 1 4.2 ATI FirePro M3900 .*ATI.*FirePro.*M39.* 2 1 0 0 ATI FirePro M5800 .*ATI.*FirePro.*M58.* 3 1 0 0 @@ -99,6 +99,15 @@ ATI M76 .*ATI.*M76.* 3 1 0 0 ATI Radeon HD 64xx .*ATI.*AMD Radeon.* HD [67]4..[MG] 2 1 1 4.2 ATI Radeon HD 65xx .*ATI.*AMD Radeon.* HD [67]5..[MG] 2 1 1 4.2 ATI Radeon HD 66xx .*ATI.*AMD Radeon.* HD [67]6..[MG] 3 1 1 4.2 +ATI Radeon HD 7100 .*ATI.*AMD Radeon.* HD 71.* 2 1 0 0 +ATI Radeon HD 7200 .*ATI.*AMD Radeon.* HD 72.* 2 1 0 0 +ATI Radeon HD 7300 .*ATI.*AMD Radeon.* HD 73.* 2 1 0 0 +ATI Radeon HD 7400 .*ATI.*AMD Radeon.* HD 74.* 2 1 0 0 +ATI Radeon HD 7500 .*ATI.*AMD Radeon.* HD 75.* 3 1 1 4.2 +ATI Radeon HD 7600 .*ATI.*AMD Radeon.* HD 76.* 3 1 0 0 +ATI Radeon HD 7700 .*ATI.*AMD Radeon.* HD 77.* 4 1 1 4.2 +ATI Radeon HD 7800 .*ATI.*AMD Radeon.* HD 78.* 5 1 1 4.2 +ATI Radeon HD 7900 .*ATI.*AMD Radeon.* HD 79.* 5 1 1 4.2 ATI Mobility Radeon 4100 .*ATI.*Mobility.*41.. 1 1 1 3.3 ATI Mobility Radeon 7xxx .*ATI.*Mobility.*Radeon 7.* 0 1 1 1.3 ATI Mobility Radeon 8xxx .*ATI.*Mobility.*Radeon 8.* 0 1 0 0 @@ -113,7 +122,7 @@ ATI Mobility Radeon HD 560v .*ATI.*Mobility.*HD *560v.* 3 1 1 3.2 ATI Mobility Radeon HD 565v .*ATI.*Mobility.*HD *565v.* 3 1 1 3.3 ATI Mobility Radeon HD 2300 .*ATI.*Mobility.*HD *23.* 0 1 1 2.1 ATI Mobility Radeon HD 2400 .*ATI.*Mobility.*HD *24.* 1 1 1 3.3 -ATI Mobility Radeon HD 2600 .*ATI.*Mobility.*HD *26.* 0 1 1 3.3 +ATI Mobility Radeon HD 2600 .*ATI.*Mobility.*HD *26.* 1 1 1 3.3 ATI Mobility Radeon HD 2700 .*ATI.*Mobility.*HD *27.* 3 1 0 0 ATI Mobility Radeon HD 3100 .*ATI.*Mobility.*HD *31.* 0 1 0 0 ATI Mobility Radeon HD 3200 .*ATI.*Mobility.*HD *32.* 0 1 0 0 @@ -130,7 +139,7 @@ ATI Mobility Radeon HD 5300 .*ATI.*Mobility.*HD *53.* 3 1 0 0 ATI Mobility Radeon HD 5400 .*ATI.*Mobility.*HD *54.* 2 1 1 4.2 ATI Mobility Radeon HD 5500 .*ATI.*Mobility.*HD *55.* 3 1 0 0 ATI Mobility Radeon HD 5600 .*ATI.*Mobility.*HD *56.* 3 1 1 4.2 -ATI Mobility Radeon HD 5700 .*ATI.*Mobility.*HD *57.* 1 1 1 4.1 +ATI Mobility Radeon HD 5700 .*ATI.*Mobility.*HD *57.* 3 1 1 4.1 ATI Mobility Radeon HD 6200 .*ATI.*Mobility.*HD *62.* 3 1 0 0 ATI Mobility Radeon HD 6300 .*ATI.*Mobility.*HD *63.* 3 1 1 4.2 ATI Mobility Radeon HD 6400M .*ATI.*Mobility.*HD *64.* 3 1 0 0 @@ -253,7 +262,6 @@ ATI FirePro 4000 .*ATI.*FirePro V4.* 2 1 0 0 ATI FirePro 5000 .*ATI.*FirePro V5.* 3 1 0 0 ATI FirePro 7000 .*ATI.*FirePro V7.* 3 1 0 0 ATI FirePro M .*ATI.*FirePro M.* 3 1 1 4.2 -ATI Technologies .*ATI *Technologies.* 4 1 1 4.2 ATI R300 (9700) .*R300.* 0 1 1 2.1 ATI Radeon .*ATI.*(Diamond|Radeon).* 0 1 0 0 Intel X3100 .*Intel.*X3100.* 1 1 1 2.1 @@ -290,6 +298,7 @@ Intel HD Graphics 2000 .*Intel.*HD2000.* 1 1 0 0 Intel HD Graphics 3000 .*Intel.*HD3000.* 2 1 0 0 Intel Q45/Q43 .*Intel.*Q4.* 1 1 1 2.1 Intel B45/B43 .*Intel.*B4.* 1 1 1 2.1 +Intel 3D-Analyze .*Intel.*3D-Analyze.* 2 1 0 0 Matrox .*Matrox.* 0 0 0 0 Mesa .*Mesa.* 1 0 1 2.1 Gallium .*Gallium.* 1 1 1 2.1 @@ -345,6 +354,15 @@ NVIDIA GT 530M .*NVIDIA .*GT *530(M)?.* 3 1 1 4.2 NVIDIA GT 540M .*NVIDIA .*GT *54.(M)?.* 3 1 1 4.2 NVIDIA GT 550M .*NVIDIA .*GT *550(M)?.* 3 1 1 4.2 NVIDIA GT 555M .*NVIDIA .*GT *555(M)?.* 3 1 1 4.2 +NVIDIA 610M .*NVIDIA.*61*M.* 3 1 1 4.2 +NVIDIA GT 620M .*NVIDIA .*GT *62*M.* 3 1 0 0 +NVIDIA GT 630M .*NVIDIA .*GT *63*M.* 3 1 0 0 +NVIDIA GT 640M .*NVIDIA .*GT *64*M.* 3 1 0 0 +NVIDIA GT 650M .*NVIDIA .*GT *65*M.* 3 1 0 0 +NVIDIA GTX 660M .*NVIDIA .*GTX *66*M.* 5 1 0 0 +NVIDIA GTX 670M .*NVIDIA .*GTX *67*M.* 5 1 1 4.2 +NVIDIA GTX 680M .*NVIDIA .*GTX *68*M.* 5 1 0 0 +NVIDIA GTX 690M .*NVIDIA .*GTX *69*M.* 5 1 0 0 NVIDIA GT 610 .*NVIDIA .*GT *61.* 3 1 1 4.2 NVIDIA GT 620 .*NVIDIA .*GT *62.* 3 1 0 0 NVIDIA GT 630 .*NVIDIA .*GT *63.* 3 1 1 4.2 @@ -355,7 +373,7 @@ NVIDIA GTX 670 .*NVIDIA .*GTX *67.* 5 1 1 4.2 NVIDIA GTX 680 .*NVIDIA .*GTX *68.* 5 1 1 4.2 NVIDIA GTX 690 .*NVIDIA .*GTX *69.* 5 1 1 4.2 NVIDIA GTS 160M .*NVIDIA .*GT(S)? *160(M)?.* 2 1 0 0 -NVIDIA GTS 240 .*NVIDIA .*GTS *24.* 3 1 1 3.3 +NVIDIA GTS 240 .*NVIDIA .*GTS *24.* 4 1 1 3.3 NVIDIA GTS 250 .*NVIDIA .*GTS *25.* 4 1 1 3.3 NVIDIA GTS 350M .*NVIDIA .*GTS *350M.* 4 1 1 3.3 NVIDIA GTS 360M .*NVIDIA .*GTS *360M.* 5 1 1 3.3 @@ -461,11 +479,11 @@ NVIDIA GeForce Go 6100 .*NVIDIA .*GeForce Go 61.* 0 1 1 2.1 NVIDIA GeForce Go 6200 .*NVIDIA .*GeForce Go 62.* 0 1 0 0 NVIDIA GeForce Go 6400 .*NVIDIA .*GeForce Go 64.* 1 1 1 2 NVIDIA GeForce Go 6500 .*NVIDIA .*GeForce Go 65.* 1 1 0 0 -NVIDIA GeForce Go 6600 .*NVIDIA .*GeForce Go 66.* 1 1 1 2.1 +NVIDIA GeForce Go 6600 .*NVIDIA .*GeForce Go 66.* 0 1 1 2.1 NVIDIA GeForce Go 6700 .*NVIDIA .*GeForce Go 67.* 1 1 0 0 NVIDIA GeForce Go 6800 .*NVIDIA .*GeForce Go 68.* 0 1 1 2.1 NVIDIA GeForce Go 7200 .*NVIDIA .*GeForce Go 72.* 1 1 0 0 -NVIDIA GeForce Go 7300 LE .*NVIDIA .*GeForce Go 73.*LE.* 0 1 0 0 +NVIDIA GeForce Go 7300 LE .*NVIDIA .*GeForce Go 73.*LE.* 1 1 0 0 NVIDIA GeForce Go 7300 .*NVIDIA .*GeForce Go 73.* 1 1 1 2.1 NVIDIA GeForce Go 7400 .*NVIDIA .*GeForce Go 74.* 1 1 1 2.1 NVIDIA GeForce Go 7600 .*NVIDIA .*GeForce Go 76.* 1 1 1 2.1 @@ -529,7 +547,7 @@ NVIDIA Quadro FX 4500 .*Quadro.*FX *45.* 3 1 0 0 NVIDIA Quadro FX 880M .*Quadro.*FX *880M.* 3 1 0 0 NVIDIA Quadro FX 4800 .*NVIDIA .*Quadro *FX *4800.* 3 1 0 0 NVIDIA Quadro FX .*Quadro FX.* 1 1 0 0 -NVIDIA Quadro NVS 1xxM .*Quadro NVS *1.[05]M.* 2 1 1 2.1 +NVIDIA Quadro NVS 1xxM .*Quadro NVS *1.[05]M.* 0 1 1 2.1 NVIDIA Quadro NVS 300M .*NVIDIA .*NVS *300M.* 2 1 0 0 NVIDIA Quadro NVS 320M .*NVIDIA .*NVS *320M.* 2 1 0 0 NVIDIA Quadro NVS 2100M .*NVIDIA .*NVS *2100M.* 2 1 0 0 -- cgit v1.2.3 From fd906e603e034fab3b0f507e0769eac14f434181 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Wed, 29 Aug 2012 16:44:29 -0500 Subject: MAINT-1491 Add Intel HD Graphics entries. --- indra/newview/gpu_table.txt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/indra/newview/gpu_table.txt b/indra/newview/gpu_table.txt index 587d237961..46343ad130 100644 --- a/indra/newview/gpu_table.txt +++ b/indra/newview/gpu_table.txt @@ -286,6 +286,11 @@ Intel Brookdale .*Intel.*Brookdale.* 0 0 1 1.3 Intel Cantiga .*Intel.*Cantiga.* 0 0 1 2 Intel Eaglelake .*Intel.*Eaglelake.* 1 1 1 2 Intel Graphics Media HD .*Intel.*Graphics Media.*HD.* 1 1 1 2.1 +Intel HD Graphics 2000 .*Intel.*HD Graphics 2.* 2 1 0 0 +Intel HD Graphics 3000 .*Intel.*HD Graphics 3.* 3 1 1 3.1 +Intel HD Graphics 4000 .*Intel.*HD Graphics 4.* 3 1 1 3.3 +Intel HD2000 .*Intel.*HD2000.* 2 1 0 0 +Intel HD3000 .*Intel.*HD3000.* 3 1 1 3.1 Intel HD Graphics .*Intel.*HD Graphics.* 2 1 1 4 Intel Mobile 4 Series .*Intel.*Mobile.* 4 Series.* 0 1 1 2.1 Intel 4 Series Internal .*Intel.* 4 Series Internal.* 1 1 1 2.1 @@ -294,8 +299,6 @@ Intel Montara .*Intel.*Montara.* 0 0 1 1.3 Intel Pineview .*Intel.*Pineview.* 0 1 1 1.4 Intel Springdale .*Intel.*Springdale.* 0 0 1 1.3 Intel Grantsdale .*Intel.*Grantsdale.* 1 1 0 0 -Intel HD Graphics 2000 .*Intel.*HD2000.* 1 1 0 0 -Intel HD Graphics 3000 .*Intel.*HD3000.* 2 1 0 0 Intel Q45/Q43 .*Intel.*Q4.* 1 1 1 2.1 Intel B45/B43 .*Intel.*B4.* 1 1 1 2.1 Intel 3D-Analyze .*Intel.*3D-Analyze.* 2 1 0 0 @@ -494,7 +497,7 @@ NVIDIA D9M .*NVIDIA .*D9M.* 1 1 0 0 NVIDIA G94 .*NVIDIA .*G94.* 3 1 0 0 NVIDIA GeForce Go 6 .*GeForce Go 6.* 1 1 0 0 NVIDIA ION 2 .*NVIDIA .*ION 2.* 2 1 0 0 -NVIDIA ION .*NVIDIA Corporation.*ION.* 2 1 1 4.2 +NVIDIA ION .*NVIDIA Corporation.*ION.* 2 1 0 0 NVIDIA NB8M .*NVIDIA .*NB8M.* 1 1 0 0 NVIDIA NB8P .*NVIDIA .*NB8P.* 2 1 0 0 NVIDIA NB9E .*NVIDIA .*NB9E.* 3 1 0 0 @@ -569,3 +572,4 @@ Apple Software Renderer Apple.*Software Renderer.* 0 0 0 0 Humper Humper.* 0 1 1 2.1 PowerVR SGX545 .*PowerVR SGX.* 1 1 1 3 + -- cgit v1.2.3 From b12ade128ba2a7f1a10b283abcfa12bfb15f06d3 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Wed, 29 Aug 2012 20:49:47 -0500 Subject: MAINT-1491 Make GPU table more readable, clear out some cruft, and start reporting raw GL strings in viewer stats instead of GPU table labels to make future GPU table overhauls more effective. --- indra/newview/gpu_table.txt | 180 +++++++++++++++++++++------------------- indra/newview/llviewerstats.cpp | 2 +- 2 files changed, 95 insertions(+), 87 deletions(-) diff --git a/indra/newview/gpu_table.txt b/indra/newview/gpu_table.txt index 46343ad130..5e8189caa5 100644 --- a/indra/newview/gpu_table.txt +++ b/indra/newview/gpu_table.txt @@ -60,14 +60,21 @@ ATI ASUS EAH24xx .*ATI.*ASUS.*EAH24.* 2 1 0 0 ATI ASUS EAH26xx .*ATI.*ASUS.*EAH26.* 3 1 0 0 ATI ASUS EAH29xx .*ATI.*ASUS.*EAH29.* 3 1 0 0 ATI ASUS EAH34xx .*ATI.*ASUS.*EAH34.* 1 1 0 0 -ATI ASUS EAH36xx .*ATI.*ASUS.*EAH36.* 3 1 0 0 +ATI ASUS EAH36xx .*ATI.*ASUS.*EAH36.* 2 1 0 0 ATI ASUS EAH38xx .*ATI.*ASUS.*EAH38.* 2 1 1 3.3 ATI ASUS EAH43xx .*ATI.*ASUS.*EAH43.* 2 1 1 3.3 -ATI ASUS EAH45xx .*ATI.*ASUS.*EAH45.* 1 1 0 0 +ATI ASUS EAH45xx .*ATI.*ASUS.*EAH45.* 2 1 0 0 ATI ASUS EAH48xx .*ATI.*ASUS.*EAH48.* 3 1 1 3.3 ATI ASUS EAH57xx .*ATI.*ASUS.*EAH57.* 3 1 1 4.1 ATI ASUS EAH58xx .*ATI.*ASUS.*EAH58.* 5 1 1 4.1 -ATI ASUS EAH6xxx .*ATI.*ASUS.*EAH6.* 3 1 1 4.1 +ATI ASUS EAH62xx .*ATI.*ASUS.*EAH62.* 2 1 0 0 +ATI ASUS EAH63xx .*ATI.*ASUS.*EAH63.* 2 1 0 0 +ATI ASUS EAH64xx .*ATI.*ASUS.*EAH64.* 2 1 0 0 +ATI ASUS EAH65xx .*ATI.*ASUS.*EAH65.* 2 1 0 0 +ATI ASUS EAH66xx .*ATI.*ASUS.*EAH66.* 3 1 0 0 +ATI ASUS EAH67xx .*ATI.*ASUS.*EAH67.* 3 1 0 0 +ATI ASUS EAH68xx .*ATI.*ASUS.*EAH68.* 5 1 0 0 +ATI ASUS EAH69xx .*ATI.*ASUS.*EAH69.* 5 1 0 0 ATI ASUS Radeon X1xxx .*ATI.*ASUS.*X1.* 2 1 1 2.1 ATI Radeon X7xx .*ATI.*ASUS.*X7.* 1 1 0 0 ATI Radeon X19xx .*ATI.*(Radeon|Diamond) X19.* ?.* 2 1 1 2.1 @@ -144,10 +151,10 @@ ATI Mobility Radeon HD 6200 .*ATI.*Mobility.*HD *62.* 3 1 0 0 ATI Mobility Radeon HD 6300 .*ATI.*Mobility.*HD *63.* 3 1 1 4.2 ATI Mobility Radeon HD 6400M .*ATI.*Mobility.*HD *64.* 3 1 0 0 ATI Mobility Radeon HD 6500M .*ATI.*Mobility.*HD *65.* 5 1 1 4.2 -ATI Mobility Radeon HD 6600M .*ATI.*Mobility.*HD *66.* 3 1 0 0 -ATI Mobility Radeon HD 6700M .*ATI.*Mobility.*HD *67.* 3 1 0 0 -ATI Mobility Radeon HD 6800M .*ATI.*Mobility.*HD *68.* 3 1 0 0 -ATI Mobility Radeon HD 6900M .*ATI.*Mobility.*HD *69.* 3 1 0 0 +ATI Mobility Radeon HD 6600M .*ATI.*Mobility.*HD *66.* 5 1 0 0 +ATI Mobility Radeon HD 6700M .*ATI.*Mobility.*HD *67.* 5 1 0 0 +ATI Mobility Radeon HD 6800M .*ATI.*Mobility.*HD *68.* 5 1 0 0 +ATI Mobility Radeon HD 6900M .*ATI.*Mobility.*HD *69.* 5 1 0 0 ATI Radeon HD 2300 .*ATI.*Radeon HD *23.. 0 1 1 3.3 ATI Radeon HD 2400 .*ATI.*Radeon HD *24.. 1 1 1 4 ATI Radeon HD 2600 .*ATI.*Radeon HD *26.. 2 1 1 3.3 @@ -257,7 +264,7 @@ ATI 760G/Radeon 3000 .*ATI.*AMD 760G.* 1 1 1 3.3 ATI 780L/Radeon 3000 .*ATI.*AMD 780L.* 1 1 0 0 ATI Radeon DDR .*ATI.*Radeon ?DDR.* 0 1 0 0 ATI FirePro 2000 .*ATI.*FirePro 2.* 2 1 1 4.1 -ATI FirePro 3000 .*ATI.*FirePro V3.* 1 1 0 0 +ATI FirePro 3000 .*ATI.*FirePro V3.* 2 1 0 0 ATI FirePro 4000 .*ATI.*FirePro V4.* 2 1 0 0 ATI FirePro 5000 .*ATI.*FirePro V5.* 3 1 0 0 ATI FirePro 7000 .*ATI.*FirePro V7.* 3 1 0 0 @@ -290,7 +297,7 @@ Intel HD Graphics 2000 .*Intel.*HD Graphics 2.* 2 1 0 0 Intel HD Graphics 3000 .*Intel.*HD Graphics 3.* 3 1 1 3.1 Intel HD Graphics 4000 .*Intel.*HD Graphics 4.* 3 1 1 3.3 Intel HD2000 .*Intel.*HD2000.* 2 1 0 0 -Intel HD3000 .*Intel.*HD3000.* 3 1 1 3.1 +Intel HD3000 .*Intel.*HD3000.* 3 1 0 0 Intel HD Graphics .*Intel.*HD Graphics.* 2 1 1 4 Intel Mobile 4 Series .*Intel.*Mobile.* 4 Series.* 0 1 1 2.1 Intel 4 Series Internal .*Intel.* 4 Series Internal.* 1 1 1 2.1 @@ -305,59 +312,54 @@ Intel 3D-Analyze .*Intel.*3D-Analyze.* 2 1 0 0 Matrox .*Matrox.* 0 0 0 0 Mesa .*Mesa.* 1 0 1 2.1 Gallium .*Gallium.* 1 1 1 2.1 -NVIDIA 205 .*NVIDIA .*GeForce 205.* 2 1 1 3.3 -NVIDIA 210 .*NVIDIA .*GeForce 210.* 3 1 1 3.3 -NVIDIA 310M .*NVIDIA .*GeForce 310M.* 3 1 1 3.3 -NVIDIA 310 .*NVIDIA .*GeForce 310.* 3 1 1 3.3 -NVIDIA 315M .*NVIDIA .*GeForce 315M.* 3 1 1 3.3 -NVIDIA 315 .*NVIDIA .*GeForce 315.* 3 1 1 3.3 -NVIDIA 320M .*NVIDIA .*GeForce 320M.* 3 1 1 3.3 NVIDIA G100M .*NVIDIA .*100M.* 4 1 1 3.3 -NVIDIA G100 .*NVIDIA .*100.* 3 1 1 4.2 NVIDIA G102M .*NVIDIA .*102M.* 1 1 1 3.3 NVIDIA G103M .*NVIDIA .*103M.* 2 1 1 3.3 NVIDIA G105M .*NVIDIA .*105M.* 2 1 1 3.3 NVIDIA G 110M .*NVIDIA .*110M.* 1 1 1 3.3 NVIDIA G 120M .*NVIDIA .*120M.* 1 1 1 3.3 -NVIDIA G 200 .*NVIDIA .*200(M)?.* 1 1 1 4.2 -NVIDIA G 205M .*NVIDIA .*205(M)?.* 0 1 0 0 -NVIDIA G 210 .*NVIDIA .*210(M)?.* 2 1 1 3.3 -NVIDIA 305M .*NVIDIA .*305(M)?.* 1 1 0 0 -NVIDIA G 310M .*NVIDIA .*310(M)?.* 2 1 0 0 -NVIDIA G 315 .*NVIDIA .*315(M)?.* 2 1 0 0 -NVIDIA G 320M .*NVIDIA .*320(M)?.* 3 1 1 3.3 -NVIDIA G 405 .*NVIDIA .*405(M)?.* 3 1 1 3.3 -NVIDIA G 410M .*NVIDIA .*410(M)?.* 3 1 1 4.2 -NVIDIA GT 120M .*NVIDIA .*GT *120(M)?.* 3 1 1 3.3 -NVIDIA GT 120 .*NVIDIA .*GT.*120 2 1 0 0 -NVIDIA GT 130M .*NVIDIA .*GT *130(M)?.* 3 1 1 3.3 -NVIDIA GT 140M .*NVIDIA .*GT *140(M)?.* 3 1 1 3.3 -NVIDIA GT 150M .*NVIDIA .*GT(S)? *150(M)?.* 2 1 0 0 -NVIDIA GT 160M .*NVIDIA .*GT *160(M)?.* 2 1 0 0 -NVIDIA GT 220M .*NVIDIA .*GT *220(M)?.* 3 1 1 3.3 -NVIDIA GT 230M .*NVIDIA .*GT *230(M)?.* 3 1 1 3.3 -NVIDIA GT 240M .*NVIDIA .*GT *240(M)?.* 3 1 1 3.3 -NVIDIA GT 250M .*NVIDIA .*GT *250(M)?.* 2 1 0 0 -NVIDIA GT 260M .*NVIDIA .*GT *260(M)?.* 2 1 0 0 -NVIDIA GT 320M .*NVIDIA .*GT *320(M)?.* 2 1 0 0 -NVIDIA GT 325M .*NVIDIA .*GT *325(M)?.* 3 1 1 3.3 -NVIDIA GT 330M .*NVIDIA .*GT *330(M)?.* 3 1 1 3.3 -NVIDIA GT 335M .*NVIDIA .*GT *335(M)?.* 3 1 1 3.3 -NVIDIA GT 340M .*NVIDIA .*GT *340(M)?.* 4 1 1 3.3 -NVIDIA GT 415M .*NVIDIA .*GT *415(M)?.* 3 1 1 4.2 -NVIDIA GT 420M .*NVIDIA .*GT *420(M)?.* 3 1 1 4.2 -NVIDIA GT 425M .*NVIDIA .*GT *425(M)?.* 4 1 1 4.2 -NVIDIA GT 430M .*NVIDIA .*GT *430(M)?.* 3 1 1 4.2 -NVIDIA GT 435M .*NVIDIA .*GT *435(M)?.* 4 1 1 4.2 -NVIDIA GT 440M .*NVIDIA .*GT *440(M)?.* 3 1 1 4.2 -NVIDIA GT 445M .*NVIDIA .*GT *445(M)?.* 3 1 1 4.2 -NVIDIA GT 450M .*NVIDIA .*GT *450(M)?.* 3 1 0 0 -NVIDIA GT 520M .*NVIDIA .*GT *52.(M)?.* 3 1 1 4.2 -NVIDIA GT 530M .*NVIDIA .*GT *530(M)?.* 3 1 1 4.2 -NVIDIA GT 540M .*NVIDIA .*GT *54.(M)?.* 3 1 1 4.2 -NVIDIA GT 550M .*NVIDIA .*GT *550(M)?.* 3 1 1 4.2 -NVIDIA GT 555M .*NVIDIA .*GT *555(M)?.* 3 1 1 4.2 -NVIDIA 610M .*NVIDIA.*61*M.* 3 1 1 4.2 +NVIDIA G 205M .*NVIDIA .*205M.* 1 1 0 0 +NVIDIA G 410M .*NVIDIA .*410M.* 3 1 1 4.2 +NVIDIA GT 120M .*NVIDIA .*GT *12*M.* 3 1 1 3.3 +NVIDIA GT 130M .*NVIDIA .*GT *13*M.* 3 1 1 3.3 +NVIDIA GT 140M .*NVIDIA .*GT *14*M.* 3 1 1 3.3 +NVIDIA GT 150M .*NVIDIA .*GTS *15*M.* 2 1 0 0 +NVIDIA GTS 160M .*NVIDIA .*GTS *16*M.* 2 1 0 0 +NVIDIA G210M .*NVIDIA .*G21*M.* 3 1 0 0 +NVIDIA GT 220M .*NVIDIA .*GT *22*M.* 3 1 1 3.3 +NVIDIA GT 230M .*NVIDIA .*GT *23*M.* 3 1 1 3.3 +NVIDIA GT 240M .*NVIDIA .*GT *24*M.* 3 1 1 3.3 +NVIDIA GTS 250M .*NVIDIA .*GTS *25*M.* 3 1 0 0 +NVIDIA GTS 260M .*NVIDIA .*GTS *26*M.* 3 1 0 0 +NVIDIA GTX 260M .*NVIDIA .*GTX *26*M.* 3 1 0 0 +NVIDIA GTX 270M .*NVIDIA .*GTX *27*M.* 3 1 0 0 +NVIDIA GTX 280M .*NVIDIA .*GTX *28*M.* 3 1 0 0 +NVIDIA 300M .*NVIDIA .*30*M.* 3 1 1 4.2 +NVIDIA G 310M .*NVIDIA .*31*M.* 2 1 0 0 +NVIDIA GT 320M .*NVIDIA .*GT *32*M.* 3 1 0 0 +NVIDIA GT 325M .*NVIDIA .*GT *32*M.* 3 1 1 3.3 +NVIDIA GT 330M .*NVIDIA .*GT *33*M.* 3 1 1 3.3 +NVIDIA GT 340M .*NVIDIA .*GT *34*M.* 4 1 1 3.3 +NVIDIA GTS 350M .*NVIDIA .*GTS *35*M.* 4 1 1 3.3 +NVIDIA GTS 360M .*NVIDIA .*GTS *36*M.* 5 1 1 3.3 +NVIDIA 405M .*NVIDIA .* 40*M.* 2 1 0 0 +NVIDIA 410M .*NVIDIA .* 41*M.* 3 1 0 0 +NVIDIA GT 415M .*NVIDIA .*GT *41*M.* 3 1 1 4.2 +NVIDIA GT 420M .*NVIDIA .*GT *42*M.* 3 1 1 4.2 +NVIDIA GT 430M .*NVIDIA .*GT *43*M.* 3 1 1 4.2 +NVIDIA GT 440M .*NVIDIA .*GT *44*M.* 3 1 1 4.2 +NVIDIA GT 450M .*NVIDIA .*GT *45*M.* 3 1 0 0 +NVIDIA GTX 460M .*NVIDIA .*GTX *46*M.* 4 1 1 4.2 +NVIDIA GTX 470M .*NVIDIA .*GTX *47*M.* 3 1 0 0 +NVIDIA GTX 480M .*NVIDIA .*GTX *48*M.* 3 1 1 4.2 +NVIDIA GT 520M .*NVIDIA .*GT *52*M.* 3 1 1 4.2 +NVIDIA GT 530M .*NVIDIA .*GT *53*M.* 3 1 1 4.2 +NVIDIA GT 540M .*NVIDIA .*GT *54*M.* 3 1 1 4.2 +NVIDIA GT 550M .*NVIDIA .*GT *55*M.* 3 1 1 4.2 +NVIDIA GTX 560M .*NVIDIA .*GTX *56*M.* 3 1 0 0 +NVIDIA GTX 570M .*NVIDIA .*GTX *57*M.* 5 1 0 0 +NVIDIA GTX 580M .*NVIDIA .*GTX *58*M.* 5 1 1 4.2 +NVIDIA 610M .*NVIDIA.* 61*M.* 3 1 1 4.2 NVIDIA GT 620M .*NVIDIA .*GT *62*M.* 3 1 0 0 NVIDIA GT 630M .*NVIDIA .*GT *63*M.* 3 1 0 0 NVIDIA GT 640M .*NVIDIA .*GT *64*M.* 3 1 0 0 @@ -366,44 +368,50 @@ NVIDIA GTX 660M .*NVIDIA .*GTX *66*M.* 5 1 0 0 NVIDIA GTX 670M .*NVIDIA .*GTX *67*M.* 5 1 1 4.2 NVIDIA GTX 680M .*NVIDIA .*GTX *68*M.* 5 1 0 0 NVIDIA GTX 690M .*NVIDIA .*GTX *69*M.* 5 1 0 0 -NVIDIA GT 610 .*NVIDIA .*GT *61.* 3 1 1 4.2 -NVIDIA GT 620 .*NVIDIA .*GT *62.* 3 1 0 0 -NVIDIA GT 630 .*NVIDIA .*GT *63.* 3 1 1 4.2 -NVIDIA GT 640 .*NVIDIA .*GT *64.* 3 1 0 0 -NVIDIA GT 650 .*NVIDIA .*GT *65.* 3 1 1 4.2 -NVIDIA GTX 660 .*NVIDIA .*GTX *66.* 5 1 0 0 -NVIDIA GTX 670 .*NVIDIA .*GTX *67.* 5 1 1 4.2 -NVIDIA GTX 680 .*NVIDIA .*GTX *68.* 5 1 1 4.2 -NVIDIA GTX 690 .*NVIDIA .*GTX *69.* 5 1 1 4.2 -NVIDIA GTS 160M .*NVIDIA .*GT(S)? *160(M)?.* 2 1 0 0 +NVIDIA G100 .*NVIDIA .*G10.* 3 1 1 4.2 +NVIDIA GT 120 .*NVIDIA .*GT *12.* 2 1 0 0 +NVIDIA GT 130 .*NVIDIA .*GT *13.* 2 1 0 0 +NVIDIA GTS 150 .*NVIDIA .*GTS *15.* 2 1 0 0 +NVIDIA 205 .*NVIDIA .*GeForce 205.* 2 1 1 3.3 +NVIDIA 210 .*NVIDIA .*GeForce 210.* 3 1 1 3.3 +NVIDIA GT 220 .*NVIDIA .*GT *22.* 2 1 1 3.3 NVIDIA GTS 240 .*NVIDIA .*GTS *24.* 4 1 1 3.3 NVIDIA GTS 250 .*NVIDIA .*GTS *25.* 4 1 1 3.3 -NVIDIA GTS 350M .*NVIDIA .*GTS *350M.* 4 1 1 3.3 -NVIDIA GTS 360M .*NVIDIA .*GTS *360M.* 5 1 1 3.3 -NVIDIA GTS 360 .*NVIDIA .*GTS *360.* 3 1 0 0 -NVIDIA GTS 450 .*NVIDIA .*GTS *45.* 4 1 1 4.2 NVIDIA GTX 260 .*NVIDIA .*GTX *26.* 4 1 1 3.3 -NVIDIA GTX 275 .*NVIDIA .*GTX *275.* 4 1 1 3.3 -NVIDIA GTX 270 .*NVIDIA .*GTX *27.* 3 1 0 0 -NVIDIA GTX 285 .*NVIDIA .*GTX *285.* 5 1 1 3.3 -NVIDIA GTX 280 .*NVIDIA .*GTX *280.* 4 1 1 3.3 -NVIDIA GTX 290 .*NVIDIA .*GTX *290.* 3 1 0 0 -NVIDIA GTX 295 .*NVIDIA .*GTX *295.* 5 1 1 3.3 -NVIDIA GTX 460M .*NVIDIA .*GTX *460M.* 4 1 1 4.2 -NVIDIA GTX 465 .*NVIDIA .*GTX *465.* 5 1 1 4.2 +NVIDIA GTX 270 .*NVIDIA .*GTX *27.* 4 1 0 0 +NVIDIA GTX 280 .*NVIDIA .*GTX *28.* 4 1 1 3.3 +NVIDIA GTX 290 .*NVIDIA .*GTX *29.* 5 1 0 0 +NVIDIA 310 .*NVIDIA .*GeForce 310.* 3 1 1 3.3 +NVIDIA 315 .*NVIDIA .*GeForce 315.* 3 1 1 3.3 +NVIDIA GT 320 .*NVIDIA .*GT *32.* 3 1 0 0 +NVIDIA GT 330 .*NVIDIA .*GT *33.* 3 1 0 0 +NVIDIA GT 340 .*NVIDIA .*GT *34.* 3 1 0 0 +NVIDIA 405 .*NVIDIA .* 405.* 3 1 0 0 +NVIDIA GT 420 .*NVIDIA .*GT *42.* 3 1 1 4.2 +NVIDIA GT 430 .*NVIDIA .*GT *43.* 3 1 1 4.1 +NVIDIA GT 440 .*NVIDIA .*GT *44.* 4 1 0 0 +NVIDIA GTS 450 .*NVIDIA .*GTS *45.* 4 1 1 4.2 NVIDIA GTX 460 .*NVIDIA .*GTX *46.* 5 1 1 4.2 -NVIDIA GTX 470M .*NVIDIA .*GTX *470M.* 3 1 0 0 NVIDIA GTX 470 .*NVIDIA .*GTX *47.* 5 1 1 4.2 -NVIDIA GTX 480M .*NVIDIA .*GTX *480M.* 3 1 1 4.2 -NVIDIA GTX 485M .*NVIDIA .*GTX *485M.* 3 1 0 0 NVIDIA GTX 480 .*NVIDIA .*GTX *48.* 5 1 1 4.2 -NVIDIA GTX 530 .*NVIDIA .*GTX *53.* 3 1 0 0 +NVIDIA 510 .*NVIDIA .* 510.* 3 1 0 0 +NVIDIA GT 520 .*NVIDIA .*GT *52.* 3 1 1 4.2 +NVIDIA GT 530 .*NVIDIA .*GT *53.* 3 1 1 4.2 +NVIDIA GT 540 .*NVIDIA .*GT *54.* 3 1 1 4.2 NVIDIA GTX 550 .*NVIDIA .*GTX *55.* 5 1 1 4.2 NVIDIA GTX 560 .*NVIDIA .*GTX *56.* 5 1 1 4.2 NVIDIA GTX 570 .*NVIDIA .*GTX *57.* 5 1 1 4.2 -NVIDIA GTX 580M .*NVIDIA .*GTX *580M.* 5 1 1 4.2 NVIDIA GTX 580 .*NVIDIA .*GTX *58.* 5 1 1 4.2 NVIDIA GTX 590 .*NVIDIA .*GTX *59.* 5 1 1 4.2 +NVIDIA GT 610 .*NVIDIA .*GT *61.* 3 1 1 4.2 +NVIDIA GT 620 .*NVIDIA .*GT *62.* 3 1 0 0 +NVIDIA GT 630 .*NVIDIA .*GT *63.* 3 1 0 0 +NVIDIA GT 640 .*NVIDIA .*GT *64.* 3 1 0 0 +NVIDIA GT 650 .*NVIDIA .*GT *65.* 3 1 1 4.2 +NVIDIA GTX 660 .*NVIDIA .*GTX *66.* 5 1 0 0 +NVIDIA GTX 670 .*NVIDIA .*GTX *67.* 5 1 1 4.2 +NVIDIA GTX 680 .*NVIDIA .*GTX *68.* 5 1 1 4.2 +NVIDIA GTX 690 .*NVIDIA .*GTX *69.* 5 1 1 4.2 NVIDIA C51 .*NVIDIA .*C51.* 0 1 1 2 NVIDIA G72 .*NVIDIA .*G72.* 1 1 0 0 NVIDIA G73 .*NVIDIA .*G73.* 1 1 0 0 @@ -412,7 +420,7 @@ NVIDIA G86 .*NVIDIA .*G86.* 3 1 0 0 NVIDIA G92 .*NVIDIA .*G92.* 3 1 0 0 NVIDIA GeForce .*GeForce 256.* 0 0 0 0 NVIDIA GeForce 2 .*GeForce ?2 ?.* 0 1 1 1.5 -NVIDIA GeForce 3 .*GeForce ?3 ?.* 0 1 1 1.5 +NVIDIA GeForce 3 .*GeForce ?3 ?.* 2 1 1 2.1 NVIDIA GeForce 3 Ti .*GeForce ?3 Ti.* 0 1 0 0 NVIDIA GeForce 4 .*NVIDIA .*GeForce ?4.* 0 1 1 1.5 NVIDIA GeForce 4 Go .*NVIDIA .*GeForce ?4.*Go.* 0 1 0 0 @@ -420,7 +428,7 @@ NVIDIA GeForce 4 MX .*NVIDIA .*GeForce ?4 MX.* 0 1 0 0 NVIDIA GeForce 4 PCX .*NVIDIA .*GeForce ?4 PCX.* 0 1 0 0 NVIDIA GeForce 4 Ti .*NVIDIA .*GeForce ?4 Ti.* 0 1 0 0 NVIDIA GeForce 6100 .*NVIDIA .*GeForce 61.* 3 1 1 4.2 -NVIDIA GeForce 6200 .*NVIDIA .*GeForce 62.* 0 1 0 0 +NVIDIA GeForce 6200 .*NVIDIA .*GeForce 62.* 0 1 1 2.1 NVIDIA GeForce 6500 .*NVIDIA .*GeForce 65.* 1 1 1 2.1 NVIDIA GeForce 6600 .*NVIDIA .*GeForce 66.* 2 1 1 2.1 NVIDIA GeForce 6700 .*NVIDIA .*GeForce 67.* 2 1 1 2.1 @@ -497,7 +505,7 @@ NVIDIA D9M .*NVIDIA .*D9M.* 1 1 0 0 NVIDIA G94 .*NVIDIA .*G94.* 3 1 0 0 NVIDIA GeForce Go 6 .*GeForce Go 6.* 1 1 0 0 NVIDIA ION 2 .*NVIDIA .*ION 2.* 2 1 0 0 -NVIDIA ION .*NVIDIA Corporation.*ION.* 2 1 0 0 +NVIDIA ION .*NVIDIA Corporation.*ION.* 2 1 1 0 NVIDIA NB8M .*NVIDIA .*NB8M.* 1 1 0 0 NVIDIA NB8P .*NVIDIA .*NB8P.* 2 1 0 0 NVIDIA NB9E .*NVIDIA .*NB9E.* 3 1 0 0 diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index dfd4981946..4493343906 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -783,7 +783,7 @@ void send_stats() "%-6s Class %d ", gGLManager.mGLVendorShort.substr(0,6).c_str(), (S32)LLFeatureManager::getInstance()->getGPUClass()) - + LLFeatureManager::getInstance()->getGPUString(); + + gGLManager.getRawGLString(); system["gpu"] = gpu_desc; system["gpu_class"] = (S32)LLFeatureManager::getInstance()->getGPUClass(); -- cgit v1.2.3 From 7db0cb7583b829be3b4884b69a31af1bbdfaadfb Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 31 Aug 2012 09:55:52 -0400 Subject: Fix longstanding LLURI::buildHTTP() bug when passing string path. The LLURI::buildHTTP() overloads that take an LLSD 'path' accept 'undefined', LLSD::String and (LLSD::Array of LLSD::String). A sequence of path components passed in an Array is constructed into a slash-separated path. There are unit tests in lluri_test.cpp to exercise that case. To my amazement, there were NO unit tests covering the case of an LLSD::String path. The code for that case escaped and appended the entire passed string. While that might be fine for a 'path' consisting of a single undecorated path component, the available documentation does not forbid one from passing a path containing slashes as well. But this had the dubious effect of replacing every slash with %2F. In particular, decomposing a URL string with one LLURI instance and constructing another like it using LLURI::buildHTTP() was not symmetrical. Having consulted with Richard, I made the string-path logic a bit more nuanced: - The passed path string is split on slashes. Every path component is individually escaped, then recombined with slashes into the final path. - Duplicate slashes are eliminated. - The presence or absence of a trailing slash in the original path string is carefully respected. Now that we've nailed down how it ought to behave -- added unit tests to ensure that it DOES behave that way!! --- indra/llcommon/lluri.cpp | 36 ++++++++++++-- indra/llcommon/tests/lluri_test.cpp | 94 +++++++++++++++++++++++-------------- 2 files changed, 91 insertions(+), 39 deletions(-) diff --git a/indra/llcommon/lluri.cpp b/indra/llcommon/lluri.cpp index b39ea0c6f2..21456a599b 100644 --- a/indra/llcommon/lluri.cpp +++ b/indra/llcommon/lluri.cpp @@ -37,6 +37,8 @@ // system includes #include +#include +#include void encode_character(std::ostream& ostr, std::string::value_type val) { @@ -317,7 +319,7 @@ LLURI LLURI::buildHTTP(const std::string& prefix, const LLSD& path) { LLURI result; - + // TODO: deal with '/' '?' '#' in host_port if (prefix.find("://") != prefix.npos) { @@ -342,15 +344,41 @@ LLURI LLURI::buildHTTP(const std::string& prefix, result.mEscapedPath += "/" + escapePathComponent(it->asString()); } } - else if(path.isString()) + else if (path.isString()) { - result.mEscapedPath += "/" + escapePathComponent(path.asString()); + std::string pathstr(path); + // Trailing slash is significant in HTTP land. If caller specified, + // make a point of preserving. + std::string last_slash; + std::string::size_type len(pathstr.length()); + if (len && pathstr[len-1] == '/') + { + last_slash = "/"; + } + + // Escape every individual path component, recombining with slashes. + for (boost::split_iterator + ti(pathstr, boost::first_finder("/")), tend; + ti != tend; ++ti) + { + // Eliminate a leading slash or duplicate slashes anywhere. (Extra + // slashes show up here as empty components.) This test also + // eliminates a trailing slash, hence last_slash above. + if (! ti->empty()) + { + result.mEscapedPath + += "/" + escapePathComponent(std::string(ti->begin(), ti->end())); + } + } + + // Reinstate trailing slash, if any. + result.mEscapedPath += last_slash; } else if(path.isUndefined()) { // do nothing } - else + else { llwarns << "Valid path arguments to buildHTTP are array, string, or undef, you passed type" << path.type() << llendl; diff --git a/indra/llcommon/tests/lluri_test.cpp b/indra/llcommon/tests/lluri_test.cpp index f6d4221256..4c64f15ca7 100644 --- a/indra/llcommon/tests/lluri_test.cpp +++ b/indra/llcommon/tests/lluri_test.cpp @@ -58,12 +58,12 @@ namespace tut ensure_equals("escape/unescape escaped", uri_esc_2, uri_esc_1); } }; - + typedef test_group URITestGroup; typedef URITestGroup::object URITestObject; URITestGroup uriTestGroup("LLURI"); - + template<> template<> void URITestObject::test<1>() { @@ -89,14 +89,14 @@ namespace tut template<> template<> void URITestObject::test<2>() { - // empty string + set_test_name("empty string"); checkParts(LLURI(""), "", "", "", ""); } - + template<> template<> void URITestObject::test<3>() { - // no scheme + set_test_name("no scheme"); checkParts(LLURI("foo"), "", "foo", "", ""); checkParts(LLURI("foo%3A"), "", "foo:", "", ""); } @@ -104,7 +104,7 @@ namespace tut template<> template<> void URITestObject::test<4>() { - // scheme w/o paths + set_test_name("scheme w/o paths"); checkParts(LLURI("mailto:zero@ll.com"), "mailto", "zero@ll.com", "", ""); checkParts(LLURI("silly://abc/def?foo"), @@ -114,16 +114,16 @@ namespace tut template<> template<> void URITestObject::test<5>() { - // authority section + set_test_name("authority section"); checkParts(LLURI("http:///"), "http", "///", "", "/"); - + checkParts(LLURI("http://abc"), "http", "//abc", "abc", ""); - + checkParts(LLURI("http://a%2Fb/cd"), "http", "//a/b/cd", "a/b", "/cd"); - + checkParts(LLURI("http://host?"), "http", "//host?", "host", ""); } @@ -131,13 +131,13 @@ namespace tut template<> template<> void URITestObject::test<6>() { - // path section + set_test_name("path section"); checkParts(LLURI("http://host/a/b/"), "http", "//host/a/b/", "host", "/a/b/"); - + checkParts(LLURI("http://host/a%3Fb/"), "http", "//host/a?b/", "host", "/a?b/"); - + checkParts(LLURI("http://host/a:b/"), "http", "//host/a:b/", "host", "/a:b/"); } @@ -145,16 +145,16 @@ namespace tut template<> template<> void URITestObject::test<7>() { - // query string + set_test_name("query string"); checkParts(LLURI("http://host/?"), "http", "//host/?", "host", "/", ""); - + checkParts(LLURI("http://host/?x"), "http", "//host/?x", "host", "/", "x"); - + checkParts(LLURI("http://host/??"), "http", "//host/??", "host", "/", "?"); - + checkParts(LLURI("http://host/?%3F"), "http", "//host/??", "host", "/", "?"); } @@ -167,19 +167,44 @@ namespace tut path.append("123"); checkParts(LLURI::buildHTTP("host", path), "http", "//host/x/123", "host", "/x/123"); - + LLSD query; query["123"] = "12"; query["abcd"] = "abc"; checkParts(LLURI::buildHTTP("host", path, query), "http", "//host/x/123?123=12&abcd=abc", "host", "/x/123", "123=12&abcd=abc"); + + ensure_equals(LLURI::buildHTTP("host", "").asString(), + "http://host"); + ensure_equals(LLURI::buildHTTP("host", "/").asString(), + "http://host/"); + ensure_equals(LLURI::buildHTTP("host", "//").asString(), + "http://host/"); + ensure_equals(LLURI::buildHTTP("host", "dir name").asString(), + "http://host/dir%20name"); + ensure_equals(LLURI::buildHTTP("host", "dir name/").asString(), + "http://host/dir%20name/"); + ensure_equals(LLURI::buildHTTP("host", "/dir name").asString(), + "http://host/dir%20name"); + ensure_equals(LLURI::buildHTTP("host", "/dir name/").asString(), + "http://host/dir%20name/"); + ensure_equals(LLURI::buildHTTP("host", "dir name/subdir name").asString(), + "http://host/dir%20name/subdir%20name"); + ensure_equals(LLURI::buildHTTP("host", "dir name/subdir name/").asString(), + "http://host/dir%20name/subdir%20name/"); + ensure_equals(LLURI::buildHTTP("host", "/dir name/subdir name").asString(), + "http://host/dir%20name/subdir%20name"); + ensure_equals(LLURI::buildHTTP("host", "/dir name/subdir name/").asString(), + "http://host/dir%20name/subdir%20name/"); + ensure_equals(LLURI::buildHTTP("host", "//dir name//subdir name//").asString(), + "http://host/dir%20name/subdir%20name/"); } template<> template<> void URITestObject::test<9>() { - // test unescaped path components + set_test_name("test unescaped path components"); LLSD path; path.append("x@*//*$&^"); path.append("123"); @@ -190,7 +215,7 @@ namespace tut template<> template<> void URITestObject::test<10>() { - // test unescaped query components + set_test_name("test unescaped query components"); LLSD path; path.append("x"); path.append("123"); @@ -205,7 +230,7 @@ namespace tut template<> template<> void URITestObject::test<11>() { - // test unescaped host components + set_test_name("test unescaped host components"); LLSD path; path.append("x"); path.append("123"); @@ -216,16 +241,16 @@ namespace tut "http", "//hi123*33--}{:portstuffs/x/123?123=12&abcd=abc", "hi123*33--}{:portstuffs", "/x/123", "123=12&abcd=abc"); } - + template<> template<> void URITestObject::test<12>() { - // test funky host_port values that are actually prefixes - + set_test_name("test funky host_port values that are actually prefixes"); + checkParts(LLURI::buildHTTP("http://example.com:8080", LLSD()), "http", "//example.com:8080", "example.com:8080", ""); - + checkParts(LLURI::buildHTTP("http://example.com:8080/", LLSD()), "http", "//example.com:8080/", "example.com:8080", "/"); @@ -242,7 +267,7 @@ namespace tut "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" "0123456789" "-._~"; - // test escape + set_test_name("test escape"); ensure_equals("escaping", LLURI::escape("abcdefg", "abcdef"), "abcdef%67"); ensure_equals("escaping", LLURI::escape("|/&\\+-_!@", ""), "%7C%2F%26%5C%2B%2D%5F%21%40"); ensure_equals("escaping as query variable", @@ -259,13 +284,12 @@ namespace tut cedilla.push_back( (char)0xA7 ); ensure_equals("escape UTF8", LLURI::escape( cedilla, unreserved), "%C3%A7"); } - + template<> template<> void URITestObject::test<14>() { - // make sure escape and unescape of empty strings return empty - // strings. + set_test_name("make sure escape and unescape of empty strings return empty strings."); std::string uri_esc(LLURI::escape("")); ensure("escape string empty", uri_esc.empty()); std::string uri_raw(LLURI::unescape("")); @@ -275,7 +299,7 @@ namespace tut template<> template<> void URITestObject::test<15>() { - // do some round-trip tests + set_test_name("do some round-trip tests"); escapeRoundTrip("http://secondlife.com"); escapeRoundTrip("http://secondlife.com/url with spaces"); escapeRoundTrip("http://bad[domain]name.com/"); @@ -286,7 +310,7 @@ namespace tut template<> template<> void URITestObject::test<16>() { - // Test the default escaping + set_test_name("Test the default escaping"); // yes -- this mangles the url. This is expected behavior std::string simple("http://secondlife.com"); ensure_equals( @@ -302,7 +326,7 @@ namespace tut template<> template<> void URITestObject::test<17>() { - // do some round-trip tests with very long strings. + set_test_name("do some round-trip tests with very long strings."); escapeRoundTrip("Welcome to Second Life.We hope you'll have a richly rewarding experience, filled with creativity, self expression and fun.The goals of the Community Standards are simple: treat each other with respect and without harassment, adhere to local standards as indicated by simulator ratings, and refrain from any hate activity which slurs a real-world individual or real-world community. Behavioral Guidelines - The Big Six"); escapeRoundTrip( "'asset_data':b(12100){'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444921\n\ttotal_crc\t323\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.368634403\t0.00781063363\t-0.569040775\n\toldpos\t150.117996\t25.8658009\t8.19664001\n\trotation\t-0.06293071806430816650390625\t-0.6995697021484375\t-0.7002241611480712890625\t0.1277817934751510620117188\n\tchildpos\t-0.00499999989\t-0.0359999985\t0.307999998\n\tchildrot\t-0.515492737293243408203125\t-0.46601200103759765625\t0.529055416584014892578125\t0.4870323240756988525390625\n\tscale" @@ -322,7 +346,7 @@ namespace tut "D STRING RW SV 20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\torig_asset_id\t8747acbc-d391-1e59-69f1-41d06830e6c0\n\torig_item_id\t20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tfrom_task_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tlinked\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n"); } - + template<> template<> void URITestObject::test<18>() { @@ -335,7 +359,7 @@ namespace tut ensure_equals("pathmap", u.pathArray()[1].asString(), "login"); ensure_equals("query", u.query(), "first_name=Testert4&last_name=Tester&web_login_key=test"); ensure_equals("query map element", u.queryMap()["last_name"].asString(), "Tester"); - + u = LLURI("secondlife://Da Boom/128/128/128"); // if secondlife is the scheme, LLURI should parse /128/128/128 as path, with Da Boom as authority ensure_equals("scheme", u.scheme(), "secondlife"); @@ -350,7 +374,7 @@ namespace tut template<> template<> void URITestObject::test<19>() { - // Parse about: schemes + set_test_name("Parse about: schemes"); LLURI u("about:blank?redirect-http-hack=secondlife%3A%2F%2F%2Fapp%2Flogin%3Ffirst_name%3DCallum%26last_name%3DLinden%26location%3Dspecify%26grid%3Dvaak%26region%3D%2FMorris%2F128%2F128%26web_login_key%3Defaa4795-c2aa-4c58-8966-763c27931e78"); ensure_equals("scheme", u.scheme(), "about"); ensure_equals("authority", u.authority(), ""); -- cgit v1.2.3 From 488362b0ac926c51fccf093276ca15d93369c96f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 31 Aug 2012 11:32:31 -0400 Subject: Now that LLURI isn't broken, use it to construct login-page URL. Previous logic constructed a std::ostringstream, directly messing with '?' vs. '&', ugly libcurl escape calls etc. Now we can deconstruct the LLGridManager:: getLoginPage() URL, supplement the params map as needed and then rebuild a new URL using LLURI::buildHTTP(). --- indra/newview/llpanellogin.cpp | 62 +++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 40 deletions(-) diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 3bb3e5cf47..d787b69b5e 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -744,63 +744,45 @@ void LLPanelLogin::setAlwaysRefresh(bool refresh) void LLPanelLogin::loadLoginPage() { if (!sInstance) return; - - std::ostringstream oStr; - std::string login_page = LLGridManager::getInstance()->getLoginPage(); + LLURI login_page = LLURI(LLGridManager::getInstance()->getLoginPage()); + LLSD params(login_page.queryMap()); - oStr << login_page; - - // Use the right delimeter depending on how LLURI parses the URL - LLURI login_page_uri = LLURI(login_page); - - std::string first_query_delimiter = "&"; - if (login_page_uri.queryMap().size() == 0) - { - first_query_delimiter = "?"; - } + LL_DEBUGS("AppInit") << "login_page: " << login_page << LL_ENDL; // Language - std::string language = LLUI::getLanguage(); - oStr << first_query_delimiter<<"lang=" << language; - + params["lang"] = LLUI::getLanguage(); + // First Login? if (gSavedSettings.getBOOL("FirstLoginThisInstall")) { - oStr << "&firstlogin=TRUE"; + params["firstlogin"] = "TRUE"; // not bool: server expects string TRUE } // Channel and Version - std::string version = llformat("%s (%d)", - LLVersionInfo::getShortVersion().c_str(), - LLVersionInfo::getBuild()); + params["version"] = llformat("%s (%d)", + LLVersionInfo::getShortVersion().c_str(), + LLVersionInfo::getBuild()); + params["channel"] = LLVersionInfo::getChannel(); - char* curl_channel = curl_escape(LLVersionInfo::getChannel().c_str(), 0); - char* curl_version = curl_escape(version.c_str(), 0); + // Grid + params["grid"] = LLGridManager::getInstance()->getGridId(); - oStr << "&channel=" << curl_channel; - oStr << "&version=" << curl_version; + // add OS info + params["os"] = LLAppViewer::instance()->getOSInfo().getOSStringSimple(); - curl_free(curl_channel); - curl_free(curl_version); + // Make an LLURI with this augmented info + LLURI login_uri(LLURI::buildHTTP(login_page.authority(), + login_page.path(), + params)); - // Grid - char* curl_grid = curl_escape(LLGridManager::getInstance()->getGridId().c_str(), 0); - oStr << "&grid=" << curl_grid; - curl_free(curl_grid); - - // add OS info - char * os_info = curl_escape(LLAppViewer::instance()->getOSInfo().getOSStringSimple().c_str(), 0); - oStr << "&os=" << os_info; - curl_free(os_info); - gViewerWindow->setMenuBackgroundColor(false, !LLGridManager::getInstance()->isInProductionGrid()); - + LLMediaCtrl* web_browser = sInstance->getChild("login_html"); - if (web_browser->getCurrentNavUrl() != oStr.str()) + if (web_browser->getCurrentNavUrl() != login_uri.asString()) { - LL_DEBUGS("AppInit")<navigateTo( oStr.str(), "text/html" ); + LL_DEBUGS("AppInit") << "loading: " << login_uri << LL_ENDL; + web_browser->navigateTo( login_uri.asString(), "text/html" ); } } -- cgit v1.2.3 From f346cbbdeb8502e1cb06a01a184dde7a7f53a087 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 31 Aug 2012 11:58:25 -0400 Subject: Copy sourceid= from create_account_url to login-page URL. This allows the login-page server to respond to any sourceid= associated with the create_account_url, which (we happen to know) varies by skin -- e.g. for the Steam viewer. --- indra/newview/llpanellogin.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index d787b69b5e..c6bcaeab07 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -771,6 +771,14 @@ void LLPanelLogin::loadLoginPage() // add OS info params["os"] = LLAppViewer::instance()->getOSInfo().getOSStringSimple(); + // sourceid: create_account_url's sourceid= varies by skin + LLURI create_account_url(LLTrans::getString("create_account_url")); + LLSD create_account_params(create_account_url.queryMap()); + if (create_account_params.has("sourceid")) + { + params["sourceid"] = create_account_params["sourceid"]; + } + // Make an LLURI with this augmented info LLURI login_uri(LLURI::buildHTTP(login_page.authority(), login_page.path(), -- cgit v1.2.3 From 84884b06f206790ab5a4f52c7125665f9d32b7d8 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 5 Sep 2012 17:07:39 -0400 Subject: Reapply f6ed57210865 to zh/notifications.xml: remove "FirstRun" link. Because create_account_url should vary between viewer languages and skins, we've made an effort to ensure that create_account_url is used everywhere, instead of directly embedding http://join.secondlife.com. Recent rev 6ee71714935f accidentally reinserted the link embedded in the "FirstRun" notification for language 'zh'. Delete it again. --- indra/newview/skins/default/xui/zh/notifications.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/indra/newview/skins/default/xui/zh/notifications.xml b/indra/newview/skins/default/xui/zh/notifications.xml index 97a1bd6c84..fe4f84dce7 100644 --- a/indra/newview/skins/default/xui/zh/notifications.xml +++ b/indra/newview/skins/default/xui/zh/notifications.xml @@ -1122,7 +1122,6 @@ [APP_NAME] 安裝完成。 如果你是第一次使用 [SECOND_LIFE],你將需要建立新帳號才可登入。 -返回 [http://join.secondlife.com secondlife.com] 建立新帳號? -- cgit v1.2.3 From 04eb771043ddc2a1aa34dc3d1c604615ac03e023 Mon Sep 17 00:00:00 2001 From: maxim_productengine Date: Thu, 6 Sep 2012 12:24:10 +0300 Subject: DRTVWR-232: MAINT-1440: FIXED Call Tools.TakeCopy instead of InspectObject.TakeFreeCopy --- indra/newview/skins/default/xui/en/menu_inspect_object_gear.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/skins/default/xui/en/menu_inspect_object_gear.xml b/indra/newview/skins/default/xui/en/menu_inspect_object_gear.xml index 63e154697b..2c420aa1e3 100644 --- a/indra/newview/skins/default/xui/en/menu_inspect_object_gear.xml +++ b/indra/newview/skins/default/xui/en/menu_inspect_object_gear.xml @@ -57,7 +57,7 @@ layout="topleft" name="take_copy"> + function="Tools.TakeCopy"/> -- cgit v1.2.3 From af6e665d5c9c9a5ea480389c284839f3945bf786 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Fri, 7 Sep 2012 11:22:45 -0500 Subject: MAINT-1491 Fix for L&S never being enabled by default on XP/Linux/OS X --- doc/contributions.txt | 3 +++ indra/newview/featuretable_linux.txt | 12 ------------ indra/newview/featuretable_mac.txt | 12 ------------ indra/newview/featuretable_xp.txt | 18 +++--------------- 4 files changed, 6 insertions(+), 39 deletions(-) diff --git a/doc/contributions.txt b/doc/contributions.txt index df504e4a8a..98c606c0be 100644 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -883,6 +883,8 @@ Nicholaz Beresford VWR-2682 VWR-2684 Nick Rhodes +Nicky Dasmijn + VWR-29228 Nicky Perian OPEN-1 STORM-1087 @@ -1112,6 +1114,7 @@ TankMaster Finesmith STORM-1602 STORM-1258 VWR-26622 + VWR-29224 Talamasca Tali Rosca Tayra Dagostino diff --git a/indra/newview/featuretable_linux.txt b/indra/newview/featuretable_linux.txt index 378e9650cf..5699bd9c8a 100644 --- a/indra/newview/featuretable_linux.txt +++ b/indra/newview/featuretable_linux.txt @@ -95,9 +95,6 @@ RenderVolumeLODFactor 1 0.5 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 0 WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 -RenderShadowDetail 1 0 RenderFSAASamples 1 0 // @@ -126,9 +123,6 @@ RenderVolumeLODFactor 1 0.5 VertexShaderEnable 1 0 WindLightUseAtmosShaders 1 0 WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 -RenderShadowDetail 1 0 RenderFSAASamples 1 0 // @@ -156,9 +150,6 @@ RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 0 WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 -RenderShadowDetail 1 0 RenderFSAASamples 1 0 // @@ -186,9 +177,6 @@ RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 1 WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 -RenderShadowDetail 1 0 RenderFSAASamples 1 2 // diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt index 0f12769b38..3a91f19c58 100644 --- a/indra/newview/featuretable_mac.txt +++ b/indra/newview/featuretable_mac.txt @@ -97,9 +97,6 @@ RenderVolumeLODFactor 1 0.5 VertexShaderEnable 1 0 WindLightUseAtmosShaders 1 0 WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 -RenderShadowDetail 1 0 RenderFSAASamples 1 0 // @@ -128,9 +125,6 @@ RenderVolumeLODFactor 1 0.5 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 0 WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 -RenderShadowDetail 1 0 RenderFSAASamples 1 0 // @@ -158,9 +152,6 @@ RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 0 WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 -RenderShadowDetail 1 0 RenderFSAASamples 1 0 // @@ -188,9 +179,6 @@ RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 1 WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 -RenderShadowDetail 1 2 RenderFSAASamples 1 2 // diff --git a/indra/newview/featuretable_xp.txt b/indra/newview/featuretable_xp.txt index 8c1dd80d07..ad16e2533b 100644 --- a/indra/newview/featuretable_xp.txt +++ b/indra/newview/featuretable_xp.txt @@ -63,9 +63,9 @@ Disregard96DefaultDrawDistance 1 1 RenderTextureMemoryMultiple 1 1.0 RenderCompressTextures 1 1 RenderShaderLightingMaxLevel 1 3 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 -RenderShadowDetail 1 0 +RenderDeferred 1 1 +RenderDeferredSSAO 1 1 +RenderShadowDetail 1 2 WatchdogDisabled 1 1 RenderUseStreamVBO 1 1 RenderFSAASamples 1 16 @@ -97,9 +97,6 @@ RenderVolumeLODFactor 1 0.5 VertexShaderEnable 1 0 WindLightUseAtmosShaders 1 0 WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 -RenderShadowDetail 1 0 RenderFSAASamples 1 0 // @@ -128,9 +125,6 @@ RenderVolumeLODFactor 1 0.5 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 0 WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 -RenderShadowDetail 1 0 RenderFSAASamples 1 0 // @@ -158,9 +152,6 @@ RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 0 WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 -RenderShadowDetail 1 0 RenderFSAASamples 1 0 // @@ -188,9 +179,6 @@ RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 1 WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 -RenderShadowDetail 1 2 RenderFSAASamples 1 2 // -- cgit v1.2.3 From 09cd2a4b1a9f1ddf046fb0ce5d12988b968269a3 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 7 Sep 2012 15:08:12 -0400 Subject: DRTVWR-209 Additional merge of viewer-development with SH-3316 drano-http code. Restore original deleteRequest/removeRequest implementation removing a small race. Remove a short-lived additional timeout scheme on requests which really isn't appropriate as originally implemented as we can have very long-lived requests on big regions. --- indra/newview/lltexturefetch.cpp | 61 ++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 43198d3725..8884c978b2 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1068,8 +1068,6 @@ void LLTextureFetchWorker::startWork(S32 param) // Threads: Ttf bool LLTextureFetchWorker::doWork(S32 param) { - static const F32 FETCHING_TIMEOUT = 120.f;//seconds - static const LLCore::HttpStatus http_not_found(HTTP_NOT_FOUND); // 404 static const LLCore::HttpStatus http_service_unavail(HTTP_SERVICE_UNAVAILABLE); // 503 static const LLCore::HttpStatus http_not_sat(HTTP_REQUESTED_RANGE_NOT_SATISFIABLE); // 416; @@ -1637,32 +1635,12 @@ bool LLTextureFetchWorker::doWork(S32 param) } else { - // *FIXME: This auxiliary timeout logic appeared recently and then - // quickly disappeared. While I haven't seen it invoked, I'm leaving - // it active for now. - if(FETCHING_TIMEOUT < mRequestedTimer.getElapsedTimeF32()) - { - //timeout, abort. - LL_WARNS("Texture") << "Fetch of texture " << mID << " timed out after " - << mRequestedTimer.getElapsedTimeF32() - << " seconds. Canceling request." << LL_ENDL; - - if (LLCORE_HTTP_HANDLE_INVALID != mHttpHandle) - { - // Issue cancel on any outstanding request. Asynchronous - // so cancel may not actually take effect if operation is - // complete & queued. Either way, notification will - // complete and the request can be transitioned. - mFetcher->mHttpRequest->requestCancel(mHttpHandle, NULL); - } - else - { - // Shouldn't happen but if it does, cancel quickly. - mState = DONE; - releaseHttpSemaphore(); - return true; - } - } + // *HISTORY: There was a texture timeout test here originally that + // would cancel a request that was over 120 seconds old. That's + // probably not a good idea. Particularly rich regions can take + // an enormous amount of time to load textures. We'll revisit the + // various possible timeout components (total request time, connection + // time, I/O time, with and without retries, etc.) in the future. setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; @@ -2571,19 +2549,36 @@ void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id, S32 received_size) mHTTPTextureBits += received_size * 8; // Approximate - does not include header bits } // -Mfnq +// NB: If you change deleteRequest() you should probably make +// parallel changes in removeRequest(). They're functionally +// identical with only argument variations. +// // Threads: T* void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) { lockQueue(); // +Mfq LLTextureFetchWorker* worker = getWorkerAfterLock(id); - unlockQueue(); // -Mfq + if (worker) + { + size_t erased_1 = mRequestMap.erase(worker->mID); + unlockQueue(); // -Mfq + + llassert_always(erased_1 > 0) ; + removeFromNetworkQueue(worker, cancel); + llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ; - // *TODO: Refactoring this code may have introduced a thread race - // here where other code can run between the lookup above and the - // removeRequest() below. - removeRequest(worker, cancel); + worker->scheduleDelete(); + } + else + { + unlockQueue(); // -Mfq + } } +// NB: If you change removeRequest() you should probably make +// parallel changes in deleteRequest(). They're functionally +// identical with only argument variations. +// // Threads: T* void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel) { -- cgit v1.2.3 From 81b9e29a1fe227c8f51c6a644b4e2e1afa6bcfb2 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 7 Sep 2012 18:55:04 -0400 Subject: DRTVWR-209 Merge of viewer-development with SH-3316 drano-http code. Cmake files not merged correctly and had to be done by hand. New memory allocation made some memory usage tests in the llcorehttp integration tests no longer valid. Would like to work on LLLog sometime and get it to be consistent. Special flags needed for windows build of example program. --- indra/cmake/00-Common.cmake | 5 +++++ indra/cmake/LLAddBuildTest.cmake | 9 +++++++++ indra/cmake/LLCommon.cmake | 2 +- indra/cmake/Variables.cmake | 17 ++++++++++++++++- indra/llcorehttp/CMakeLists.txt | 14 +++++++++++++- indra/llcorehttp/tests/test_httprequest.hpp | 21 +++++++++------------ 6 files changed, 53 insertions(+), 15 deletions(-) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 00baf626d2..21cb87237d 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -51,6 +51,7 @@ if (WINDOWS) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${LL_CXX_FLAGS} /O2 /Zi /MD /MP /Ob2 -D_SECURE_STL=0 -D_HAS_ITERATOR_DEBUGGING=0" CACHE STRING "C++ compiler release options" FORCE) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE") set(CMAKE_CXX_STANDARD_LIBRARIES "") set(CMAKE_C_STANDARD_LIBRARIES "") @@ -206,6 +207,10 @@ if (DARWIN) # NOTE: it's critical to have both CXX_FLAGS and C_FLAGS covered. set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O0 ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O0 ${CMAKE_C_FLAGS_RELWITHDEBINFO}") + if (XCODE_VERSION GREATER 4.2) + set(ENABLE_SIGNING TRUE) + set(SIGNING_IDENTITY "Developer ID Application: Linden Research, Inc.") + endif (XCODE_VERSION GREATER 4.2) endif (DARWIN) diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake index a6f69a09e9..543075db5b 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -201,6 +201,15 @@ FUNCTION(LL_ADD_INTEGRATION_TEST endif(TEST_DEBUG) ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files}) SET_TARGET_PROPERTIES(INTEGRATION_TEST_${testname} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}") + if (WINDOWS) + set_target_properties(INTEGRATION_TEST_${testname} + PROPERTIES + LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:WINDOWS /INCLUDE:__tcmalloc" + LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\" /INCREMENTAL:NO" + LINK_FLAGS_RELEASE "" + ) + endif(WINDOWS) + if(STANDALONE) SET_TARGET_PROPERTIES(INTEGRATION_TEST_${testname} PROPERTIES COMPILE_FLAGS -I"${TUT_INCLUDE_DIR}") endif(STANDALONE) diff --git a/indra/cmake/LLCommon.cmake b/indra/cmake/LLCommon.cmake index 17e211cb99..8f7bb296ce 100644 --- a/indra/cmake/LLCommon.cmake +++ b/indra/cmake/LLCommon.cmake @@ -24,7 +24,7 @@ endif (LINUX) add_definitions(${TCMALLOC_FLAG}) -set(LLCOMMON_LINK_SHARED ON CACHE BOOL "Build the llcommon target as a shared library.") +set(LLCOMMON_LINK_SHARED OFF CACHE BOOL "Build the llcommon target as a static library.") if(LLCOMMON_LINK_SHARED) add_definitions(-DLL_COMMON_LINK_SHARED=1) endif(LLCOMMON_LINK_SHARED) diff --git a/indra/cmake/Variables.cmake b/indra/cmake/Variables.cmake index 56ced20abf..4b459f1a48 100644 --- a/indra/cmake/Variables.cmake +++ b/indra/cmake/Variables.cmake @@ -99,10 +99,20 @@ endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(DARWIN 1) + execute_process( + COMMAND sh -c "xcodebuild -version | grep Xcode | cut -d ' ' -f2 | cut -d'.' -f1-2" + OUTPUT_VARIABLE XCODE_VERSION ) + # To support a different SDK update these Xcode settings: - set(CMAKE_OSX_DEPLOYMENT_TARGET 10.5) + if (XCODE_VERSION GREATER 4.2) + set(CMAKE_OSX_DEPLOYMENT_TARGET 10.6) + else (XCODE_VERSION GREATER 4.2) + set(CMAKE_OSX_DEPLOYMENT_TARGET 10.5) + endif (XCODE_VERSION GREATER 4.2) + set(CMAKE_OSX_SYSROOT macosx10.6) set(CMAKE_XCODE_ATTRIBUTE_GCC_VERSION "com.apple.compilers.llvmgcc42") + set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT dwarf-with-dsym) # NOTE: To attempt an i386/PPC Universal build, add this on the configure line: @@ -134,6 +144,11 @@ set(VIEWER ON CACHE BOOL "Build Second Life viewer.") set(VIEWER_CHANNEL "LindenDeveloper" CACHE STRING "Viewer Channel Name") set(VIEWER_LOGIN_CHANNEL ${VIEWER_CHANNEL} CACHE STRING "Fake login channel for A/B Testing") +if (XCODE_VERSION GREATER 4.2) + set(ENABLE_SIGNING OFF CACHE BOOL "Enable signing the viewer") + set(SIGNING_IDENTITY "" CACHE STRING "Specifies the signing identity to use, if necessary.") +endif (XCODE_VERSION GREATER 4.2) + set(VERSION_BUILD "0" CACHE STRING "Revision number passed in from the outside") set(STANDALONE OFF CACHE BOOL "Do not use Linden-supplied prebuilt libraries.") set(UNATTENDED OFF CACHE BOOL "Should be set to ON for building with VC Express editions.") diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index f3df9bb94f..8632a2b722 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -164,7 +164,19 @@ if (LL_TESTS) ${llcorehttp_EXAMPLE_SOURCE_FILES} ) set_target_properties(http_texture_load - PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}") + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}" + ) + + if (WINDOWS) + # The following come from LLAddBuildTest.cmake's INTEGRATION_TEST_xxxx target. + set_target_properties(http_texture_load + PROPERTIES + LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:WINDOWS /INCLUDE:__tcmalloc" + LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\" /INCREMENTAL:NO" + LINK_FLAGS_RELEASE "" + ) + endif (WINDOWS) target_link_libraries(http_texture_load ${example_libs}) diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index ec144693c3..e5488cf941 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -697,10 +697,9 @@ void HttpRequestTestObjectType::test<7>() ensure("Two handler calls on the way out", 2 == mHandlerCalls); -#if defined(WIN32) - // Can only do this memory test on Windows. On other platforms, - // the LL logging system holds on to memory and produces what looks - // like memory leaks... +#if 0 // defined(WIN32) + // Can't do this on any platform anymore, the LL logging system holds + // on to memory and produces what looks like memory leaks... // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); @@ -1036,10 +1035,9 @@ void HttpRequestTestObjectType::test<10>() ensure("Two handler calls on the way out", 2 == mHandlerCalls); -#if defined(WIN32) - // Can only do this memory test on Windows. On other platforms, - // the LL logging system holds on to memory and produces what looks - // like memory leaks... +#if 0 // defined(WIN32) + // Can't do this on any platform anymore, the LL logging system holds + // on to memory and produces what looks like memory leaks... // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); @@ -1271,10 +1269,9 @@ void HttpRequestTestObjectType::test<12>() ensure("Two handler calls on the way out", 2 == mHandlerCalls); -#if defined(WIN32) - // Can only do this memory test on Windows. On other platforms, - // the LL logging system holds on to memory and produces what looks - // like memory leaks... +#if 0 // defined(WIN32) + // Can't do this on any platform anymore, the LL logging system holds + // on to memory and produces what looks like memory leaks... // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); -- cgit v1.2.3 From f2e2ea99a351ca8663af460974137545d020b97f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 11 Sep 2012 12:44:09 -0400 Subject: Resync steam/xui/*/strings.xml with default/xui/*/strings.xml. The steam/xui/*/strings.xml files are intended only to override a single string (create_account_url). But until the strings.xml C++ code supports fallback to the default skin, we must manually keep them synchronized except for that one string. --- indra/newview/skins/steam/xui/de/strings.xml | 36 + indra/newview/skins/steam/xui/en/strings.xml | 235 +++- indra/newview/skins/steam/xui/es/strings.xml | 36 + indra/newview/skins/steam/xui/fr/strings.xml | 36 + indra/newview/skins/steam/xui/it/strings.xml | 36 + indra/newview/skins/steam/xui/ja/strings.xml | 36 + indra/newview/skins/steam/xui/pt/strings.xml | 36 + indra/newview/skins/steam/xui/ru/strings.xml | 36 + indra/newview/skins/steam/xui/tr/strings.xml | 36 + indra/newview/skins/steam/xui/zh/strings.xml | 1909 ++++++++++++++------------ 10 files changed, 1503 insertions(+), 929 deletions(-) diff --git a/indra/newview/skins/steam/xui/de/strings.xml b/indra/newview/skins/steam/xui/de/strings.xml index b15cecb146..e0e272637c 100644 --- a/indra/newview/skins/steam/xui/de/strings.xml +++ b/indra/newview/skins/steam/xui/de/strings.xml @@ -886,6 +886,9 @@ Warten Sie kurz und versuchen Sie dann noch einmal, sich anzumelden. Dem Objekt „[OBJECTNAME]“, ein Objekt von „[OWNERNAME]“, in [REGIONNAME] [REGIONPOS], wurde folgende Berechtigung verweigert: [PERMISSIONS]. + + Wenn Sie dem Objekt Zugriff auf Ihr Konto gewähren, kann dieses außerdem: + Linden-Dollar (L$) von Ihnen nehmen @@ -919,6 +922,9 @@ Warten Sie kurz und versuchen Sie dann noch einmal, sich anzumelden. Kamerasteuerung + + Sie teleportieren + Nicht verbunden @@ -1000,6 +1006,9 @@ Warten Sie kurz und versuchen Sie dann noch einmal, sich anzumelden. Skripts + + Wörterbücher + Nicht abwesend @@ -3858,6 +3867,12 @@ Falls diese Meldung weiterhin angezeigt wird, wenden Sie sich bitte an [SUPPORT_ Avatare sichtbar; Chat außerhalb dieser Parzelle gestattet + + Bewegliche Objekte verhalten sich in dieser Region u. U. erst dann korrekt, wenn die Region neu geformt wird. + + + Dynamisches Pathfinding ist in dieser Region nicht aktiviert. + [APP_NAME] Aktualisierung @@ -5000,6 +5015,21 @@ Setzen Sie den Editorpfad in Anführungszeichen Normal + + http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer + + + Keine + + + Wirkt sich auf Navmesh aus + + + Figur + + + (mehrere) + Sehr niedrig @@ -5015,4 +5045,10 @@ Setzen Sie den Editorpfad in Anführungszeichen Sehr hoch + + Der Einwohner kann diese Region nicht besuchen. + + + [Benutzer] + diff --git a/indra/newview/skins/steam/xui/en/strings.xml b/indra/newview/skins/steam/xui/en/strings.xml index c29b80e21c..4f4b2b2125 100644 --- a/indra/newview/skins/steam/xui/en/strings.xml +++ b/indra/newview/skins/steam/xui/en/strings.xml @@ -441,6 +441,7 @@ Please try logging in again in a minute. Load Files Choose Directory Scripts + Dictionaries @@ -2140,11 +2272,15 @@ Drag folders to this area and click "Send to Marketplace" to list them for sale My Favorites + My Favorites + My Favorites Current Outfit Initial Outfits My Outfits Accessories Meshes + Received Items + Merchant Outbox Friends @@ -3195,7 +3331,8 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. Moderate Region General Region Avatars visible and chat allowed outside of this parcel - + Objects that move may not behave correctly in this region until the region is rebaked. + Dynamic pathfinding is not enabled on this region. [APP_NAME] Update @@ -3751,6 +3888,13 @@ Try enclosing path to the editor with double quotes. Preview Normal + + http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer + None + Affects navmesh + Character + (Multiple) + Very Low Low @@ -3758,4 +3902,9 @@ Try enclosing path to the editor with double quotes. High Very High + The Resident cannot visit this region. + + + [User] + diff --git a/indra/newview/skins/steam/xui/es/strings.xml b/indra/newview/skins/steam/xui/es/strings.xml index 2b36c39e7e..daa3199cec 100644 --- a/indra/newview/skins/steam/xui/es/strings.xml +++ b/indra/newview/skins/steam/xui/es/strings.xml @@ -871,6 +871,9 @@ Intenta iniciar sesión de nuevo en unos instantes. A '[OBJECTNAME]', un objeto propiedad de '[OWNERNAME]', localizado en [REGIONNAME] con la posición [REGIONPOS], se le ha denegado el permiso para: [PERMISSIONS]. + + Si autorizas el acceso a tu cuenta, también permitirás al objeto: + Cogerle a usted dólares Linden (L$) @@ -904,6 +907,9 @@ Intenta iniciar sesión de nuevo en unos instantes. Controlar su cámara + + Teleportarte + General @@ -982,6 +988,9 @@ Intenta iniciar sesión de nuevo en unos instantes. Scripts + + Diccionarios + Salir del estado ausente @@ -3774,6 +3783,12 @@ Si sigues recibiendo este mensaje, contacta con [SUPPORT_SITE]. Los avatares están visibles y está permitido el chat fuera de esta parcela + + Los objetos que se mueven pueden presentar un comportamiento incorrecto en la región hasta que ésta se recargue. + + + Esta región no tiene activado el pathfinding dinámico. + Actualizar [APP_NAME] @@ -4907,6 +4922,21 @@ Inténtalo incluyendo la ruta de acceso al editor entre comillas Normal + + http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer + + + Ninguno + + + Afecta al navmesh + + + Personaje + + + (Múltiple) + Muy bajo @@ -4922,4 +4952,10 @@ Inténtalo incluyendo la ruta de acceso al editor entre comillas Muy alto + + El Residente no puede visitar esta región. + + + [Usuario] + diff --git a/indra/newview/skins/steam/xui/fr/strings.xml b/indra/newview/skins/steam/xui/fr/strings.xml index ac34dd67c8..dcb25357bc 100644 --- a/indra/newview/skins/steam/xui/fr/strings.xml +++ b/indra/newview/skins/steam/xui/fr/strings.xml @@ -886,6 +886,9 @@ Veuillez réessayer de vous connecter dans une minute. '[OBJECTNAME]', un objet appartenant à [OWNERNAME], situé dans [REGIONNAME] à [REGIONPOS], n'a pas reçu le droit de : [PERMISSIONS]. + + Si vous autorisez un accès à votre compte, vous autorisez également l'objet à : + Débiter vos Linden dollars (L$) @@ -919,6 +922,9 @@ Veuillez réessayer de vous connecter dans une minute. Contrôler votre caméra + + Vous téléporter + Pas connecté(e) @@ -1000,6 +1006,9 @@ Veuillez réessayer de vous connecter dans une minute. Scripts + + Dictionnaires + Présent @@ -3858,6 +3867,12 @@ Si ce message persiste, veuillez aller sur la page [SUPPORT_SITE]. Avatars visibles et chat autorisé en dehors de cette parcelle + + Les objets mobiles risquent de ne pas se comporter correctement dans cette région tant qu'elle n'est pas refigée. + + + La recherche de chemin dynamique n'est pas activée dans cette région. + [APP_NAME] - Mise à jour @@ -5000,6 +5015,21 @@ Essayez avec le chemin d'accès à l'éditeur entre guillemets doubles Normal + + http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer + + + Aucun + + + Maillage de navigation affecté + + + Personnage + + + (Multiple) + Très faible @@ -5015,4 +5045,10 @@ Essayez avec le chemin d'accès à l'éditeur entre guillemets doubles Très élevée + + Le résident ne peut pas visiter cette région. + + + [User] + diff --git a/indra/newview/skins/steam/xui/it/strings.xml b/indra/newview/skins/steam/xui/it/strings.xml index 0fcc4e0820..180eecad7b 100644 --- a/indra/newview/skins/steam/xui/it/strings.xml +++ b/indra/newview/skins/steam/xui/it/strings.xml @@ -880,6 +880,9 @@ Prova ad accedere nuovamente tra un minuto. A '[OBJECTNAME]', un oggetto di proprietà di '[OWNERNAME]', situato in [REGIONNAME] [REGIONPOS], è stato negato il permesso di: [PERMISSIONS]. + + Se consenti l'accesso al tuo account, consentirai anche all'oggetto di: + Prendere dollari Linden (L$) da te @@ -913,6 +916,9 @@ Prova ad accedere nuovamente tra un minuto. Controllare la tua fotocamera + + Teleportarti + Generale @@ -991,6 +997,9 @@ Prova ad accedere nuovamente tra un minuto. Script + + Dizionari + Imposta come non assente @@ -3780,6 +3789,12 @@ Se il messaggio persiste, contatta [SUPPORT_SITE]. Avatar visibili e chat consentita fuori di questo lotto + + Gli oggetti che si muovono potrebbero non comportarsi correttamente in questa regione fino a quando non viene eseguito il rebake della regione. + + + Il pathfinding dinamico non è attivato in questa regione. + Aggiornamento [APP_NAME] @@ -4910,6 +4925,21 @@ Prova a racchiudere il percorso dell'editor in doppie virgolette. Normale + + http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer + + + Nessuno + + + Influenza il navmesh + + + Personaggio + + + (Multiple) + Molto basso @@ -4925,4 +4955,10 @@ Prova a racchiudere il percorso dell'editor in doppie virgolette. Molto alto + + Il Residente non può visitare questa regione. + + + [User] + diff --git a/indra/newview/skins/steam/xui/ja/strings.xml b/indra/newview/skins/steam/xui/ja/strings.xml index 29ef13109c..d474703fca 100644 --- a/indra/newview/skins/steam/xui/ja/strings.xml +++ b/indra/newview/skins/steam/xui/ja/strings.xml @@ -886,6 +886,9 @@ support@secondlife.com にお問い合わせください。 [REGIONNAME] の [REGIONPOS] という場所にある、「 [OWNERNAME] 」が所有する「 [OBJECTNAME] 」というオブジェクトは、次の権限を拒否しました: [PERMISSIONS] + + あなたのアカウントへのアクセスを許可すると、このオブジェクトには次の操作も許可されます: + リンデンドル(L$)を支払う @@ -919,6 +922,9 @@ support@secondlife.com にお問い合わせください。 カメラのコントロール + + あなたをテレポート + 接続されていません @@ -1000,6 +1006,9 @@ support@secondlife.com にお問い合わせください。 スクリプト + + 辞書 + 一時退席中解除 @@ -3858,6 +3867,12 @@ www.secondlife.com から最新バージョンをダウンロードしてくだ この区画外にアバターを見えるようにして、チャットも許可 + + 地域(リージョン)が再構築されるまで、移動するオブジェクトは正しく動作しない可能性があります。 + + + この地域(リージョン)でダイナミックパスファインディングが有効になっていません。 + [APP_NAME] アップデート @@ -5000,6 +5015,21 @@ www.secondlife.com から最新バージョンをダウンロードしてくだ 普通 + + http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer + + + なし + + + ナビメッシュに影響を与える + + + キャラクター + + + (複数) + 非常に低い @@ -5015,4 +5045,10 @@ www.secondlife.com から最新バージョンをダウンロードしてくだ 非常に高い + + 住人はこの地域(リージョン)を訪問できません。 + + + [User] + diff --git a/indra/newview/skins/steam/xui/pt/strings.xml b/indra/newview/skins/steam/xui/pt/strings.xml index 07fd95c907..f2f66e8a6f 100644 --- a/indra/newview/skins/steam/xui/pt/strings.xml +++ b/indra/newview/skins/steam/xui/pt/strings.xml @@ -835,6 +835,9 @@ Pessoas com contas gratuitas não poderão acessar o Second Life no momento para '[OBJECTNAME]', um objeto de '[OWNERNAME]', localizado em [REGIONNAME] a [REGIONPOS], teve permissão negada para: [PERMISSIONS]. + + Se você permitir acesso à sua conta, o objeto também poderá: + Tomar linden dólares (L$) de você @@ -868,6 +871,9 @@ Pessoas com contas gratuitas não poderão acessar o Second Life no momento para Controle sua camera + + Teletransportá-lo + Público geral @@ -946,6 +952,9 @@ Pessoas com contas gratuitas não poderão acessar o Second Life no momento para Scripts + + Dicionários + deixar como ausente @@ -3734,6 +3743,12 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. Avatar visíveis e bate-papo permitido fora deste terreno + + Os objetos que se movem podem não se comportar corretamente nesta região até que ela seja recarregada. + + + O pathfinding dinâmico não está habilitado nesta região. + [APP_NAME] Atualização @@ -4867,6 +4882,21 @@ Tente colocar o caminho do editor entre aspas. Normal + + http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer + + + Nenhum + + + Afeta o navmesh + + + Personagem + + + (Múltiplo) + Muito baixo @@ -4882,4 +4912,10 @@ Tente colocar o caminho do editor entre aspas. Muito alto + + O residente não pode visitar a região. + + + [Usuário] + diff --git a/indra/newview/skins/steam/xui/ru/strings.xml b/indra/newview/skins/steam/xui/ru/strings.xml index 2b4c6e6f48..03b8a153df 100644 --- a/indra/newview/skins/steam/xui/ru/strings.xml +++ b/indra/newview/skins/steam/xui/ru/strings.xml @@ -883,6 +883,9 @@ support@secondlife.com. Объекту «[OBJECTNAME]», который принадлежит пользователю «[OWNERNAME]» и находится в [REGIONPOS] в регионе «[REGIONNAME]», отказано в разрешении: [PERMISSIONS]. + + Разрешив доступ к своему аккаунту, вы также разрешите объекту: + У вас берут Linden-деньги @@ -916,6 +919,9 @@ support@secondlife.com. Управлять камерой + + Телепортировать вас + Нет подключения @@ -997,6 +1003,9 @@ support@secondlife.com. Скрипты + + Словари + На месте @@ -3852,6 +3861,12 @@ support@secondlife.com. Все жители с других участков могут видеть аватары и общаться в чате + + Возможны неполадки подвижных объектов в этом регионе, пока регион не будет восстановлен. + + + В этом регионе не разрешен динамический поиск пути. + Обновление [APP_NAME] @@ -5006,6 +5021,21 @@ support@secondlife.com. Нормальный + + http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer + + + Нет + + + Влияет на навигационную сетку + + + Персонаж + + + (несколько) + Очень низкий @@ -5021,4 +5051,10 @@ support@secondlife.com. Очень высокий + + Житель не может посетить этот регион. + + + [Пользователь] + diff --git a/indra/newview/skins/steam/xui/tr/strings.xml b/indra/newview/skins/steam/xui/tr/strings.xml index 74bb33a5c5..4ca10eb6ca 100644 --- a/indra/newview/skins/steam/xui/tr/strings.xml +++ b/indra/newview/skins/steam/xui/tr/strings.xml @@ -883,6 +883,9 @@ Lütfen bir dakika içerisinde tekrar oturum açmayı deneyin. '[OWNERNAME]' adlı kişiye ait, [REGIONPOS] üzerinde [REGIONNAME] içerisinde bulunan '[OBJECTNAME]' nesnesine şunu yapma izni verilmedi: [PERMISSIONS]. + + Eğer hesabınıza erişime izin verirseniz, bu nesneye aynı zamanda şunun için izin vermiş olacaksınız: + Sizden Linden dolar (L$) almak @@ -916,6 +919,9 @@ Lütfen bir dakika içerisinde tekrar oturum açmayı deneyin. Kameranızı kontrol etmek + + Sizi ışınlama + Bağlı Değil @@ -997,6 +1003,9 @@ Lütfen bir dakika içerisinde tekrar oturum açmayı deneyin. Komut Dosyaları + + Sözlükler + Uzakta Değil @@ -3855,6 +3864,12 @@ Bu iletiyi almaya devam ederseniz, lütfen [SUPPORT_SITE] bölümüne başvurun. Bu parselin dışında avatarlar görünür durumda ve sohbete izin veriliyor + + Bölge yeniden kaydedilinceye kadar hareket eden nesneler bu bölgede doğru davranmayabilir. + + + Bu bölgede dinamik yol bulma etkin değil. + [APP_NAME] Güncelleştirme @@ -5007,6 +5022,21 @@ Düzenleyici yolunu çift tırnakla çevrelemeyi deneyin. Normal + + http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer + + + Hiçbiri + + + Navigasyon örgüsünü etkiler + + + Karakter + + + (Birden çok) + Çok Düşük @@ -5022,4 +5052,10 @@ Düzenleyici yolunu çift tırnakla çevrelemeyi deneyin. Çok Yüksek + + Sakin bu bölgeyi ziyaret edemez. + + + [User] + diff --git a/indra/newview/skins/steam/xui/zh/strings.xml b/indra/newview/skins/steam/xui/zh/strings.xml index 882b617289..718d6805f1 100644 --- a/indra/newview/skins/steam/xui/zh/strings.xml +++ b/indra/newview/skins/steam/xui/zh/strings.xml @@ -44,7 +44,7 @@ 全亮(舊版) - 登入中。[APP_NAME] 可能出現凍結狀態。請耐心稍等。 + 登入中。[APP_NAME] 可能看似凍結,請耐心稍等。 請稍候。 登入中... @@ -56,7 +56,7 @@ 進行帳戶維護... - 先前企圖嘗試登入失敗。現登入中,嘗試 [NUMBER] + 前一次登入失敗。 登入中,第 [NUMBER] 次嘗試 世界載入中... @@ -101,25 +101,25 @@ 服裝下載中... - 伺服器回傳一個無效果損壞的憑證。請連繫網格管理者。 + 伺服器回傳一個無效或損壞的憑證。 請聯絡網格管理員。 - An invalid hostname was used to access the server, please check your SLURL or Grid hostname. + 用了無效的主機名來聯絡伺服器,請檢查你的 SLURL 或網格主機名。 - The certificate returned by the Grid appears to be expired. Please check your system clock, or contact your Grid administrator. + 網格傳回的憑證似乎已過期。 請檢查系統時鐘,或聯絡網格管理員。 - The certificate returned by the server could not be used for SSL. Please contact your Grid administrator. + 伺服器傳回的憑證無法用於 SSL。 請聯絡網格管理員。 - Too many certificates were in the servers Certificate chain. Please contact your Grid administrator. + 伺服器憑證鍊中的憑證數目太多。 請聯絡網格管理員。 - The certificate signature returned by the Grid server could not be verified. Please contact your Grid administrator. + 無法檢驗通過網格伺服器傳回的憑證簽名。 請聯絡網格管理員。 - Network Error: Could not establish connection, please check your network connection. + 網路出錯:無法建立連線,請檢查網路連線是否正常。 登入失敗。 @@ -130,8 +130,136 @@ http://join.secondlife.com/?sourceid=1206_steam + + 你目前所用的 Viewer 已經無法再進入第二人生。 請到這個頁面下載最新 Viewer: +http://secondlife.com/download + +欲知詳情,請參閱下面的常見問題集: +http://secondlife.com/viewer-access-faq + + + 有可以選擇性安裝的更新版:[VERSION] + + + 你必須更新為這個版本:[VERSION] + + + 此用戶已經登入。 + + + 抱歉! 我們無法讓你登入。 +請檢查確定你輸入了正確的 + * 使用者名稱(例:bobsmith123 或 steller.sunshine) + * 密碼 +並請確定鍵盤沒有鎖定大寫鍵。 + + + 為了安全起見,已經變更你的密碼。 +請到位於 http://secondlife.com/password 的帳號頁面 +回答安全驗證問題後,重設密碼。 +如有造成不便請多包涵。 + + + 我們系統有所變更,你必須重設密碼。 +請到位於 http://secondlife.com/password 的帳號頁面 +回答安全驗證問題後,重設密碼。 +如有造成不便請多包涵。 + + + 第二人生目前暫時關閉進行維護。 +目前僅允許林登員工登入。 +請到 www.secondlife.com/status 察看最新公告。 + + + 第二人生此時暫時限制登入,以確保不影響效能,讓目前虛擬世界裡的用戶享受最佳的體驗。 + +免費帳戶的用戶此時暫時無法進入第二人生,因為我們必須優先容納付費用戶。 + + + 無法從這部電腦進入第二人生。 +如你認為這是我們弄錯,請聯絡 support@secondlife.com。 + + + 你的帳號要等到 [TIME] (太平洋時間)才可使用。 + + + 此時無法完成你的請求。 +請到 http://secondlife.com/support 聯絡支援人員獲得幫助。 +如果你無法變更密碼,請致電 (866) 476-9763 (美國)。 + + + 登入時的資料不一致。 +請聯絡 support@secondlife.com。 + + + 你的帳號目前正在進行小規模的維護。 +你的帳號要等到 [TIME] (太平洋時間)才可使用。 +如你認為這是我們弄錯,請聯絡 support@secondlife.com。 + + + 模擬器回應:登出請求出錯。 + + + 系統正在處理你的登出。 +你的帳號要等到 [TIME] (太平洋時間)才可使用。 + + + 無法建立有效的時域。 + + + 無法連接到模擬器。 + + + 你的帳號僅能在 [START] 到 [END] (太平洋時間)時段進入第二人生。 +請耐心等到該時段再回來。 +如你認為這是我們弄錯,請聯絡 support@secondlife.com。 + + + 錯誤的參數。 +如你認為這是我們弄錯,請聯絡 support@secondlife.com。 + + + 全名的第一個字(first name)須為英文字母或數字。 +如你認為這是我們弄錯,請聯絡 support@secondlife.com。 + + + 全名的第二個字(last name)須為英文字母或數字。 +如你認為這是我們弄錯,請聯絡 support@secondlife.com。 + + + 地區即將離線。 +請稍待一分鐘再試。 + + + 用戶不在地區裡。 +請稍待一分鐘再試。 + + + 地區正在登入另一個時域。 +請稍待一分鐘再試。 + + + 地區正在登入上一個時域。 +請稍待一分鐘再試。 + + + 地區正在登出上一個時域。 +請稍待一分鐘再試。 + + + 地區剛剛登出上一個時域。 +請稍待一分鐘再試。 + + + 地區已經開始登出程序。 +請稍待一分鐘再試。 + + + 系統已經開始登出你的上一個時域。 +請稍待一分鐘再試。 + - 這個地區可能遭遇問題,請檢查你的網路連線。 + 本區域可能正發生問題。 請檢查你的網際網路連線是否正常。 你的設定儲存中... @@ -158,7 +286,7 @@ (無名稱) - 擁有者: + 所有人: 公開 @@ -196,60 +324,60 @@ 只有一個物品可以被拖曳到此處 - + - 點擊以察看這個網頁 + 點按以察看這個網頁 - 點擊以察看這個位置資訊 + 點按以察看這個位置資訊 - 點擊以察看這個居民檔案 + 點按以察看這個居民檔案 瞭解更多有關這個居民 - 點擊以封鎖這位居民 + 點按以封鎖這位居民 - 點擊以解除封鎖這位居民 + 點按以解除封鎖這位居民 - 點擊開始 IM 這位居民 + 點按開始 IM 這位居民 - 點擊以支付這位居民 + 點按以支付這位居民 - 點擊以送出瞬間傳送邀請給這位居民 + 點按以送出瞬間傳送邀請給這位居民 - 點擊以送出交友邀請給這位居民 + 點按以送出交友邀請給這位居民 - 點擊以察看這個群組的描述 + 點按以察看這個群組的描述 - 點擊以察看這個活動的描述 + 點按以察看這個活動的描述 - Click to view this classified + 點按察看這個個人廣告 - 點擊以察看這個地段的描述 + 點按以察看這個地段的描述 - 點擊以傳送到這個位置 + 點按以傳送到這個位置 - 點擊以察看這個物件的描述 + 點按以察看這個物件的描述 - 點擊以察看此處在地圖上的位置 + 點按以察看此處在地圖上的位置 - 點擊以執行 secondlife:// 指令 + 點按以執行 secondlife:// 指令 @@ -304,7 +432,7 @@ 搜尋中... - 未發現。 + 查無結果。 檢索... @@ -328,7 +456,7 @@ (無) - Avaline Caller [ORDER] + Avaline 通話者 [ORDER] 無錯誤 @@ -355,10 +483,10 @@ 檔案傳輸逾時 - Circuit gone + 失去線路 - Viewer and server do not agree on price + Viewer 和伺服器無法同意價格 未知狀態 @@ -391,13 +519,13 @@ 資料夾 - root + 根目錄 - LSL2 script + LSL2 腳本 - LSL bytecode + LSL 位元組碼 tga 材質 @@ -409,7 +537,7 @@ 快照 - Lost and Found + 失物招領 targa 圖像 @@ -438,6 +566,9 @@ 資料夾聯結 + + 網面 + (外觀編輯中) @@ -457,88 +588,88 @@ 生氣 - Away + 離開 - Backflip + 後空翻 - Belly Laugh + 捧腹大笑 - BigSmile + 大微笑 - Blow Kiss + 飛吻 無聊 - Bow + 彎腰點頭 拍手 - Court Bow + 宮廷鞠躬 哭泣 - Dance 1 + 跳舞1 - Dance 2 + 跳舞2 - Dance 3 + 跳舞3 - Dance 4 + 跳舞4 - Dance 5 + 跳舞5 - Dance 6 + 跳舞6 - Dance 7 + 跳舞7 - Dance 8 + 跳舞8 - Disdain + 鄙視 - Drink + 喝一口 - Embarrassed + 尷尬 - Finger Wag + 揮動食指 - Fist Pump + 高舉右拳 - Floating Yoga + 漂浮瑜伽 - Frown + 皺眉 - Impatient + 不耐煩 - Jump For Joy + 雀躍 - Kiss My Butt + 親我屁股! 親吻 @@ -547,55 +678,55 @@ 笑 - Muscle Beach + 秀健美肌肉 不(不快樂) - 不 + 否 Nya-nya-nya - One-Two Punch + 連續左右出拳 - Open Mouth + 張口 - Peace + 和平手勢 - Point at Other + 指著別人 - Point at Self + 指著自己 - Punch Left + 左出拳 - Punch Right + 右出拳 - RPS count + 剪刀石頭布預備動作 - RPS paper + 剪刀石頭布:布 - RPS rock + 剪刀石頭布:石頭 - RPS scissors + 剪刀石頭布:剪刀 - Repulsed + 作噁 - Roundhouse Kick + 旋踢 傷心 @@ -604,37 +735,37 @@ 敬禮 - Shout + 吶喊 - Shrug + 聳聳肩 微笑 - Smoke Idle + 悠閒抽菸 - Smoke Inhale + 吸一口菸 - Smoke Throw Down + 甩菸蒂 驚喜 - Sword Strike + 劍擊 - Tantrum + 鬧脾氣 - TongueOut + 吐舌頭 - Wave + 揮手 耳語 @@ -664,19 +795,19 @@ 離線 - [AREA] m² L$[PRICE] + [AREA] 平方公尺 L$[PRICE] - 沒有發現。 + 查無結果。 確定 - Premature end of file + 檔案異常中止 - Can't find ROOT or JOINT. + 找不到 ROOT 或旋軸。 低語: @@ -700,34 +831,34 @@ 現在你將重新聯接到附近的語音聊天 - '[OBJECTNAME]', an object owned by '[OWNERNAME]', located in [REGIONNAME] at [REGIONPOS], has been granted permission to: [PERMISSIONS]. + 物件「[OBJECTNAME]'」(所有人「[OWNERNAME]」,位於「[REGIONNAME]」,方位「[REGIONPOS]」)已獲得下列權限:[PERMISSIONS]。 - '[OBJECTNAME]', an object owned by '[OWNERNAME]', located in [REGIONNAME] at [REGIONPOS], has been denied permission to: [PERMISSIONS]. + 物件「[OBJECTNAME]'」(所有人「[OWNERNAME]」,位於「[REGIONNAME]」,方位「[REGIONPOS]」)已被撤除下列權限:[PERMISSIONS]。 由你身上拿走林登幣(L$) - Act on your control inputs + 按你的控制輸入行動 - Remap your control inputs + 重新規劃你的控制輸入 - Animate your avatar + 使化身動起來 - Attach to your avatar + 附加到化身 - Release ownership and become public + 釋出所有權,開放給所有人 - Link and delink from other objects + 連結其他物件或去除連結 - Add and remove joints with other objects + 和其他物件建立或移除旋軸 變更它的權限 @@ -799,7 +930,7 @@ AVI 視頻檔案 - XAF Anim File + XAF 動畫檔案 XML 檔案 @@ -991,19 +1122,19 @@ 送出由 - Attached: + 附件: - View past notices or opt-out of receiving these messages here. + 在這裡察看舊通知或選擇不接收訊息。 - Open Attachment + 開啟附件 - Save Attachment + 儲存附件 - Teleport offering + 發出「瞬間傳送」邀請 當你離開時有新的通知送達。 @@ -1042,16 +1173,16 @@ 按下 ESC 鍵回復到世界的視角 - 沒有發現你想要找的嗎?試試 [secondlife:///app/search/all/[SEARCH_TERM] 搜尋]。 + 找不到你要找的嗎? 請試試 [secondlife:///app/search/places/ 搜尋]。 - 沒有發現你想要找的嗎?試試 [secondlife:///app/search/places/[SEARCH_TERM] 搜尋]。 + 找不到你要找的嗎? 請試試 [secondlife:///app/search/places/[SEARCH_TERM] 搜尋]。 - Drag a landmark here to add it to your favorites. + 將一個地標拖曳到這裡,加進「我的最愛」。 - You do not have a copy of this texture in your inventory + 你的收納區裡沒有這個材質的副本 @@ -1066,7 +1197,7 @@ 無內容 - + @@ -1078,23 +1209,23 @@ - + - + - - - - - - - - - - - - + + + + + + + + + + + + 我的收納區 @@ -1136,7 +1267,7 @@ 收納區 - Uncompressed Images + 未壓縮圖像 身體部位 @@ -1148,10 +1279,10 @@ 相簿 - Lost And Found + 失物招領 - 無壓縮聲音 + 未壓縮聲音 動作 @@ -1177,6 +1308,9 @@ 配件 + + 網面 + 朋友 @@ -1187,7 +1321,7 @@ 購買 - Buy for L$ + 出價購買:L$ 石頭 @@ -1211,13 +1345,13 @@ 橡膠 - Light + 光源 - Shift + Shift 鍵 - Ctrl + Ctrl 鍵 胸部 @@ -1250,7 +1384,7 @@ 骨盆 - 嘴 + 嘴巴 下巴 @@ -1310,22 +1444,22 @@ 右胸肌 - 無效的附件聯接點 + 無效的附接點 - [AGEYEARS] [AGEMONTHS] old + 年齡:[AGEYEARS] 年 [AGEMONTHS] 月 - [AGEYEARS] old + 年齡:[AGEYEARS] 年 - [AGEMONTHS] old + [AGEMONTHS] 月 - [AGEWEEKS] old + [AGEWEEKS] 週 - [AGEDAYS] old + [AGEDAYS] 天 今日剛加入 @@ -1367,40 +1501,40 @@ [COUNT] 天 - [COUNT] 成員 + [COUNT] 位成員 - [COUNT] 成員 + [COUNT] 位成員 - [COUNT] 成員 + [COUNT] 位成員 居民 - Trial + 試用 - Charter Member + 老牌 Charter 成員 林登實驗室員工 - Payment Info Used + 使用的付款資料 - Payment Info On File + 預留付款資料 - No Payment Info On File + 未預留付款資料 - 已年齡驗證 + 通過年齡驗證 - 未年齡驗證 + 未通過年齡驗證 中央 2 @@ -1427,22 +1561,22 @@ 右下 - 已下載,現在進行編譯中 + 已下載,正在編譯中 伺服器上未發現腳本。 - 問題下載中 + 下載時出問題 - Insufficient permissions to download a script. + 下載腳本的權限不足。 - Insufficient permissions for + 權限不足: - Unknown failure to download + 下載失敗,原因不明 重新編譯進度 @@ -1457,19 +1591,19 @@ 重設 - 設定執行中程序 + 設定「執行中」進度 - 設為執行中 + 設為「執行中」 - 設定非執行中程序 + 設定「非執行中」進度 - 設為非執行中 + 設為「非執行中」 - 編譯成功!! + 編譯成功! 編譯成功,儲存中... @@ -1490,36 +1624,36 @@ (未知) - + - + - Balance + 餘額 - Credits + 貸記 - Debits + 借記 - Total + 總額 - 無群組資料發現 + 查無群組資料 - parent estate + 母領地 - mainland + 大陸 - teen + 青少年 - error + 錯誤 [OWNER] 所擁有的的全部領地 @@ -1528,13 +1662,13 @@ 你所擁有的全部領地 - all estates that you manage for [OWNER] + 你為 [OWNER] 管理的全部領地 - Allowed Residents: ([ALLOWEDAGENTS], max [MAXACCESS]) + 允許居民:([ALLOWEDAGENTS],最多 [MAXACCESS]) - 允許的群群組:([ALLOWEDGROUPS],最大 [MAXACCESS]) + 允許的群組:([ALLOWEDGROUPS],最多 [MAXACCESS]) 地段腳本記憶體 @@ -1549,28 +1683,28 @@ 記憶體用量:[COUNT] kb - 地段腳本 URLs + 地段腳本的 URL - URLs used: [COUNT] out of [MAX]; [AVAILABLE] available + URL 使用狀況:最多可用 [MAX],已用 [COUNT],剩餘 [AVAILABLE] - URLs used: [COUNT] + 已用 URL:[COUNT] - Error requesting information + 調資料時出錯 - 無地段被選擇 + 未選擇地段 - Error: script information is only available in your current region + 錯誤:只能在你目前所處區域取得腳本資訊 - Retrieving information... + 正在調閱資料... - You do not have permission to examine this parcel + 你無權審視此地段 坐在 @@ -1666,34 +1800,34 @@ 左胸肌 - HUD Center 2 + 擡頭顯示中央 2 - HUD Top Right + 擡頭顯示右上 - HUD Top Center + 擡頭顯示中央上方 - HUD Top Left + 擡頭顯示左上 - HUD Center 1 + 擡頭顯示中央 1 - HUD Bottom Left + 擡頭顯示左下 - HUD Bottom + 擡頭顯示下方 - HUD Bottom Right + 擡頭顯示右下 - Line [LINE], Column [COLUMN] + 橫行 [LINE],縱列 [COLUMN] - [COUNT] found + 找到 [COUNT] [hour12,datetime,slt]:[min,datetime,slt] [ampm,datetime,slt] @@ -1702,16 +1836,16 @@ [mthnum,datetime,slt]/[day,datetime,slt] - 物件的內容 + 物件內容 新腳本 - 你傳送訊息的居民目前處於忙碌模式中,這意味著他要求不被打擾。你所傳的訊息仍將會留存並顯示於 IM 面板上供他稍後時查閱。 + 你傳訊過去的居民目前處於忙碌狀態,這意味著他要求不被打擾。 你的訊息仍將留存並顯示於對���的 IM 面板上供稍後查閱。 - (由名稱) + (按名稱) (居民) @@ -1726,12 +1860,12 @@ (外部) - 此領地未提供任何契約要求。 + 此領地沒有任何契約要求。 - There is no Covenant provided for this Estate. The land on this estate is being sold by the Estate owner, not Linden Lab. Please contact the Estate Owner for sales details. + 此領地沒有任何契約要求。 本領地土地的出售人是領地所有人,不是林登實驗室。 請聯絡領地所有人獲知售地詳情。 - + @@ -1741,13 +1875,13 @@ 公開 - Clicks: [TELEPORT] teleport, [MAP] map, [PROFILE] profile + 點按:[TELEPORT] 瞬間傳送,[MAP] 地圖,[PROFILE] 檔案 - (將於發布後自動更新) + (將於發布後更新) - You haven't created any Picks or Classifieds. Click the Plus button below to create a Pick or Classified. + 你尚未建立任何精選地點或個人廣告。 點按下面的 + 按鈕建立精選地點或個人廣告。 使用者無精選地點或個人廣告 @@ -1762,86 +1896,86 @@ 屬性 - An object named + 一個物件,名為 - 群組所擁有 + 屬於群組 - 由一個未知的群組所擁有 + 屬於一個未知群組 - owned by + 所有人: - owned by an unknown user + 屬於某未知使用者 - gave you + 給予你 - You decline [DESC] from <nolink>[NAME]</nolink>. + 你拒絕了來自 <nolink>[NAME]</nolink> 的 [DESC]。 - Total + 總額 - bought + 買了 - paid you + 支付給你 - paid into + 支付給 - bought pass to + 買了通行權: - paid fee for event + 付了費用參加活動 - paid prize for event + 付了賞金參加活動 - Balance + 餘額 - Credits + 貸記 - Debits + 借記 - Contents + 內容 - Acquired Items + 取得物品 取消 - 花費 L$ [AMOUNT] 上傳 [NAME] + 上傳 [NAME] 費用 L$ [AMOUNT] - 花費 L$ [AMOUNT] 購買這個 + 購買這個需要 L$ [AMOUNT] 未知的副檔名 .%s -預期為 .wav, .tga, .bmp, .jpg, .jpeg, or .bvh 類型 +應該是 .wav、.tga、.bmp、.jpg、.jpeg 或 .bvh - Block + 封鎖 - Block + 封鎖 - Unblock + 解除封鎖 - Unblock + 解除封鎖 添加到我的地標... @@ -1877,25 +2011,25 @@ 接收中 - AM + 上午 - PM + 下午 - PST + 太平洋時間 - PDT + 太平洋日光節約時間 向前 - 向左 + 左移鍵 - 向右 + 右移鍵 向後 @@ -1913,46 +2047,46 @@ 東 - 向上 + 上移鍵 - 向下 + 下移鍵 任何類別 - 採購 + 購物 - Land Rental + 土地租賃 - Property Rental + 房產租賃 - Special Attraction + 熱門地點 - New Products + 新產品 - Employment + 徵人 - Wanted + 徵求 - Service + 服務 - Personal + 交友 - Linden Location + 林登位置 完全成人 @@ -1973,25 +2107,25 @@ 聚會所 - 新手友善 + 歡迎新手光臨 - 公園與自然 + 公園與自然景觀 住宅 - Stage + 舞臺 - Other + 其他 - Rental + 出租 - Any + 任何 你 @@ -2027,7 +2161,7 @@ 多媒體 - 播放/暫停 媒體 + 播放/暫停媒體 解析命令列時發現錯誤。 @@ -2038,1510 +2172,1510 @@ [APP_NAME] 命令列用法: - [APP_NAME] 無法存取它所需要的檔案。 + [APP_NAME] 無法存取它所需的檔案。 -This can be because you somehow have multiple copies running, or your system incorrectly thinks a file is open. -If this message persists, restart your computer and try again. -If it continues to persist, you may need to completely uninstall [APP_NAME] and reinstall it. +你可能有多重實例執行中,或你的系統誤以為某檔案已經開啟。 +如果這個訊息持續出現,請重新啟動你的電腦。 +如果問題仍然存在,你可能需要徹底卸除 [APP_NAME] 再重新安裝。 致命錯誤 - [APP_NAME] requires a processor with AltiVec (G4 or later). + [APP_NAME] 需要有 AltiVec(G4 或更新版)的處理器。 [APP_NAME] 已經在執行中。 -請檢查你的工作列裡是否有其他最小化的相同程式。 +請檢查你的工作列裡是否已有最小化的相同程式。 如果這個訊息持續出現,請重新啟動你的電腦。 - [APP_NAME] appears to have frozen or crashed on the previous run. -Would you like to send a crash report? + [APP_NAME] 上次執行時好像出現凍結或當掉。 +你是否想要報告當機事例? 通知 - [APP_NAME] is unable to detect DirectX 9.0b or greater. -[APP_NAME] uses DirectX to detect hardware and/or outdated drivers that can cause stability problems, poor performance and crashes. While you can run [APP_NAME] without it, we highly recommend running with DirectX 9.0b. + [APP_NAME] 偵測不到 DirectX 9.0b 或更新版。 +[APP_NAME] 使用 DirectX 來偵測可能導致不穩定、執行效能不佳或當機發生的硬體或老舊驅動器。 你可以選擇不用 DirectX 9.0b 來執行 [APP_NAME],但我們大力推薦你使用。 -Do you wish to continue? +你確定要繼續嗎? 警告 - Automatic updating is not yet implemented for Linux. -Please download the latest version from www.secondlife.com. + 尚無試用於 Linux 的自動更新功能。 +請到 www.secondlife.com 下載最新版本。 - RegisterClass failed + RegisterClass 失敗 錯誤 - 無法執行全螢幕於 [WIDTH] x [HEIGHT]. -執行於視窗中。 + [WIDTH] x [HEIGHT] 解析度下無法執行全螢幕。 +在視窗中執行。 - Shutdown Error while destroying window (DestroyWindow() failed) + 消滅視窗時發生強行關閉錯誤(DestroyWindow()失敗) - Shutdown Error + 強行關閉出錯 - Can't make GL device context + 無法建立 GL 裝置環境 - Can't find suitable pixel format + 找不到適合的像素格式 - Can't get pixel format description + 無法取得像素格式描述 - [APP_NAME] requires True Color (32-bit) to run. -Please go to your computer's display settings and set the color mode to 32-bit. + [APP_NAME] 需要全彩(32位元)才能執行。 +請到電腦的顯示設定,將色彩模式設為 32 位元。 - [APP_NAME] is unable to run because it can't get an 8 bit alpha channel. Usually this is due to video card driver issues. -Please make sure you have the latest video card drivers installed. -Also be sure your monitor is set to True Color (32-bit) in Control Panels > Display > Settings. -If you continue to receive this message, contact the [SUPPORT_SITE]. + [APP_NAME] 無法執行,無法取得 8 位元 alpha 頻道。 這通常是因為顯示卡驅動程式出問題。 +請確定你安裝了最新的顯示卡驅動程式。 +請到控制台 > 顯示 > 設定處將螢幕設為全彩(32 位元)。 +如果你繼續看到此訊息,請聯絡 [SUPPORT_SITE]。 無法設定像素格式 - Can't create GL rendering context + 無法建立 GL 呈像環境 - Can't activate GL rendering context + 無法啟動 GL 呈像環境 - [APP_NAME] is unable to run because your video card drivers did not install properly, are out of date, or are for unsupported hardware. Please make sure you have the latest video card drivers and even if you do have the latest, try reinstalling them. + [APP_NAME] 無法執行,這可能因為你的顯示卡驅動程式安裝不當、版本過舊,或者你的硬體不受支援。 請確定你安裝了最新的顯示卡驅動程式。如果已裝了最新驅動程式,請再重新安裝。 -If you continue to receive this message, contact the [SUPPORT_SITE]. +如果你繼續看到此訊息,請聯絡 [SUPPORT_SITE]。 - 5 O'Clock Shadow + 下午五點的新鬍渣 全白 - Anime Eyes + 日式動漫眼 - Arced + 彎拱的 - Arm Length + 臂長 - Attached + 貼附的 - Attached Earlobes + 附著耳垂 - Back Fringe + 後瀏海 - Baggy + 袋型的 - Bangs + 瀏海 - Beady Eyes + 珠圓銳光眼 - Belly Size + 腹部大小 - Big + 大 - Big Butt + 大臀部 - Big Hair: Back + 大型頭髮:後面 - Big Hair: Front + 大型頭髮:前面 - Big Hair: Top + 大型頭髮:頂部 - Big Head + 大頭 - Big Pectorals + 大胸肌 - Big Spikes + 大尖直髮 - Black + 黑色 - Blonde + 金色 - Blonde Hair + 金髮 - Blush + 腮紅 - Blush Color + 淺粉色 - Blush Opacity + 腮紅不透明度: - Body Definition + 身體結實度 - Body Fat + 體脂肪 - Body Freckles + 身體雀斑 - Body Thick + 寬厚體型 - Body Thickness + 體型厚度 - Body Thin + 窄瘦體型 - Bow Legged + 弓形腿 - Breast Buoyancy + 乳房彈性 - Breast Cleavage + 乳溝深淺 - Breast Size + 乳房尺寸 - Bridge Width + 兩眼間距 - Broad + 寬 - Brow Size + 眉毛大小 - Bug Eyes + 突眼 - Bugged Eyes + 突眼 - Bulbous + 球狀 - Bulbous Nose + 球狀鼻 - Breast Mass + 乳房質量 - Breast Smoothing + 乳房增圓滑 - Breast Gravity + 乳房重力特性 - Breast Drag + 乳房阻力 - Max Effect + 最大效果 - Spring + 彈跳 - Gain + 增益 - Damping + 阻尼 - Max Effect + 最大效果 - Spring + 彈跳 - Gain + 增益 - Damping + 阻尼 - Max Effect + 最大效果 - Spring + 彈跳 - Gain + 增益 - Damping + 阻尼 - Belly Mass + 腹部質量 - Belly Smoothing + 腹部增圓滑 - Belly Gravity + 腹部重力特性 - Belly Drag + 腹部阻力 - Max Effect + 最大效果 - Spring + 彈跳 - Gain + 增益 - Damping + 阻尼 - Butt Mass + 臀部質量 - Butt Smoothing + 臀部增圓滑 - Butt Gravity + 臀部重力特性 - Butt Drag + 臀部阻力 - Max Effect + 最大效果 - Spring + 彈跳 - Gain + 增益 - Damping + 阻尼 - Max Effect + 最大效果 - Spring + 彈跳 - Gain + 增益 - Damping + 阻尼 - Bushy Eyebrows + 濃眉 - Bushy Hair + 濃密頭髮 - Butt Size + 臀部大小 - Butt Gravity + 臀部重力特性 - Bustle Skirt + 側皺過膝長裙 - No Bustle + 無側皺 - More Bustle + 增加側皺 Chaplin - Cheek Bones + 顴骨 - Chest Size + 胸部大小 下巴角度 - Chin Cleft + 下巴裂度 - Chin Curtains + 絡腮鬍 - Chin Depth + 下巴深度 - Chin Heavy + 下巴厚重 - Chin In + 下巴後縮 - Chin Out + 下巴突出 - Chin-Neck + 下巴-頸部 清除 - Cleft + 分裂 - Close Set Eyes + 雙眼靠近 - Closed + 閉合 - Closed Back + 後閉合 - Closed Front + 前閉合 - Closed Left + 左閉合 - Closed Right + 右閉合 - Coin Purse + 小錢包 - Collar Back + 後 Collar - Collar Front + 前 Collar - Corner Down + 角落朝下 - Corner Up + 角落朝上 - Creased + 皺褶 - Crooked Nose + 彎曲鼻 - Cuff Flare + 袖口裝飾 - Dark + 深暗 - Dark Green + 深綠 - Darker + 更深暗 - Deep + 深 - Default Heels + 預設高跟鞋 - Dense + 稠密 - Double Chin + 雙下巴 - Downturned + 嘴角下垂 - Duffle Bag + 旅行袋 - Ear Angle + 耳朵角度 - Ear Size + 耳朵大小 - Ear Tips + 耳端 - Egg Head + 蛋形頭 - Eye Bags + 眼袋 - Eye Color + 眼睛顏色 - Eye Depth + 眼睛深度 - Eye Lightness + 眼睛亮度 - Eye Opening + 眼睛垂直大小 - Eye Pop + 眼睛 Pop - Eye Size + 眼睛大小 - Eye Spacing + 雙眼間距 - Eyebrow Arc + 眉毛弧度 - Eyebrow Density + 眉毛密度 - Eyebrow Height + 眉毛高度 - Eyebrow Points + 眉毛點 - Eyebrow Size + 眉毛尺寸 - Eyelash Length + 睫毛長度 - Eyeliner + 眼線筆 - Eyeliner Color + 眼影筆顏色 - Eyes Bugged + 突眼 - Face Shear + 臉部偏移 - Facial Definition + 臉部結實度 - Far Set Eyes + 雙眼距離遠 - Fat Lips + 厚脣 - Female + 女性 - Fingerless + 無手指 - Fingers + 手指 - Flared Cuffs + 帶裝飾袖口 - Flat + 平 - Flat Butt + 臀部扁平 - Flat Head + 頭部扁平 - Flat Toe + 腳趾扁平 - Foot Size + 腳部大小 - Forehead Angle + 前額角度 - Forehead Heavy + 前額寬厚 - Freckles + 雀斑 - Front Fringe + 前瀏海 - Full Back + 後部飽滿 - Full Eyeliner + 厚重眼線 - Full Front + 前面飽滿 - Full Hair Sides + 側髮飽滿 - Full Sides + 兩側飽滿 - Glossy + 光亮 - Glove Fingers + 手套手指 - Glove Length + 手套長度 頭髮 - Hair: Back + 頭髮:後面 - Hair: Front + 頭髮:前面 - Hair: Sides + 頭髮:側面 - Hair Sweep + 頭髮垂擺 - Hair Thickness + 頭髮厚度 - Hair Thickness + 頭髮厚度 - Hair Tilt + 頭髮傾度 - Hair Tilted Left + 頭髮傾左 - Hair Tilted Right + 頭髮傾右 - Hair: Volume + 頭髮:髮量 - Hand Size + 手部大小 - Handlebars + 把手 - Head Length + 頭部長度 - Head Shape + 頭型 - Head Size + 頭部大小 - Head Stretch + 頭部延展度 - Heel Height + 腳踵高度 - Heel Shape + 腳踵形狀 - Height + 高度 - High + 高 - High Heels + 高跟鞋 - High Jaw + 高顎 - High Platforms + 高平臺 - High and Tight + 高而緊緻 - Higher + 更高 - Hip Length + 腰長 - Hip Width + 腰寬 - In + 向內 - Inner Shadow Color + 內陰影顏色 - Inner Shadow Opacity + 內陰影不透明度 - Inner Eye Corner + 眼內角 - Inner Eye Shadow + 內眼陰影 - Inner Shadow + 內陰影 - Jacket Length + 外套長度 - Jacket Wrinkles + 外套皺褶 - Jaw Angle + 顎角度 - Jaw Jut + 顎突出 - Jaw Shape + 顎形 - Join + 加入 - Jowls + 下頜 - Knee Angle + 膝部角度 - Knock Kneed + 八字腿 - Large + 大 - Large Hands + 大手掌 - Left Part + 左邊分 - Leg Length + 腿長 - Leg Muscles + 腿肌肉 - Less + 更少 - Less Body Fat + 體脂肪較少 - Less Curtains + 絡腮鬍較短 - Less Freckles + 雀斑較少 - Less Full + 較不飽滿 - Less Gravity + 較少重力 - Less Love + 少一點愛 - Less Muscles + 少一點肌肉 - Less Muscular + 少一點肌壯感 - Less Rosy + 少一點紅潤 - Less Round + 少一點圓度 - Less Saddle + 腿靠攏一點 - Less Square + 少一點方形 - Less Volume + 少一點量 - Less soul + 少一點靈魂 - Lighter + 亮一點 - Lip Cleft + 脣裂度 - Lip Cleft Depth + 脣裂深度 - Lip Fullness + 脣豐度 - Lip Pinkness + 脣色粉紅度 - Lip Ratio + 脣比率 - Lip Thickness + 脣厚度 - Lip Width + 脣寬度 - Lipgloss + 脣蜜 - Lipstick + 脣膏 - Lipstick Color + 脣膏顏色 - Long + 長 - Long Head + 長臉 - Long Hips + 高腰 - Long Legs + 長腿 - Long Neck + 長頸 - Long Pigtails + 長辮子 - Long Ponytail + 長馬尾辮 - Long Torso + 長軀幹 - Long arms + 長臂 - Loose Pants + 寬鬆褲子 - Loose Shirt + 寬鬆襯衫 - Loose Sleeves + 寬鬆袖子 - Love Handles + 腰間贅肉 - Low + 低 - Low Heels + 低跟鞋 - Low Jaw + 低顎 - Low Platforms + 低平臺 - Low and Loose + 低而寬鬆 - Lower + 降低 - Lower Bridge + 鼻樑低一點 - Lower Cheeks + 雙頰低一點 男性 - Middle Part + 中間邊分 更多 - More Blush + 增加腮紅 - More Body Fat + 增加體脂肪 - More Curtains + 增加絡腮鬍 - More Eyeshadow + 增加眼影 - More Freckles + 增加雀斑 - More Full + 更飽滿 - More Gravity + 增加重力 - More Lipstick + 多一點脣膏 - More Love + 多一點愛 - More Lower Lip + 下脣更豐滿 - More Muscles + 多一點肌肉 - More Muscular + 增加肌壯感 - More Rosy + 更加紅潤 - More Round + 增加圓度 - More Saddle + 腿更加張開 - More Sloped + 更加傾斜 - More Square + 增加方形 - More Upper Lip + 上脣更豐滿 - More Vertical + 更加垂直 - More Volume + 多一點量 - More soul + 多一點靈魂 - Moustache + 髭鬍 - Mouth Corner + 嘴角 - Mouth Position + 嘴巴位置 - Mowhawk + 莫霍克髮型 - Muscular + 肌肉發達 - Mutton Chops + 羊排式絡腮鬍 - Nail Polish + 指甲油 - Nail Polish Color + 指甲油顏色 - Narrow + 窄 - Narrow Back + 後窄 - Narrow Front + 前窄 - Narrow Lips + 窄脣 - Natural + 自然 - Neck Length + 頸長 - Neck Thickness + 頸部厚度 - No Blush + 無腮紅 - No Eyeliner + 無眼線 - No Eyeshadow + 無眼影 - No Lipgloss + 無脣蜜 - No Lipstick + 無脣膏 - No Part + 無邊分 - No Polish + 無指甲油 - No Red + 去紅色 - No Spikes + 無尖直形 - No White + 去白色 - No Wrinkles + 無皺紋 - Normal Lower + 正常下半 - Normal Upper + 正常上半 - Nose Left + 左鼻 - Nose Right + 右鼻 - Nose Size + 鼻部大小 - Nose Thickness + 鼻子厚度 - Nose Tip Angle + 鼻尖角度 - Nose Tip Shape + 鼻尖形狀 - Nose Width + 鼻寬 - Nostril Division + 鼻孔分開度 - Nostril Width + 鼻孔寬 - Opaque + 不透明 - Open + 打開 - Open Back + 後開 - Open Front + 前開 - Open Left + 左開 - Open Right + 右開 - Orange + 橘色 - Out + 外 - Outer Shadow Color + 外陰影顏色 - Outer Shadow Opacity + 外陰影不透明度 - Outer Eye Corner + 眼外角 - Outer Eye Shadow + 外眼陰影 - Outer Shadow + 外陰影 - Overbite + 齙牙 - Package + 配套 - Painted Nails + 美甲 - Pale + 蒼白 - Pants Crotch + 褲襠 - Pants Fit + 褲子合身度 - Pants Length + 褲長 - Pants Waist + 褲腰 - Pants Wrinkles + 褲子皺褶 - Part + 邊分 - Part Bangs + 邊分瀏海 - Pectorals + 胸肌 - Pigment + 色素 - Pigtails + 辮子 - Pink + 粉紅 - Pinker + 增加粉紅 - Platform Height + 平臺高度 - Platform Width + 平臺寬度 - Pointy + 尖狀 - Pointy Heels + 尖狀高跟鞋 - Ponytail + 馬尾辮 - Poofy Skirt + 蓬蓬裙 - Pop Left Eye + 左眼 Pop - Pop Right Eye + 右眼 Pop - Puffy + 眼腫 - Puffy Eyelids + 眼瞼腫 - Rainbow Color + 虹彩 - Red Hair + 紅髮 - Regular + 一般 - Right Part + 右邊分 - Rosy Complexion + 臉色紅潤 - Round + 圓 - Ruddiness + 紅潤度 - Ruddy + 紅潤 - Rumpled Hair + 亂髮 - Saddle Bags + 馬鞍型工具袋 - Scrawny Leg + 削瘦的腿 - Separate + 分開 - Shallow + 淺 - Shear Back + 後偏移 - Shear Face + 臉部偏移 - Shear Front + 前偏移 - Shear Left Up + 左上偏移 - Shear Right Up + 右上偏移 - Sheared Back + 後偏移 - Sheared Front + 前偏移 - Shift Left + 左移 - Shift Mouth + 嘴部偏移 - Shift Right + 右移 - Shirt Bottom + 襯衫底 - Shirt Fit + 襯衫合身度 - Shirt Wrinkles + 襯衫皺褶 - Shoe Height + 鞋高 - Short + 短 - Short Arms + 短臂 - Short Legs + 短腿 - Short Neck + 短頸 - Short Pigtails + 短辮子 - Short Ponytail + 短馬尾辮 - Short Sideburns + 短鬢鬚 - Short Torso + 短軀幹 - Short hips + 短腰 - Shoulders + 肩膀 - Side Fringe + 側瀏海 - Sideburns + 鬢鬚 - Sides Hair + 側面頭髮 - Sides Hair Down + 側面頭髮下部 - Sides Hair Up + 側面頭髮上部 - Skinny Neck + 削瘦頸部 - Skirt Fit + 裙子合身度 - Skirt Length + 裙長 - Slanted Forehead + 斜前額 - Sleeve Length + 袖長 - Sleeve Looseness + 袖子寬鬆度 - Slit: Back + 後分 - Slit: Front + 前分 - Slit: Left + 左分 - Slit: Right + 右分 - Small + 小 - Small Hands + 小手 - Small Head + 小頭 - Smooth + 平滑 - Smooth Hair + 平滑頭髮 - Socks Length + 襪長 - Soulpatch + 脣下撮鬍 - Sparse + 稀疏 - Spiked Hair + 尖直頭髮 - Square + 方形 - Square Toe + 平頭鞋 - Squash Head + 長瓜形臉 - Stretch Head + 延展頭部 - Sunken + 深陷 - Sunken Chest + 胸部內陷 - Sunken Eyes + 眼睛深陷 - Sweep Back + 後垂擺 - Sweep Forward + 前垂擺 - Tall + 高 - Taper Back + 後漸細 - Taper Front + 前漸細 - Thick Heels + 粗腳跟 - Thick Neck + 粗頸 - Thick Toe + 粗腳趾 - Thin + 瘦 - Thin Eyebrows + 細眉 - Thin Lips + 細脣 - Thin Nose + 細鼻 - Tight Chin + 緊緻下巴 - Tight Cuffs + 緊緻袖口 - Tight Pants + 緊緻褲子 - Tight Shirt + 緊緻襯衫 - Tight Skirt + 緊緻裙子 - Tight Sleeves + 緊緻袖子 - Toe Shape + 腳趾形狀 - Toe Thickness + 腳趾粗細 - Torso Length + 軀幹長度 - Torso Muscles + 軀幹肌肉 - Torso Scrawny + 軀幹削瘦 - Unattached + 未附著 - Uncreased + 無皺痕 - Underbite + 戽斗 - Unnatural + 不自然 - Upper Bridge + 上鼻梁 - Upper Cheeks + 上臉頰 - Upper Chin Cleft + 上部顎裂 - Upper Eyelid Fold + 上眼瞼皺褶 - Upturned + 嘴角上翹 - Very Red + 大紅 - Waist Height + 腰高 - Well-Fed + 飽食的 - White Hair + 白髮 - Wide + 寬 - Wide Back + 後寬 - Wide Front + 前寬 - Wide Lips + 寬脣 - Wild + 狂野 - Wrinkles + 皺紋 添加到我的地標 @@ -3595,7 +3729,7 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. [APP_NAME] 安裝中... - Your [APP_NAME] Viewer is being updated to the latest release. This may take some time, so please be patient. + 你的 [APP_NAME] Viewer 正在更新為最新發行版。 可能要等一會兒,請耐心稍候。 更新下載中... @@ -3607,7 +3741,7 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. 夏載更新失敗 - An error occurred while updating [APP_NAME]. Please download the latest version from www.secondlife.com. + [APP_NAME] 更新時出錯。 請到 www.secondlife.com 下載最新版本。 安裝更新失敗 @@ -3616,13 +3750,13 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. 啟動瀏覽器失敗 - [APP_NAME]: Items coming in too fast from [FROM_NAME], automatic preview disabled for [TIME] seconds + [APP_NAME]:來自 [FROM_NAME] 的項目速度過快,因此自動預覽暫停 [TIME] 秒 - [APP_NAME]: Items coming in too fast, automatic preview disabled for [TIME] seconds + [APP_NAME]:進來的項目速度過快,因此自動預覽暫停 [TIME] 秒 - -- Instant message logging enabled -- + -- 已開啟及時訊息記錄 -- [NAME] 正在輸入... @@ -3631,43 +3765,43 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. (未命名) - (Moderated: Voices off by default) + (有人主持:預設關閉語音) - Text chat is not available for this call. + 這次通話無法使用文字聊天。 - Your text chat has been disabled by a Group Moderator. + 群組主持人已禁止你進行文字聊天。 - 點擊此處以傳送即時訊息。 + 點按此處以傳送即時訊息。 - (Moderator) + (主持人) - (Saved [LONG_TIMESTAMP]) + (於 [LONG_TIMESTAMP] 儲存) - Your call has been answered + 你的通話已經接通 - You started a voice call + 你發起了語音通話 - You joined the voice call + 你發起了語音通話 - [NAME] started a voice call + [NAME] 發起了語音通話 加入語音通話... - Connected, click Leave Call to hang up + 已接通,要掛斷請按「離開通話」 離開語音通話 @@ -3676,40 +3810,40 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. 聯接中... - Ad-hoc Conference + 臨時多方通話 - Conference with [AGENT_NAME] + 和 [AGENT_NAME] 多方通話 - Inventory item offered + 收納區物品已發送 - Drag items from inventory here + 將收納區物品拖曳到這裡 (IM 會話不存在) - You are the only user in this session. + 這次會話只有你一人。 [NAME] 離線。 - Click the [BUTTON NAME] button to accept/connect to this voice chat. + 點按 [BUTTON NAME] 按鈕接受並連通到這個語音聊天。 - You have blocked this Resident. Sending a message will automatically unblock them. + 你已經封鎖這位居民。 發送訊息將會自動解除封鎖。 - Error making request, please try again later. + 送出請求時出錯,請稍候再試。 - Error making request, please try again later. + 送出請求時出錯,請稍候再試。 - You do not have sufficient permissions. + 你權限不足。 此會話不再存在 @@ -3721,25 +3855,25 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. 你並不具有這個能力。 - You are not a session moderator. + 你不具會話主持人身份。 - A group moderator disabled your text chat. + 某群組主持人禁止了你的文字聊天。 - A group moderator disabled your text chat. + 某群組主持人禁止了你的文字聊天。 - Unable to add users to chat session with [RECIPIENT]. + 無法新增使用者加入與 [RECIPIENT] 的聊天會話。 - Unable to send your message to the chat session with [RECIPIENT]. + 你的訊息無法傳送給與 [RECIPIENT] 的聊天會話。 - Unable to send your message to the chat session with [RECIPIENT]. + 你的訊息無法傳送給與 [RECIPIENT] 的聊天會話。 - Error while moderating. + 主持期間發生錯誤。 你已經由群組中被移除。 @@ -3748,49 +3882,52 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. 你已經由群組中被移除。 - You no longer have the ability to be in the chat session. + 你已無法進入這個聊天會話。 - [SOURCES] has said something new + [SOURCES] 又說了一些話 - [SOURCES] have said something new + [SOURCES] 又說了一些話 - The session initialization is timed out + 會話初始化逾時,無法完成 + + + 我的家位置已定。 http://secondlife.com/landing/voicemorphing - [NAME] paid you L$[AMOUNT] [REASON]. + [NAME] 支付你 L$[AMOUNT]([REASON])。 - [NAME] paid you L$[AMOUNT]. + [NAME] 支付你 L$[AMOUNT]。 - You paid [NAME] L$[AMOUNT] [REASON]. + 你支付 L$[AMOUNT] 給 [NAME]([REASON])。 - You paid L$[AMOUNT]. + 你支付了 L$[AMOUNT]。 - You paid [NAME] L$[AMOUNT]. + 你支付 L$[AMOUNT] 給 [NAME]。 - You paid L$[AMOUNT] [REASON]. + 你支付了 L$[AMOUNT]([REASON])。 - for [ITEM] + 購買 [ITEM] - for a parcel of land + 購買一土地地段 - for a land access pass + 購買土地出入通行權 - for deeding land + 讓渡土地 以創造群組 @@ -3802,31 +3939,31 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. 以上傳 - to publish a classified ad + 發布一則個人廣告 - Giving L$ [AMOUNT] + 捐贈 L$ [AMOUNT] 上傳花費 L$ [AMOUNT] - This costs L$ [AMOUNT] + 本次花費 L$ [AMOUNT] - Buying selected land for L$ [AMOUNT] + 花費 L$ [AMOUNT] 購買所選土地 - This object costs L$ [AMOUNT] + 本物件價格 L$ [AMOUNT] 任何人 - Officers + 職員 - 擁有者 + 所有人 上線 @@ -3834,7 +3971,7 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. 上傳中... -舉報濫用 +違規舉報 新體形 @@ -3861,7 +3998,7 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. 新襪子 - 新夾克 + 新外套 新手套 @@ -3912,7 +4049,7 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. 女性姿勢 - 其他姿勢 + 其他���勢 演說姿勢 @@ -3921,91 +4058,91 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. 一般姿勢 - Male - Excuse me + 男性 - Excuse me - Male - Get lost + Male - 給我滾蛋! - Male - Blow kiss + 男性 - 飛吻 - Male - Boo + 男性 - 我噓你 - Male - Bored + 男性 - 無聊狀 - Male - Hey + 男性 - 嘿! - Male - Laugh + 男性 - 笑 - Male - Repulsed + 男性 - 作噁 - Male - Shrug + 男性 - 聳聳肩 - Male - Stick tougue out + 男性 - 吐舌頭 - Male - Wow + 男性 - 哇塞 - Female - Chuckle + 女性 - 咯咯笑 - Female - Cry + 女性 - 哭 - Female - Embarrassed + 女性 - 尷尬 - Female - Excuse me + 男性 - Excuse me - Female - Get lost + 女性 - 給我滾蛋! - Female - Blow kiss + 女性 - 飛吻 - Female - Boo + 女性 - 我噓你 - Female - Bored + 女性 - 無聊狀 - Female - Hey + 女性 - 嘿! - Female - Hey baby + 女性 - 嘿,寶貝 - Female - Laugh + 女性 - 笑 - Female - Looking good + 女性 - 看我美不美 - Female - Over here + 女性 - 我在這兒 - Female - Please + 女性 - 請 - Female - Repulsed + 女性 - 作噁 - Female - Shrug + 女性 - 聳聳肩 - Female - Stick tougue out + 女性 - 吐舌頭 - Female - Wow + 女性 - 哇塞 [mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt] @@ -4018,34 +4155,34 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. - Despite our best efforts, something unexpected has gone wrong. + 儘管我們努力避免,還是發生意料外的錯誤。 - Please check status.secondlifegrid.net to see if there is a known problem with the service. - If you continue to experience problems, please check your network and firewall setup. + 請察訪 status.secondlifegrid.net 看是否發生了已知狀況。 + 如果文體繼續發生,請檢查你的網路和防火牆設定。 - Sunday:Monday:Tuesday:Wednesday:Thursday:Friday:Saturday + 星期日:星期一:星期二:星期三:星期四:星期五:星期六 - Sun:Mon:Tue:Wed:Thu:Fri:Sat + 星期日:星期一:星期二:星期三:星期四:星期五:星期六 - January:February:March:April:May:June:July:August:September:October:November:December + 一月:二月:三月:四月:五月:六月:七月:八月:九月:十月:十一月:十二月 - Jan:Feb:Mar:Apr:May:Jun:Jul:Aug:Sep:Oct:Nov:Dec + 一月:二月:三月:四月:五月:六月:七月:八月:九月:十月:十一月:十二月 [MDAY] - AM + 上午 - PM + 下午 - US$ [AMOUNT] + $ [AMOUNT] 美元 成員資格 @@ -4054,25 +4191,25 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. 角色 - Group Identity + 群組身份 地段管理 - Parcel Identity + 地段名 地段設定 - Parcel Powers + 地段權利 - Parcel Access + 地段出入 - Parcel Content + 地段內容 物件管理 @@ -4090,7 +4227,7 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. 刪除所選取的物品? - 刪除所選取的物品? + 刪除所選取的物���? 沒有任何物品在這個裝扮內 @@ -4099,72 +4236,72 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. 選擇一個編輯器使用 ExternalEditor 設定。 - Cannot find the external editor you specified. -Try enclosing path to the editor with double quotes. -(e.g. "/path to my/editor" "%s") + 找不到你指定的外部編輯器。 +請嘗試在編輯器路經前後加上英文雙括號。 +(例:"/path to my/editor" "%s") - Error parsing the external editor command. + 解析外部編輯器指令時出錯。 執行外部編輯器失敗。 - Esc + Esc 鍵 - Space + 空間鍵 - Enter + Enter 鍵 - Tab + Tab 鍵 - Ins + Ins 鍵 - Del + Del 鍵 - Backsp + Backspace 鍵 - Shift + Shift 鍵 - Ctrl + Ctrl 鍵 - Alt + Alt 鍵 - CapsLock + CapsLock 鍵 - Left + 左移鍵 - Right + 右移鍵 - Up + 上移鍵 - Down + 下移鍵 - Home + Home 鍵 - End + End 鍵 - PgUp + PgUp 鍵 - PgDn + PgDn 鍵 F1 @@ -4203,16 +4340,16 @@ Try enclosing path to the editor with double quotes. F12 - Add + 添加 - Subtract + 減 - Multiply + 乘 - Divide + 除 PAD_DIVIDE @@ -4392,7 +4529,7 @@ Try enclosing path to the editor with double quotes. M - N + 北 O @@ -4407,7 +4544,7 @@ Try enclosing path to the editor with double quotes. R - S + 南 T @@ -4419,7 +4556,7 @@ Try enclosing path to the editor with double quotes. V - W + 西 X @@ -4431,24 +4568,24 @@ Try enclosing path to the editor with double quotes. Z - Viewing particle beacons (blue) + 檢視粒子效果的導引(藍色) - Viewing physical object beacons (green) + 檢視具體物件的導引(綠色) - Viewing scripted object beacons (red) + 檢視腳本物件的導引(紅色) - Viewing scripted object with touch function beacons (red) + 檢視帶有觸摸函式腳本物件的導引(紅色) - Viewing sound beacons (yellow) + 檢視聲音的導引(黃色) - Viewing media beacons (white) + 檢視媒體的導引(白色) - Hiding Particles + 隱藏粒子效果 -- cgit v1.2.3 From 76333248a8f4cc28512099fc6cafe0881e67edb5 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 10 Oct 2012 09:04:31 -0400 Subject: #include in v3color.h: recent Linux compilers care. Linux dev builds have started breaking because v3color.h uses strlen() and strncpy() without declaring them. Add relevant #include, which fixes. --- indra/llmath/v3color.h | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/llmath/v3color.h b/indra/llmath/v3color.h index 56cb2ae73e..daf3a6857b 100644 --- a/indra/llmath/v3color.h +++ b/indra/llmath/v3color.h @@ -33,6 +33,7 @@ class LLVector4; #include "llerror.h" #include "llmath.h" #include "llsd.h" +#include // LLColor3 = |r g b| -- cgit v1.2.3 From e97c06cf0c1b7bc81d787b54d98b8870da22166c Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 10 Oct 2012 10:45:37 -0400 Subject: On lluuidhashmap_tut<1> failure, save/reload data in temp file. Generating new random data on every test run makes it impossible to debug a test failure. While in general we do want to generate random data to thoroughly exercise the generator logic, if every new run generates new data, the only thing we can do about an observed failure is shrug and ignore it. Add logic to save data on failure, with corresponding logic to notice and reload from a previously-generated save file. In case of a merge collision, this version SUPERCEDES my previous efforts with this file. (My other changes may still be in a backed-up merge request.) It is okay to resolve collisions in favor of this version. --- indra/test/lluuidhashmap_tut.cpp | 141 +++++++++++++++++++++++++++++++++------ 1 file changed, 119 insertions(+), 22 deletions(-) diff --git a/indra/test/lluuidhashmap_tut.cpp b/indra/test/lluuidhashmap_tut.cpp index 0544e832ce..408bc3faf1 100644 --- a/indra/test/lluuidhashmap_tut.cpp +++ b/indra/test/lluuidhashmap_tut.cpp @@ -30,6 +30,10 @@ #include "linden_common.h" #include "lluuidhashmap.h" #include "llsdserialize.h" +#include "lldir.h" +#include "stringize.h" +#include +#include namespace tut { @@ -79,40 +83,133 @@ namespace tut template<> template<> void hash_index_object_t::test<1>() { - LLUUIDHashMap hashTable(UUIDTableEntry::uuidEq, UUIDTableEntry()); + set_test_name("stress test"); + // As of 2012-10-10, I (nat) have observed sporadic failures of this + // test: "set/get did not work." The trouble is that since test data + // are randomly generated with every run, it is impossible to debug a + // test failure. One is left with the uneasy suspicion that + // LLUUID::generate() can sometimes produce duplicates even within the + // moderately small number requested here. Since rerunning the test + // generally allows it to pass, it's too easy to shrug and forget it. + // The following code is intended to support reproducing such test + // failures. The idea is that, on test failure, we save the generated + // data to a canonical filename in a temp directory. Then on every + // subsequent run, we check for that filename. If it exists, we reload + // that specific data rather than generating fresh data -- which + // should presumably reproduce the same test failure. But we inform + // the user that to resume normal (random) test runs, s/he need only + // delete that file. And since it's in a temp directory, sooner or + // later the system will clean it up anyway. + const char* tempvar = "TEMP"; + const char* tempdir = getenv(tempvar); // Windows convention + if (! tempdir) + { + tempvar = "TMPDIR"; + tempdir = getenv(tempvar); // Mac convention + } + if (! tempdir) + { + // reset tempvar to the first var we check; it's just a + // recommendation + tempvar = "TEMP"; + tempdir = "/tmp"; // Posix in general + } + std::string savefile(gDirUtilp->add(tempdir, "lluuidhashmap_tut.save.txt")); const int numElementsToCheck = 32*256*32; - std::vector idList(numElementsToCheck); - int i; - - for (i = 0; i < numElementsToCheck; i++) + std::vector idList; + if (gDirUtilp->fileExists(savefile)) { - LLUUID id; - id.generate(); - UUIDTableEntry entry(id, i); - hashTable.set(id, entry); - idList[i] = id; + // We have saved data from a previous failed run. Reload that data. + std::ifstream inf(savefile.c_str()); + if (! inf.is_open()) + { + fail(STRINGIZE("Although save file '" << savefile << "' exists, it cannot be opened")); + } + std::string item; + while (std::getline(inf, item)) + { + idList.push_back(LLUUID(item)); + } + std::cout << "Reloaded " << idList.size() << " items from '" << savefile << "'"; + if (idList.size() != numElementsToCheck) + { + std::cout << " (expected " << numElementsToCheck << ")"; + } + std::cout << " -- delete this file to generate new data" << std::endl; + } + else + { + // savefile does not exist (normal case): regenerate idList from + // scratch. + for (int i = 0; i < numElementsToCheck; ++i) + { + LLUUID id; + id.generate(); + idList.push_back(id); + } } - for (i = 0; i < numElementsToCheck; i++) + LLUUIDHashMap hashTable(UUIDTableEntry::uuidEq, UUIDTableEntry()); + int i; + + for (i = 0; i < idList.size(); ++i) { - LLUUID idToCheck = idList[i]; - UUIDTableEntry entryToCheck = hashTable.get(idToCheck); - ensure("set/get did not work", entryToCheck.getID() == idToCheck && entryToCheck.getValue() == (size_t)i); + UUIDTableEntry entry(idList[i], i); + hashTable.set(idList[i], entry); } - for (i = 0; i < numElementsToCheck; i++) + try { - LLUUID idToCheck = idList[i]; - if (i % 2 != 0) + for (i = 0; i < idList.size(); i++) { - hashTable.remove(idToCheck); + LLUUID idToCheck = idList[i]; + UUIDTableEntry entryToCheck = hashTable.get(idToCheck); + ensure_equals(STRINGIZE("set/get ID (entry " << i << ")").c_str(), + entryToCheck.getID(), idToCheck); + ensure_equals(STRINGIZE("set/get value (ID " << idToCheck << ")").c_str(), + entryToCheck.getValue(), (size_t)i); } - } - for (i = 0; i < numElementsToCheck; i++) + for (i = 0; i < idList.size(); i++) + { + LLUUID idToCheck = idList[i]; + if (i % 2 != 0) + { + hashTable.remove(idToCheck); + } + } + + for (i = 0; i < idList.size(); i++) + { + LLUUID idToCheck = idList[i]; + ensure("remove or check did not work", (i % 2 == 0 && hashTable.check(idToCheck)) || (i % 2 != 0 && !hashTable.check(idToCheck))); + } + } + catch (const failure&) { - LLUUID idToCheck = idList[i]; - ensure("remove or check did not work", (i % 2 == 0 && hashTable.check(idToCheck)) || (i % 2 != 0 && !hashTable.check(idToCheck))); + // One of the above tests failed. Try to save idList to repro with + // a later run. + std::ofstream outf(savefile.c_str()); + if (! outf.is_open()) + { + // Sigh, don't use fail() here because we want to preserve + // the original test failure. + std::cout << "Cannot open file '" << savefile + << "' to save data -- check and fix " << tempvar << std::endl; + } + else + { + // outf.is_open() + for (int i = 0; i < idList.size(); ++i) + { + outf << idList[i] << std::endl; + } + std::cout << "Saved " << idList.size() << " entries to '" << savefile + << "' -- rerun test to debug with these" << std::endl; + } + // re-raise the same exception -- we WANT this test failure to + // be reported! We just needed to save the data on the way out. + throw; } } -- cgit v1.2.3 From 3653727e7f84f10caefb6ea7dc33859455ebfa0b Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 10 Oct 2012 14:57:43 -0400 Subject: Introduce new LLDir::findSkinnedFilenames() method. Use as needed. In a number of different places, for different reasons, the viewer wants to load a UI-related file that might be overridden by a non-default skin; and within that skin, might further be overridden by a non-default language. Apparently, for each of those use cases, every individual developer approached it as an entirely new problem, solving it idiosyncratically for that one case. Not only is this a maintenance problem, but it rubs one's nose in the fact that most such solutions consider only a subset of the relevant skin directories. Richard and I evolved an API intended to address all such cases: a central LLDir method returning a list of relevant pathnames, from most general to most localized, filtered to present only existing files; plus a couple of convenience methods to specifically obtain the most general and most localized available file. There were several load-skinned-file methods (LLFloater::buildFromFile(), LLPanel::buildFromFile() and LLUICtrlFactory::createFromFile() -- apparently cloned-and-modified from each other) that contained funky bolted-on logic to output the loaded data to an optional passed LLXMLNodePtr param. The trouble is that passing that param forced each of these methods to subvert its normal search: specifically for that case, it needed to find the baseline XML file instead of the localized one. Richard agreed that for the intended usage (reformatting XML files) we should use XML schema instead, and that the hacky functionality should be removed. Remove it. Also remove LLUICtrlFactory::getLocalizedXMLNode(), only used for those three special cases. Some callers explicitly passed the optional LLXMLNodePtr param as NULL. Remove that. Remove LLFloaterUIPreview::displayFloater(save) param, which relied on the optional output LLXMLNodePtr param. Make onClickSaveFloater() and onClickSaveAll() emit popupAndPrintWarning() about discontinued functionality. Recast LLFloater::buildFromFile(), LLPanel::buildFromFile(), LLUICtrlFactory::createFromFile(), LLNotifications::loadTemplates(), LLUI::locateSkin(), LLFontRegistry::parseFontInfo(), LLUIColorTable::loadFromSettings(), LLUICtrlFactory::loadWidgetTemplate(), LLUICtrlFactory::getLayeredXMLNode(), LLUIImageList::initFromFile(), LLAppViewer::launchUpdater() and LLMediaCtrl::navigateToLocalPage() to use findSkinnedFilenames(). (Is LLAppViewer::launchUpdater() ever called any more? Apparently so -- though the linux-updater.bin logic to process the relevant command-line switch has been disabled. Shrug.) (Is LLMediaCtrl::navigateToLocalPage() ever used?? If so, why?) Remove LLUI::setupPaths(), getXUIPaths(), getSkinPath() and getLocalizedSkinPath(). Remove the skins/paths.xml file read by setupPaths(). The only configuration it contained was the pair of partial paths "xui/en" and "xui/[LANGUAGE]" -- hardly likely to change. getSkinPath() specifically returned the first of these, while getLocalizedSkinPath() specifically returned the second. This knowledge is now embedded in findSkinnedFilenames(). Also remove paths.xml from viewer_manifest.py. Remove injected xui_paths from LLFontGL::initClass() and LLFontRegistry::LLFontRegistry(). These are no longer needed since LLFontRegistry can now directly consult LLDir for its path search. Stop passing LLUI::getXUIPaths() to LLFontGL::initClass() in LLViewerWindow's constructor and initFonts() method. Add LLDir::append() and add() methods for the simple task of combining two path components separated by getDirDelimiter() -- but only if they're both non-empty. Amazing how often that logic is replicated. Replace some existing concatenations with add() or append(). New LLDir::findSkinnedFilenames() method must know current language. Allow injecting current language by adding an LLDir::setSkinFolder(language) param, and pass it where LLAppViewer::init() and initConfiguration() currently call setSkinFolder(). Also add LLDir::getSkinFolder() and getLanguage() methods. Change LLFLoaterUIPreview's LLLocalizationResetForcer helper to "forcibly reset language" using LLDir::setSkinFolder() instead of LLUI::setupPaths(). Update LLDir stubs in lldir_stub.cpp and llupdaterservice_test.cpp. Add LLDir::getUserDefaultSkinDir() to obtain often-overlooked possible skin directory -- like getUserSkinDir() but with "default" in place of the current skin name as the last path component. (However, we hope findSkinnedFilenames() obviates most explicit use of such individual skin directory pathnames.) Add LLDir unit tests for new findSkinnedFilenames() and add() methods -- the latter exercises append() as well. Tweak indra/integration_tests/llui_libtest/llui_libtest.cpp for all the above. Notably, comment out its export_test_floaters() function, since the essential LLFloater::buildFromFile(optional LLXMLNodePtr) functionality has been removed. This may mean that llui_libtest.cpp has little remaining value, not sure. --- .../llimage_libtest/llimage_libtest.cpp | 2 +- .../llui_libtest/llui_libtest.cpp | 29 +- indra/linux_updater/linux_updater.cpp | 2 +- indra/llrender/llfontgl.cpp | 4 +- indra/llrender/llfontgl.h | 2 +- indra/llrender/llfontregistry.cpp | 30 +- indra/llrender/llfontregistry.h | 4 +- indra/llui/llfloater.cpp | 18 +- indra/llui/llfloater.h | 2 +- indra/llui/llfloaterreg.cpp | 2 +- indra/llui/llnotifications.cpp | 25 +- indra/llui/llpanel.cpp | 18 +- indra/llui/llpanel.h | 2 +- indra/llui/llui.cpp | 87 +--- indra/llui/llui.h | 5 - indra/llui/lluicolortable.cpp | 17 +- indra/llui/lluictrlfactory.cpp | 46 +-- indra/llui/lluictrlfactory.h | 17 +- indra/llvfs/lldir.cpp | 458 ++++++++++++++++----- indra/llvfs/lldir.h | 91 +++- indra/llvfs/tests/lldir_test.cpp | 339 ++++++++++++++- indra/newview/llappviewer.cpp | 65 +-- indra/newview/lldaycyclemanager.cpp | 2 +- indra/newview/llfloateruipreview.cpp | 122 ++---- indra/newview/llhints.cpp | 4 +- indra/newview/llmediactrl.cpp | 30 +- indra/newview/llpreviewscript.cpp | 2 +- indra/newview/llsyswellwindow.cpp | 4 +- indra/newview/lltoast.cpp | 2 +- indra/newview/llviewermedia.cpp | 15 +- indra/newview/llviewertexturelist.cpp | 55 ++- indra/newview/llviewerwindow.cpp | 6 +- indra/newview/llwaterparammanager.cpp | 2 +- indra/newview/llwlparammanager.cpp | 2 +- indra/newview/skins/paths.xml | 10 - indra/newview/tests/lldir_stub.cpp | 2 +- indra/newview/viewer_manifest.py | 1 - .../updater/tests/llupdaterservice_test.cpp | 4 +- 38 files changed, 978 insertions(+), 550 deletions(-) delete mode 100644 indra/newview/skins/paths.xml diff --git a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp index 36c5b67826..034c816742 100644 --- a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp +++ b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp @@ -240,7 +240,7 @@ void store_input_file(std::list &input_filenames, const std::string LLDirIterator iter(dir, name); while (iter.next(next_name)) { - std::string file_name = dir + gDirUtilp->getDirDelimiter() + next_name; + std::string file_name = gDirUtilp->add(dir, next_name); input_filenames.push_back(file_name); } } diff --git a/indra/integration_tests/llui_libtest/llui_libtest.cpp b/indra/integration_tests/llui_libtest/llui_libtest.cpp index 217e26c3ca..38aa1bbeb2 100644 --- a/indra/integration_tests/llui_libtest/llui_libtest.cpp +++ b/indra/integration_tests/llui_libtest/llui_libtest.cpp @@ -107,12 +107,6 @@ public: }; TestImageProvider gTestImageProvider; -static std::string get_xui_dir() -{ - std::string delim = gDirUtilp->getDirDelimiter(); - return gDirUtilp->getSkinBaseDir() + delim + "default" + delim + "xui" + delim; -} - void init_llui() { // Font lookup needs directory support @@ -122,13 +116,12 @@ void init_llui() const char* newview_path = "../../../newview"; #endif gDirUtilp->initAppDirs("SecondLife", newview_path); - gDirUtilp->setSkinFolder("default"); + gDirUtilp->setSkinFolder("default", "en"); // colors are no longer stored in a LLControlGroup file LLUIColorTable::instance().loadFromSettings(); - std::string config_filename = gDirUtilp->getExpandedFilename( - LL_PATH_APP_SETTINGS, "settings.xml"); + std::string config_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "settings.xml"); gSavedSettings.loadFromFile(config_filename); // See LLAppViewer::init() @@ -143,9 +136,7 @@ void init_llui() const bool no_register_widgets = false; LLWidgetReg::initClass( no_register_widgets ); - - // Unclear if this is needed - LLUI::setupPaths(); + // Otherwise we get translation warnings when setting up floaters // (tooltips for buttons) std::set default_args; @@ -157,7 +148,6 @@ void init_llui() // otherwise it crashes. LLFontGL::initClass(96.f, 1.f, 1.f, gDirUtilp->getAppRODataDir(), - LLUI::getXUIPaths(), false ); // don't create gl textures LLFloaterView::Params fvparams; @@ -169,6 +159,14 @@ void init_llui() gFloaterView = LLUICtrlFactory::create (fvparams); } +/*==========================================================================*| +static std::string get_xui_dir() +{ + std::string delim = gDirUtilp->getDirDelimiter(); + return gDirUtilp->getSkinBaseDir() + delim + "default" + delim + "xui" + delim; +} + +// buildFromFile() no longer supports generate-output-LLXMLNode void export_test_floaters() { // Convert all test floaters to new XML format @@ -191,7 +189,7 @@ void export_test_floaters() floater->buildFromFile( filename, // FALSE, // don't open floater output_node); - std::string out_filename = xui_dir + filename; + std::string out_filename = gDirUtilp->add(xui_dir, filename); std::string::size_type extension_pos = out_filename.rfind(".xml"); out_filename.resize(extension_pos); out_filename += "_new.xml"; @@ -203,6 +201,7 @@ void export_test_floaters() fclose(floater_file); } } +|*==========================================================================*/ int main(int argc, char** argv) { @@ -211,7 +210,7 @@ int main(int argc, char** argv) init_llui(); - export_test_floaters(); +// export_test_floaters(); return 0; } diff --git a/indra/linux_updater/linux_updater.cpp b/indra/linux_updater/linux_updater.cpp index 277f0a5367..991dfd9dce 100644 --- a/indra/linux_updater/linux_updater.cpp +++ b/indra/linux_updater/linux_updater.cpp @@ -251,7 +251,7 @@ std::string next_image_filename(std::string& image_path, LLDirIterator& iter) { std::string image_filename; iter.next(image_filename); - return image_path + "/" + image_filename; + return gDirUtilp->add(image_path, image_filename); } void on_window_closed(GtkWidget *sender, GdkEvent* event, gpointer data) diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 4dc2fcd714..647512eb2e 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -789,7 +789,7 @@ const LLFontDescriptor& LLFontGL::getFontDesc() const } // static -void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, const std::vector& xui_paths, bool create_gl_textures) +void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, bool create_gl_textures) { sVertDPI = (F32)llfloor(screen_dpi * y_scale); sHorizDPI = (F32)llfloor(screen_dpi * x_scale); @@ -800,7 +800,7 @@ void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::st // Font registry init if (!sFontRegistry) { - sFontRegistry = new LLFontRegistry(xui_paths, create_gl_textures); + sFontRegistry = new LLFontRegistry(create_gl_textures); sFontRegistry->parseFontInfo("fonts.xml"); } else diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index 5ed5d2c4eb..0988e99deb 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -150,7 +150,7 @@ public: const LLFontDescriptor& getFontDesc() const; - static void initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, const std::vector& xui_paths, bool create_gl_textures = true); + static void initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, bool create_gl_textures = true); // Load sans-serif, sans-serif-small, etc. // Slow, requires multiple seconds to load fonts. diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp index 4d22eba3d9..b5bdba996f 100644 --- a/indra/llrender/llfontregistry.cpp +++ b/indra/llrender/llfontregistry.cpp @@ -163,14 +163,9 @@ LLFontDescriptor LLFontDescriptor::normalize() const return LLFontDescriptor(new_name,new_size,new_style,getFileNames()); } -LLFontRegistry::LLFontRegistry(const string_vec_t& xui_paths, - bool create_gl_textures) +LLFontRegistry::LLFontRegistry(bool create_gl_textures) : mCreateGLTextures(create_gl_textures) { - // Propagate this down from LLUICtrlFactory so LLRender doesn't - // need an upstream dependency on LLUI. - mXUIPaths = xui_paths; - // This is potentially a slow directory traversal, so we want to // cache the result. mUltimateFallbackList = LLWindow::getDynamicFallbackFontList(); @@ -183,27 +178,30 @@ LLFontRegistry::~LLFontRegistry() bool LLFontRegistry::parseFontInfo(const std::string& xml_filename) { - bool success = false; // Succeed if we find at least one XUI file - const string_vec_t& xml_paths = mXUIPaths; + bool success = false; // Succeed if we find and read at least one XUI file + const string_vec_t xml_paths = gDirUtilp->findSkinnedFilenames(LLDir::XUI, xml_filename); + if (xml_paths.empty()) + { + // We didn't even find one single XUI file + return false; + } + for (string_vec_t::const_iterator path_it = xml_paths.begin(); path_it != xml_paths.end(); ++path_it) { - LLXMLNodePtr root; - std::string full_filename = gDirUtilp->findSkinnedFilename(*path_it, xml_filename); - bool parsed_file = LLXMLNode::parseFile(full_filename, root, NULL); + bool parsed_file = LLXMLNode::parseFile(*path_it, root, NULL); if (!parsed_file) continue; - + if ( root.isNull() || ! root->hasName( "fonts" ) ) { - llwarns << "Bad font info file: " - << full_filename << llendl; + llwarns << "Bad font info file: " << *path_it << llendl; continue; } - + std::string root_name; root->getAttributeString("name",root_name); if (root->hasName("fonts")) @@ -215,7 +213,7 @@ bool LLFontRegistry::parseFontInfo(const std::string& xml_filename) } //if (success) // dump(); - + return success; } diff --git a/indra/llrender/llfontregistry.h b/indra/llrender/llfontregistry.h index 8b06191c56..059248fbbd 100644 --- a/indra/llrender/llfontregistry.h +++ b/indra/llrender/llfontregistry.h @@ -67,8 +67,7 @@ class LLFontRegistry public: // create_gl_textures - set to false for test apps with no OpenGL window, // such as llui_libtest - LLFontRegistry(const string_vec_t& xui_paths, - bool create_gl_textures); + LLFontRegistry(bool create_gl_textures); ~LLFontRegistry(); // Load standard font info from XML file(s). @@ -105,7 +104,6 @@ private: font_size_map_t mFontSizes; string_vec_t mUltimateFallbackList; - string_vec_t mXUIPaths; bool mCreateGLTextures; }; diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 8ca1e685a9..33295f882d 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -3229,24 +3229,14 @@ bool LLFloater::isVisible(const LLFloater* floater) static LLFastTimer::DeclareTimer FTM_BUILD_FLOATERS("Build Floaters"); -bool LLFloater::buildFromFile(const std::string& filename, LLXMLNodePtr output_node) +bool LLFloater::buildFromFile(const std::string& filename) { LLFastTimer timer(FTM_BUILD_FLOATERS); LLXMLNodePtr root; - //if exporting, only load the language being exported, - //instead of layering localized version on top of english - if (output_node) - { - if (!LLUICtrlFactory::getLocalizedXMLNode(filename, root)) - { - llwarns << "Couldn't parse floater from: " << LLUI::getLocalizedSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl; - return false; - } - } - else if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) + if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) { - llwarns << "Couldn't parse floater from: " << LLUI::getSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl; + llwarns << "Couldn't find (or parse) floater from: " << filename << llendl; return false; } @@ -3271,7 +3261,7 @@ bool LLFloater::buildFromFile(const std::string& filename, LLXMLNodePtr output_n getCommitCallbackRegistrar().pushScope(); getEnableCallbackRegistrar().pushScope(); - res = initFloaterXML(root, getParent(), filename, output_node); + res = initFloaterXML(root, getParent(), filename, NULL); setXMLFilename(filename); diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 64d6dcea04..e64b6d04d3 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -202,7 +202,7 @@ public: // Don't export top/left for rect, only height/width static void setupParamsForExport(Params& p, LLView* parent); - bool buildFromFile(const std::string &filename, LLXMLNodePtr output_node = NULL); + bool buildFromFile(const std::string &filename); boost::signals2::connection setMinimizeCallback( const commit_signal_t::slot_type& cb ); boost::signals2::connection setOpenCallback( const commit_signal_t::slot_type& cb ); diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp index 9115eb7174..306caf2b91 100644 --- a/indra/llui/llfloaterreg.cpp +++ b/indra/llui/llfloaterreg.cpp @@ -154,7 +154,7 @@ LLFloater* LLFloaterReg::getInstance(const std::string& name, const LLSD& key) llwarns << "Failed to build floater type: '" << name << "'." << llendl; return NULL; } - bool success = res->buildFromFile(xui_file, NULL); + bool success = res->buildFromFile(xui_file); if (!success) { llwarns << "Failed to build floater type: '" << name << "'." << llendl; diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 629eef2c3b..4fbee8cd80 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -1424,25 +1424,18 @@ void addPathIfExists(const std::string& new_path, std::vector& path bool LLNotifications::loadTemplates() { llinfos << "Reading notifications template" << llendl; - std::vector search_paths; - - std::string skin_relative_path = gDirUtilp->getDirDelimiter() + LLUI::getSkinPath() + gDirUtilp->getDirDelimiter() + "notifications.xml"; - std::string localized_skin_relative_path = gDirUtilp->getDirDelimiter() + LLUI::getLocalizedSkinPath() + gDirUtilp->getDirDelimiter() + "notifications.xml"; - - addPathIfExists(gDirUtilp->getDefaultSkinDir() + skin_relative_path, search_paths); - addPathIfExists(gDirUtilp->getDefaultSkinDir() + localized_skin_relative_path, search_paths); - addPathIfExists(gDirUtilp->getSkinDir() + skin_relative_path, search_paths); - addPathIfExists(gDirUtilp->getSkinDir() + localized_skin_relative_path, search_paths); - addPathIfExists(gDirUtilp->getUserSkinDir() + skin_relative_path, search_paths); - addPathIfExists(gDirUtilp->getUserSkinDir() + localized_skin_relative_path, search_paths); + // Passing findSkinnedFilenames(merge=true) makes it output all relevant + // pathnames instead of just the ones from the most specific skin. + std::vector search_paths = + gDirUtilp->findSkinnedFilenames(LLDir::XUI, "notifications.xml", true); std::string base_filename = search_paths.front(); LLXMLNodePtr root; BOOL success = LLXMLNode::getLayeredXMLNode(root, search_paths); - + if (!success || root.isNull() || !root->hasName( "notifications" )) { - llerrs << "Problem reading UI Notifications file: " << base_filename << llendl; + llerrs << "Problem reading XML from UI Notifications file: " << base_filename << llendl; return false; } @@ -1452,7 +1445,7 @@ bool LLNotifications::loadTemplates() if(!params.validateBlock()) { - llerrs << "Problem reading UI Notifications file: " << base_filename << llendl; + llerrs << "Problem reading XUI from UI Notifications file: " << base_filename << llendl; return false; } @@ -1508,7 +1501,9 @@ bool LLNotifications::loadTemplates() bool LLNotifications::loadVisibilityRules() { const std::string xml_filename = "notification_visibility.xml"; - std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getXUIPaths().front(), xml_filename); + // Note that here we're looking for the "en" version, the default + // language, rather than the most localized version of this file. + std::string full_filename = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, xml_filename); LLNotificationVisibilityRule::Rules params; LLSimpleXUIParser parser; diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index 00318cec6b..67472ad166 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -968,25 +968,15 @@ static LLFastTimer::DeclareTimer FTM_BUILD_PANELS("Build Panels"); //----------------------------------------------------------------------------- // buildPanel() //----------------------------------------------------------------------------- -BOOL LLPanel::buildFromFile(const std::string& filename, LLXMLNodePtr output_node, const LLPanel::Params& default_params) +BOOL LLPanel::buildFromFile(const std::string& filename, const LLPanel::Params& default_params) { LLFastTimer timer(FTM_BUILD_PANELS); BOOL didPost = FALSE; LLXMLNodePtr root; - //if exporting, only load the language being exported, - //instead of layering localized version on top of english - if (output_node) - { - if (!LLUICtrlFactory::getLocalizedXMLNode(filename, root)) - { - llwarns << "Couldn't parse panel from: " << LLUI::getLocalizedSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl; - return didPost; - } - } - else if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) + if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) { - llwarns << "Couldn't parse panel from: " << LLUI::getSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl; + llwarns << "Couldn't parse panel from: " << filename << llendl; return didPost; } @@ -1010,7 +1000,7 @@ BOOL LLPanel::buildFromFile(const std::string& filename, LLXMLNodePtr output_nod getCommitCallbackRegistrar().pushScope(); getEnableCallbackRegistrar().pushScope(); - didPost = initPanelXML(root, NULL, output_node, default_params); + didPost = initPanelXML(root, NULL, NULL, default_params); getCommitCallbackRegistrar().popScope(); getEnableCallbackRegistrar().popScope(); diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index f620201020..e63b41f97c 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -105,7 +105,7 @@ protected: LLPanel(const LLPanel::Params& params = getDefaultParams()); public: - BOOL buildFromFile(const std::string &filename, LLXMLNodePtr output_node = NULL, const LLPanel::Params&default_params = getDefaultParams()); + BOOL buildFromFile(const std::string &filename, const LLPanel::Params& default_params = getDefaultParams()); static LLPanel* createFactoryPanel(const std::string& name); diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index 87bf518aa1..507ced9172 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -1836,88 +1836,39 @@ struct Paths : public LLInitParam::Block {} }; -//static -void LLUI::setupPaths() -{ - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "paths.xml"); - - LLXMLNodePtr root; - BOOL success = LLXMLNode::parseFile(filename, root, NULL); - Paths paths; - - if(success) - { - LLXUIParser parser; - parser.readXUI(root, paths, filename); - } - sXUIPaths.clear(); - - if (success && paths.validateBlock()) - { - LLStringUtil::format_map_t path_args; - path_args["[LANGUAGE]"] = LLUI::getLanguage(); - - for (LLInitParam::ParamIterator::const_iterator it = paths.directories.begin(), - end_it = paths.directories.end(); - it != end_it; - ++it) - { - std::string path_val_ui; - for (LLInitParam::ParamIterator::const_iterator subdir_it = it->subdirs.begin(), - subdir_end_it = it->subdirs.end(); - subdir_it != subdir_end_it;) - { - path_val_ui += subdir_it->value(); - if (++subdir_it != subdir_end_it) - path_val_ui += gDirUtilp->getDirDelimiter(); - } - LLStringUtil::format(path_val_ui, path_args); - if (std::find(sXUIPaths.begin(), sXUIPaths.end(), path_val_ui) == sXUIPaths.end()) - { - sXUIPaths.push_back(path_val_ui); - } - - } - } - else // parsing failed - { - std::string slash = gDirUtilp->getDirDelimiter(); - std::string dir = "xui" + slash + "en"; - llwarns << "XUI::config file unable to open: " << filename << llendl; - sXUIPaths.push_back(dir); - } -} - //static std::string LLUI::locateSkin(const std::string& filename) { - std::string slash = gDirUtilp->getDirDelimiter(); std::string found_file = filename; - if (!gDirUtilp->fileExists(found_file)) + if (gDirUtilp->fileExists(found_file)) { - found_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); // Should be CUSTOM_SKINS? + return found_file; } - if (sSettingGroups["config"] && sSettingGroups["config"]->controlExists("Language")) + + found_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); // Should be CUSTOM_SKINS? + if (gDirUtilp->fileExists(found_file)) { - if (!gDirUtilp->fileExists(found_file)) - { - std::string localization = getLanguage(); - std::string local_skin = "xui" + slash + localization + slash + filename; - found_file = gDirUtilp->findSkinnedFilename(local_skin); - } + return found_file; } - if (!gDirUtilp->fileExists(found_file)) + + found_file = gDirUtilp->findSkinnedFilename(LLDir::XUI, filename); + if (! found_file.empty()) { - std::string local_skin = "xui" + slash + "en" + slash + filename; - found_file = gDirUtilp->findSkinnedFilename(local_skin); + return found_file; } - if (!gDirUtilp->fileExists(found_file)) + + found_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename); +/*==========================================================================*| + // Hmm, if we got this far, previous implementation of this method would + // return this last found_file value whether or not it actually exists. + if (gDirUtilp->fileExists(found_file)) { - found_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename); + return found_file; } +|*==========================================================================*/ return found_file; -} +} //static LLVector2 LLUI::getWindowSize() diff --git a/indra/llui/llui.h b/indra/llui/llui.h index 28e84fa444..c5a12d2b31 100644 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -292,11 +292,6 @@ public: // Return the ISO639 language name ("en", "ko", etc.) for the viewer UI. // http://www.loc.gov/standards/iso639-2/php/code_list.php static std::string getLanguage(); - - static void setupPaths(); - static const std::vector& getXUIPaths() { return sXUIPaths; } - static std::string getSkinPath() { return sXUIPaths.front(); } - static std::string getLocalizedSkinPath() { return sXUIPaths.back(); } //all files may not exist at the localized path //helper functions (should probably move free standing rendering helper functions here) static LLView* getRootView() { return sRootView; } diff --git a/indra/llui/lluicolortable.cpp b/indra/llui/lluicolortable.cpp index 9455d09cc0..2717445396 100644 --- a/indra/llui/lluicolortable.cpp +++ b/indra/llui/lluicolortable.cpp @@ -32,6 +32,7 @@ #include "llui.h" #include "lluicolortable.h" #include "lluictrlfactory.h" +#include LLUIColorTable::ColorParams::ColorParams() : value("value"), @@ -206,19 +207,11 @@ bool LLUIColorTable::loadFromSettings() { bool result = false; - std::string default_filename = gDirUtilp->getExpandedFilename(LL_PATH_DEFAULT_SKIN, "colors.xml"); - result |= loadFromFilename(default_filename, mLoadedColors); - - std::string current_filename = gDirUtilp->getExpandedFilename(LL_PATH_TOP_SKIN, "colors.xml"); - if(current_filename != default_filename) - { - result |= loadFromFilename(current_filename, mLoadedColors); - } - - current_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SKIN, "colors.xml"); - if(current_filename != default_filename) + // pass merge=true because we want colors.xml from every skin dir + BOOST_FOREACH(std::string colors_path, + gDirUtilp->findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", true)) { - result |= loadFromFilename(current_filename, mLoadedColors); + result |= loadFromFilename(colors_path, mLoadedColors); } std::string user_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "colors.xml"); diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index 25e7a31e90..2b317b46e3 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -90,10 +90,12 @@ LLUICtrlFactory::~LLUICtrlFactory() void LLUICtrlFactory::loadWidgetTemplate(const std::string& widget_tag, LLInitParam::BaseBlock& block) { - std::string filename = std::string("widgets") + gDirUtilp->getDirDelimiter() + widget_tag + ".xml"; + std::string filename = gDirUtilp->add("widgets", widget_tag + ".xml"); LLXMLNodePtr root_node; - std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getXUIPaths().front(), filename); + // Here we're looking for the "en" version, the default-language version + // of the file, rather than the localized version. + std::string full_filename = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, filename); if (!full_filename.empty()) { LLUICtrlFactory::instance().pushFileName(full_filename); @@ -152,19 +154,8 @@ static LLFastTimer::DeclareTimer FTM_XML_PARSE("XML Reading/Parsing"); bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNodePtr& root) { LLFastTimer timer(FTM_XML_PARSE); - - std::vector paths; - std::string path = gDirUtilp->findSkinnedFilename(LLUI::getSkinPath(), xui_filename); - if (!path.empty()) - { - paths.push_back(path); - } - - std::string localize_path = gDirUtilp->findSkinnedFilename(LLUI::getLocalizedSkinPath(), xui_filename); - if (!localize_path.empty() && localize_path != path) - { - paths.push_back(localize_path); - } + std::vector paths = + gDirUtilp->findSkinnedFilenames(LLDir::XUI, xui_filename); if (paths.empty()) { @@ -176,23 +167,6 @@ bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNo } -//----------------------------------------------------------------------------- -// getLocalizedXMLNode() -//----------------------------------------------------------------------------- -bool LLUICtrlFactory::getLocalizedXMLNode(const std::string &xui_filename, LLXMLNodePtr& root) -{ - LLFastTimer timer(FTM_XML_PARSE); - std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getLocalizedSkinPath(), xui_filename); - if (!LLXMLNode::parseFile(full_filename, root, NULL)) - { - return false; - } - else - { - return true; - } -} - //----------------------------------------------------------------------------- // saveToXML() //----------------------------------------------------------------------------- @@ -239,8 +213,10 @@ std::string LLUICtrlFactory::getCurFileName() void LLUICtrlFactory::pushFileName(const std::string& name) -{ - mFileNames.push_back(gDirUtilp->findSkinnedFilename(LLUI::getSkinPath(), name)); +{ + // Here we seem to be looking for the default language file ("en") rather + // than the localized one, if any. + mFileNames.push_back(gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, name)); } void LLUICtrlFactory::popFileName() @@ -260,7 +236,7 @@ void LLUICtrlFactory::setCtrlParent(LLView* view, LLView* parent, S32 tab_group) //static std::string LLUICtrlFactory::findSkinnedFilename(const std::string& filename) { - return gDirUtilp->findSkinnedFilename(LLUI::getSkinPath(), filename); + return gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, filename); } //static diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h index d612ad5005..56e5f3eb7b 100644 --- a/indra/llui/lluictrlfactory.h +++ b/indra/llui/lluictrlfactory.h @@ -169,7 +169,7 @@ public: LLView* createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, const widget_registry_t&, LLXMLNodePtr output_node ); template - static T* createFromFile(const std::string &filename, LLView *parent, const widget_registry_t& registry, LLXMLNodePtr output_node = NULL) + static T* createFromFile(const std::string &filename, LLView *parent, const widget_registry_t& registry) { T* widget = NULL; @@ -178,23 +178,13 @@ public: { LLXMLNodePtr root_node; - //if exporting, only load the language being exported, - //instead of layering localized version on top of english - if (output_node) - { - if (!LLUICtrlFactory::getLocalizedXMLNode(filename, root_node)) - { - llwarns << "Couldn't parse XUI file: " << filename << llendl; - goto fail; - } - } - else if (!LLUICtrlFactory::getLayeredXMLNode(filename, root_node)) + if (!LLUICtrlFactory::getLayeredXMLNode(filename, root_node)) { llwarns << "Couldn't parse XUI file: " << skinned_filename << llendl; goto fail; } - LLView* view = getInstance()->createFromXML(root_node, parent, filename, registry, output_node); + LLView* view = getInstance()->createFromXML(root_node, parent, filename, registry, NULL); if (view) { widget = dynamic_cast(view); @@ -223,7 +213,6 @@ fail: static void createChildren(LLView* viewp, LLXMLNodePtr node, const widget_registry_t&, LLXMLNodePtr output_node = NULL); static bool getLayeredXMLNode(const std::string &filename, LLXMLNodePtr& root); - static bool getLocalizedXMLNode(const std::string &xui_filename, LLXMLNodePtr& root); private: //NOTE: both friend declarations are necessary to keep both gcc and msvc happy diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index 32d081d552..a7d12476a4 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -41,6 +41,12 @@ #include "lluuid.h" #include "lldiriterator.h" +#include "stringize.h" +#include +#include +#include +#include +#include #if LL_WINDOWS #include "lldir_win32.h" @@ -58,6 +64,14 @@ LLDir_Linux gDirUtil; LLDir *gDirUtilp = (LLDir *)&gDirUtil; +/// Values for findSkinnedFilenames(subdir) parameter +const char + *LLDir::XUI = "xui", + *LLDir::TEXTURES = "textures", + *LLDir::SKINBASE = ""; + +static const char* const empty = ""; + LLDir::LLDir() : mAppName(""), mExecutablePathAndName(""), @@ -70,7 +84,8 @@ LLDir::LLDir() mOSCacheDir(""), mCAFile(""), mTempDir(""), - mDirDelimiter("/") // fallback to forward slash if not overridden + mDirDelimiter("/"), // fallback to forward slash if not overridden + mLanguage("en") { } @@ -96,9 +111,7 @@ S32 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask) LLDirIterator iter(dirname, mask); while (iter.next(filename)) { - fullpath = dirname; - fullpath += getDirDelimiter(); - fullpath += filename; + fullpath = add(dirname, filename); if(LLFile::isdir(fullpath)) { @@ -270,12 +283,12 @@ std::string LLDir::buildSLOSCacheDir() const } else { - res = getOSUserAppDir() + mDirDelimiter + "cache"; + res = add(getOSUserAppDir(), "cache"); } } else { - res = getOSCacheDir() + mDirDelimiter + "SecondLife"; + res = add(getOSCacheDir(), "SecondLife"); } return res; } @@ -298,19 +311,24 @@ const std::string &LLDir::getDirDelimiter() const return mDirDelimiter; } +const std::string& LLDir::getDefaultSkinDir() const +{ + return mDefaultSkinDir; +} + const std::string &LLDir::getSkinDir() const { return mSkinDir; } -const std::string &LLDir::getUserSkinDir() const +const std::string &LLDir::getUserDefaultSkinDir() const { - return mUserSkinDir; + return mUserDefaultSkinDir; } -const std::string& LLDir::getDefaultSkinDir() const +const std::string &LLDir::getUserSkinDir() const { - return mDefaultSkinDir; + return mUserSkinDir; } const std::string LLDir::getSkinBaseDir() const @@ -323,6 +341,41 @@ const std::string &LLDir::getLLPluginDir() const return mLLPluginDir; } +static std::string ELLPathToString(ELLPath location) +{ + typedef std::map ELLPathMap; +#define ENT(symbol) ELLPathMap::value_type(symbol, #symbol) + static ELLPathMap::value_type init[] = + { + ENT(LL_PATH_NONE), + ENT(LL_PATH_USER_SETTINGS), + ENT(LL_PATH_APP_SETTINGS), + ENT(LL_PATH_PER_SL_ACCOUNT), // returns/expands to blank string if we don't know the account name yet + ENT(LL_PATH_CACHE), + ENT(LL_PATH_CHARACTER), + ENT(LL_PATH_HELP), + ENT(LL_PATH_LOGS), + ENT(LL_PATH_TEMP), + ENT(LL_PATH_SKINS), + ENT(LL_PATH_TOP_SKIN), + ENT(LL_PATH_CHAT_LOGS), + ENT(LL_PATH_PER_ACCOUNT_CHAT_LOGS), + ENT(LL_PATH_USER_SKIN), + ENT(LL_PATH_LOCAL_ASSETS), + ENT(LL_PATH_EXECUTABLE), + ENT(LL_PATH_DEFAULT_SKIN), + ENT(LL_PATH_FONTS), + ENT(LL_PATH_LAST) + }; +#undef ENT + static const ELLPathMap sMap(boost::begin(init), boost::end(init)); + + ELLPathMap::const_iterator found = sMap.find(location); + if (found != sMap.end()) + return found->second; + return STRINGIZE("Invalid ELLPath value " << location); +} + std::string LLDir::getExpandedFilename(ELLPath location, const std::string& filename) const { return getExpandedFilename(location, "", filename); @@ -343,15 +396,11 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_APP_SETTINGS: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "app_settings"; + prefix = add(getAppRODataDir(), "app_settings"); break; case LL_PATH_CHARACTER: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "character"; + prefix = add(getAppRODataDir(), "character"); break; case LL_PATH_HELP: @@ -363,16 +412,22 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_USER_SETTINGS: - prefix = getOSUserAppDir(); - prefix += mDirDelimiter; - prefix += "user_settings"; + prefix = add(getOSUserAppDir(), "user_settings"); break; case LL_PATH_PER_SL_ACCOUNT: prefix = getLindenUserDir(); if (prefix.empty()) { - // if we're asking for the per-SL-account directory but we haven't logged in yet (or otherwise don't know the account name from which to build this string), then intentionally return a blank string to the caller and skip the below warning about a blank prefix. + // if we're asking for the per-SL-account directory but we haven't + // logged in yet (or otherwise don't know the account name from + // which to build this string), then intentionally return a blank + // string to the caller and skip the below warning about a blank + // prefix. + LL_DEBUGS("LLDir") << "getLindenUserDir() not yet set: " + << ELLPathToString(location) + << ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename + << "' => ''" << LL_ENDL; return std::string(); } break; @@ -386,9 +441,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_LOGS: - prefix = getOSUserAppDir(); - prefix += mDirDelimiter; - prefix += "logs"; + prefix = add(getOSUserAppDir(), "logs"); break; case LL_PATH_TEMP: @@ -412,9 +465,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_LOCAL_ASSETS: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "local_assets"; + prefix = add(getAppRODataDir(), "local_assets"); break; case LL_PATH_EXECUTABLE: @@ -422,56 +473,36 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_FONTS: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "fonts"; + prefix = add(getAppRODataDir(), "fonts"); break; default: llassert(0); } - std::string filename = in_filename; - if (!subdir2.empty()) - { - filename = subdir2 + mDirDelimiter + filename; - } - - if (!subdir1.empty()) - { - filename = subdir1 + mDirDelimiter + filename; - } - if (prefix.empty()) { - llwarns << "prefix is empty, possible bad filename" << llendl; - } - - std::string expanded_filename; - if (!filename.empty()) - { - if (!prefix.empty()) - { - expanded_filename += prefix; - expanded_filename += mDirDelimiter; - expanded_filename += filename; - } - else - { - expanded_filename = filename; - } - } - else if (!prefix.empty()) - { - // Directory only, no file name. - expanded_filename = prefix; + llwarns << ELLPathToString(location) + << ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename + << "': prefix is empty, possible bad filename" << llendl; } - else + + std::string expanded_filename = add(add(prefix, subdir1), subdir2); + if (expanded_filename.empty() && in_filename.empty()) { - expanded_filename.assign(""); + return ""; } - - //llinfos << "*** EXPANDED FILENAME: <" << expanded_filename << ">" << llendl; + // Use explicit concatenation here instead of another add() call. Callers + // passing in_filename as "" expect to obtain a pathname ending with + // mDirSeparator so they can later directly concatenate with a specific + // filename. A caller using add() doesn't care, but there's still code + // loose in the system that uses std::string::operator+(). + expanded_filename += mDirDelimiter; + expanded_filename += in_filename; + + LL_DEBUGS("LLDir") << ELLPathToString(location) + << ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename + << "' => '" << expanded_filename << "'" << LL_ENDL; return expanded_filename; } @@ -511,31 +542,168 @@ std::string LLDir::getExtension(const std::string& filepath) const return exten; } -std::string LLDir::findSkinnedFilename(const std::string &filename) const +std::string LLDir::findSkinnedFilenameBaseLang(const std::string &subdir, + const std::string &filename, + bool merge) const { - return findSkinnedFilename("", "", filename); + // This implementation is basically just as described in the declaration comments. + std::vector found(findSkinnedFilenames(subdir, filename, merge)); + if (found.empty()) + { + return ""; + } + return found.front(); } -std::string LLDir::findSkinnedFilename(const std::string &subdir, const std::string &filename) const +std::string LLDir::findSkinnedFilename(const std::string &subdir, + const std::string &filename, + bool merge) const { - return findSkinnedFilename("", subdir, filename); + // This implementation is basically just as described in the declaration comments. + std::vector found(findSkinnedFilenames(subdir, filename, merge)); + if (found.empty()) + { + return ""; + } + return found.back(); } -std::string LLDir::findSkinnedFilename(const std::string &subdir1, const std::string &subdir2, const std::string &filename) const +std::vector LLDir::findSkinnedFilenames(const std::string& subdir, + const std::string& filename, + bool merge) const { - // generate subdirectory path fragment, e.g. "/foo/bar", "/foo", "" - std::string subdirs = ((subdir1.empty() ? "" : mDirDelimiter) + subdir1) - + ((subdir2.empty() ? "" : mDirDelimiter) + subdir2); + // Recognize subdirs that have no localization. + static const char* sUnlocalizedData[] = + { + "", // top-level directory not localized + "textures" // textures not localized + }; + static const std::set sUnlocalized(boost::begin(sUnlocalizedData), + boost::end(sUnlocalizedData)); + + LL_DEBUGS("LLDir") << "subdir '" << subdir << "', filename '" << filename + << "', merge " << std::boolalpha << merge << LL_ENDL; + + // Cache the default language directory for each subdir we've encountered. + // A cache entry whose value is the empty string means "not localized, + // don't bother checking again." + typedef std::map LocalizedMap; + static LocalizedMap sLocalized; + + // Check whether we've already discovered if this subdir is localized. + LocalizedMap::const_iterator found = sLocalized.find(subdir); + if (found == sLocalized.end()) + { + // We have not yet determined that. Is it one of the subdirs "known" + // to be unlocalized? + if (sUnlocalized.find(subdir) != sUnlocalized.end()) + { + // This subdir is known to be unlocalized. Remember that. + found = sLocalized.insert(LocalizedMap::value_type(subdir, "")).first; + } + else + { + // We do not recognize this subdir. Investigate. + std::string subdir_path(add(getDefaultSkinDir(), subdir)); + if (fileExists(add(subdir_path, "en"))) + { + // defaultSkinDir/subdir contains subdir "en". That's our + // default language; this subdir is localized. + found = sLocalized.insert(LocalizedMap::value_type(subdir, "en")).first; + } + else if (fileExists(add(subdir_path, "en-us"))) + { + // defaultSkinDir/subdir contains subdir "en-us" but not "en". + // Set as default language; this subdir is localized. + found = sLocalized.insert(LocalizedMap::value_type(subdir, "en-us")).first; + } + else + { + // defaultSkinDir/subdir contains neither "en" nor "en-us". + // Assume it's not localized. Remember that assumption. + found = sLocalized.insert(LocalizedMap::value_type(subdir, "")).first; + } + } + } + // Every code path above should have resulted in 'found' becoming a valid + // iterator to an entry in sLocalized. + llassert(found != sLocalized.end()); + + // Now -- is this subdir localized, or not? The answer determines what + // subdirectories we check (under subdir) for the requested filename. + std::vector subsubdirs; + if (found->second.empty()) + { + // subdir is not localized. filename should be located directly within it. + subsubdirs.push_back(""); + } + else + { + // subdir is localized, and found->second is the default language + // directory within it. Check both the default language and the + // current language -- if it differs from the default, of course. + subsubdirs.push_back(found->second); + if (mLanguage != found->second) + { + subsubdirs.push_back(mLanguage); + } + } + // Code below relies on subsubdirs not being empty: more specifically, on + // front() being valid. There may or may not be additional entries, but we + // have at least one. For an unlocalized subdir, it's the only one; for a + // localized subdir, it's the default one. + llassert(! subsubdirs.empty()); + + // Build results vector. + std::vector results; + BOOST_FOREACH(std::string skindir, mSearchSkinDirs) + { + std::string subdir_path(add(skindir, subdir)); + // Does subdir_path/subsubdirs[0]/filename exist? If there's more than + // one entry in subsubdirs, the first is the default language ("en"), + // the second is the current language. A skin that contains + // subdir/language/filename without also containing subdir/en/filename + // is ill-formed: skip any such skin. So to decide whether to keep + // this skin dir or skip it, we need only check for the existence of + // the first subsubdir entry ("en" or only). + std::string subsubdir_path(add(add(subdir_path, subsubdirs.front()), filename)); + if (! fileExists(subsubdir_path)) + continue; - std::vector search_paths; - - search_paths.push_back(getUserSkinDir() + subdirs); // first look in user skin override - search_paths.push_back(getSkinDir() + subdirs); // then in current skin - search_paths.push_back(getDefaultSkinDir() + subdirs); // then default skin - search_paths.push_back(getCacheDir() + subdirs); // and last in preload directory + // Here the desired filename exists in the first subsubdir. That means + // this is a skindir we want to record in results. But if the caller + // passed merge=false, we must discard all previous skindirs. + if (! merge) + { + results.clear(); + } + + // Now add every subsubdir in which filename exists. We already know + // it exists in the first one. + results.push_back(subsubdir_path); + + // Append all remaining subsubdirs in which filename exists. + for (std::vector::const_iterator ssdi(subsubdirs.begin() + 1), ssdend(subsubdirs.end()); + ssdi != ssdend; ++ssdi) + { + subsubdir_path = add(add(subdir_path, *ssdi), filename); + if (fileExists(subsubdir_path)) + { + results.push_back(subsubdir_path); + } + } + } - std::string found_file = findFile(filename, search_paths); - return found_file; + LL_DEBUGS("LLDir") << empty; + const char* comma = ""; + BOOST_FOREACH(std::string path, results) + { + LL_CONT << comma << "'" << path << "'"; + comma = ", "; + } + LL_CONT << LL_ENDL; + + return results; } std::string LLDir::getTempFilename() const @@ -546,12 +714,7 @@ std::string LLDir::getTempFilename() const random_uuid.generate(); random_uuid.toString(uuid_str); - std::string temp_filename = getTempDir(); - temp_filename += mDirDelimiter; - temp_filename += uuid_str; - temp_filename += ".tmp"; - - return temp_filename; + return add(getTempDir(), uuid_str + ".tmp"); } // static @@ -587,9 +750,7 @@ void LLDir::setLindenUserDir(const std::string &username) std::string userlower(username); LLStringUtil::toLower(userlower); LLStringUtil::replaceChar(userlower, ' ', '_'); - mLindenUserDir = getOSUserAppDir(); - mLindenUserDir += mDirDelimiter; - mLindenUserDir += userlower; + mLindenUserDir = add(getOSUserAppDir(), userlower); } else { @@ -621,9 +782,7 @@ void LLDir::setPerAccountChatLogsDir(const std::string &username) std::string userlower(username); LLStringUtil::toLower(userlower); LLStringUtil::replaceChar(userlower, ' ', '_'); - mPerAccountChatLogsDir = getChatLogsDir(); - mPerAccountChatLogsDir += mDirDelimiter; - mPerAccountChatLogsDir += userlower; + mPerAccountChatLogsDir = add(getChatLogsDir(), userlower); } else { @@ -632,25 +791,59 @@ void LLDir::setPerAccountChatLogsDir(const std::string &username) } -void LLDir::setSkinFolder(const std::string &skin_folder) +void LLDir::setSkinFolder(const std::string &skin_folder, const std::string& language) { - mSkinDir = getSkinBaseDir(); - mSkinDir += mDirDelimiter; - mSkinDir += skin_folder; + LL_DEBUGS("LLDir") << "Setting skin '" << skin_folder << "', language '" << language << "'" + << LL_ENDL; + mSkinName = skin_folder; + mLanguage = language; - // user modifications to current skin - // e.g. c:\documents and settings\users\username\application data\second life\skins\dazzle - mUserSkinDir = getOSUserAppDir(); - mUserSkinDir += mDirDelimiter; - mUserSkinDir += "skins"; - mUserSkinDir += mDirDelimiter; - mUserSkinDir += skin_folder; + // This method is called multiple times during viewer initialization. Each + // time it's called, reset mSearchSkinDirs. + mSearchSkinDirs.clear(); // base skin which is used as fallback for all skinned files // e.g. c:\program files\secondlife\skins\default mDefaultSkinDir = getSkinBaseDir(); - mDefaultSkinDir += mDirDelimiter; - mDefaultSkinDir += "default"; + append(mDefaultSkinDir, "default"); + // This is always the most general of the search skin directories. + addSearchSkinDir(mDefaultSkinDir); + + mSkinDir = getSkinBaseDir(); + append(mSkinDir, skin_folder); + // Next level of generality is a skin installed with the viewer. + addSearchSkinDir(mSkinDir); + + // user modifications to skins, current and default + // e.g. c:\documents and settings\users\username\application data\second life\skins\dazzle + mUserSkinDir = getOSUserAppDir(); + append(mUserSkinDir, "skins"); + mUserDefaultSkinDir = mUserSkinDir; + append(mUserDefaultSkinDir, "default"); + append(mUserSkinDir, skin_folder); + // Next level of generality is user modifications to default skin... + addSearchSkinDir(mUserDefaultSkinDir); + // then user-defined skins. + addSearchSkinDir(mUserSkinDir); +} + +void LLDir::addSearchSkinDir(const std::string& skindir) +{ + if (std::find(mSearchSkinDirs.begin(), mSearchSkinDirs.end(), skindir) == mSearchSkinDirs.end()) + { + LL_DEBUGS("LLDir") << "search skin: '" << skindir << "'" << LL_ENDL; + mSearchSkinDirs.push_back(skindir); + } +} + +std::string LLDir::getSkinFolder() const +{ + return mSkinName; +} + +std::string LLDir::getLanguage() const +{ + return mLanguage; } bool LLDir::setCacheDir(const std::string &path) @@ -664,7 +857,7 @@ bool LLDir::setCacheDir(const std::string &path) else { LLFile::mkdir(path); - std::string tempname = path + mDirDelimiter + "temp"; + std::string tempname = add(path, "temp"); LLFILE* file = LLFile::fopen(tempname,"wt"); if (file) { @@ -697,6 +890,57 @@ void LLDir::dumpCurrentDirectories() LL_DEBUGS2("AppInit","Directories") << " SkinDir: " << getSkinDir() << LL_ENDL; } +std::string LLDir::add(const std::string& path, const std::string& name) const +{ + std::string destpath(path); + append(destpath, name); + return destpath; +} + +void LLDir::append(std::string& destpath, const std::string& name) const +{ + // Delegate question of whether we need a separator to helper method. + SepOff sepoff(needSep(destpath, name)); + if (sepoff.first) // do we need a separator? + { + destpath += mDirDelimiter; + } + // If destpath ends with a separator, AND name starts with one, skip + // name's leading separator. + destpath += name.substr(sepoff.second); +} + +LLDir::SepOff LLDir::needSep(const std::string& path, const std::string& name) const +{ + if (path.empty() || name.empty()) + { + // If either path or name are empty, we do not need a separator + // between them. + return SepOff(false, 0); + } + // Here we know path and name are both non-empty. But if path already ends + // with a separator, or if name already starts with a separator, we need + // not add one. + std::string::size_type seplen(mDirDelimiter.length()); + bool path_ends_sep(path.substr(path.length() - seplen) == mDirDelimiter); + bool name_starts_sep(name.substr(0, seplen) == mDirDelimiter); + if ((! path_ends_sep) && (! name_starts_sep)) + { + // If neither path nor name brings a separator to the junction, then + // we need one. + return SepOff(true, 0); + } + if (path_ends_sep && name_starts_sep) + { + // But if BOTH path and name bring a separator, we need not add one. + // Moreover, we should actually skip the leading separator of 'name'. + return SepOff(false, seplen); + } + // Here we know that either path_ends_sep or name_starts_sep is true -- + // but not both. So don't add a separator, and don't skip any characters: + // simple concatenation will do the trick. + return SepOff(false, 0); +} void dir_exists_or_crash(const std::string &dir_name) { diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h index 5ee8bdb542..a242802979 100644 --- a/indra/llvfs/lldir.h +++ b/indra/llvfs/lldir.h @@ -56,7 +56,7 @@ typedef enum ELLPath LL_PATH_LAST } ELLPath; - +/// Directory operations class LLDir { public: @@ -100,9 +100,10 @@ class LLDir const std::string &getOSCacheDir() const; // location of OS-specific cache folder (may be empty string) const std::string &getCAFile() const; // File containing TLS certificate authorities const std::string &getDirDelimiter() const; // directory separator for platform (ie. '\' or '/' or ':') + const std::string &getDefaultSkinDir() const; // folder for default skin. e.g. c:\program files\second life\skins\default const std::string &getSkinDir() const; // User-specified skin folder. + const std::string &getUserDefaultSkinDir() const; // dir with user modifications to default skin const std::string &getUserSkinDir() const; // User-specified skin folder with user modifications. e.g. c:\documents and settings\username\application data\second life\skins\curskin - const std::string &getDefaultSkinDir() const; // folder for default skin. e.g. c:\program files\second life\skins\default const std::string getSkinBaseDir() const; // folder that contains all installed skins (not user modifications). e.g. c:\program files\second life\skins const std::string &getLLPluginDir() const; // Directory containing plugins and plugin shell @@ -117,10 +118,59 @@ class LLDir std::string getExtension(const std::string& filepath) const; // Excludes '.', e.g getExtension("foo.wav") == "wav" // these methods search the various skin paths for the specified file in the following order: - // getUserSkinDir(), getSkinDir(), getDefaultSkinDir() - std::string findSkinnedFilename(const std::string &filename) const; - std::string findSkinnedFilename(const std::string &subdir, const std::string &filename) const; - std::string findSkinnedFilename(const std::string &subdir1, const std::string &subdir2, const std::string &filename) const; + // getUserSkinDir(), getUserDefaultSkinDir(), getSkinDir(), getDefaultSkinDir() + /** + * Given a filename within skin, return an ordered sequence of paths to + * search. Nonexistent files will be filtered out -- which means that the + * vector might be empty. + * + * @param subdir Identify top-level skin subdirectory by passing one of + * LLDir::XUI (file lives under "xui" subtree), LLDir::TEXTURES (file + * lives under "textures" subtree), LLDir::SKINBASE (file lives at top + * level of skin subdirectory). + * @param filename Desired filename within subdir within skin, e.g. + * "panel_login.xml". DO NOT prepend (e.g.) "xui" or the desired language. + * @param merge Callers perform two different kinds of processing. When + * fetching a XUI file, for instance, the existence of @a filename in the + * specified skin completely supercedes any @a filename in the default + * skin. For that case, leave the default @a merge=false. The returned + * vector will contain only + * ".../current_skin/xui/en/filename", + * ".../current_skin/xui/current_language/filename". + * But for (e.g.) "strings.xml", we want a given skin to be able to + * override only specific entries from the default skin. Any string not + * defined in the specified skin will be sought in the default skin. For + * that case, pass @a merge=true. The returned vector will contain at + * least ".../default/xui/en/strings.xml", + * ".../default/xui/current_language/strings.xml", + * ".../current_skin/xui/en/strings.xml", + * ".../current_skin/xui/current_language/strings.xml". + */ + std::vector findSkinnedFilenames(const std::string& subdir, + const std::string& filename, + bool merge=false) const; + /// Values for findSkinnedFilenames(subdir) parameter + static const char *XUI, *TEXTURES, *SKINBASE; + /** + * Return the base-language pathname from findSkinnedFilenames(), or + * the empty string if no such file exists. Parameters are identical to + * findSkinnedFilenames(). This is shorthand for capturing the vector + * returned by findSkinnedFilenames(), checking for empty() and then + * returning front(). + */ + std::string findSkinnedFilenameBaseLang(const std::string &subdir, + const std::string &filename, + bool merge=false) const; + /** + * Return the "most localized" pathname from findSkinnedFilenames(), or + * the empty string if no such file exists. Parameters are identical to + * findSkinnedFilenames(). This is shorthand for capturing the vector + * returned by findSkinnedFilenames(), checking for empty() and then + * returning back(). + */ + std::string findSkinnedFilename(const std::string &subdir, + const std::string &filename, + bool merge=false) const; // random filename in common temporary directory std::string getTempFilename() const; @@ -132,15 +182,30 @@ class LLDir virtual void setChatLogsDir(const std::string &path); // Set the chat logs dir to this user's dir virtual void setPerAccountChatLogsDir(const std::string &username); // Set the per user chat log directory. virtual void setLindenUserDir(const std::string &username); // Set the linden user dir to this user's dir - virtual void setSkinFolder(const std::string &skin_folder); + virtual void setSkinFolder(const std::string &skin_folder, const std::string& language); + virtual std::string getSkinFolder() const; + virtual std::string getLanguage() const; virtual bool setCacheDir(const std::string &path); virtual void dumpCurrentDirectories(); - + // Utility routine std::string buildSLOSCacheDir() const; + /// Append specified @a name to @a destpath, separated by getDirDelimiter() + /// if both are non-empty. + void append(std::string& destpath, const std::string& name) const; + /// Append specified @a name to @a path, separated by getDirDelimiter() + /// if both are non-empty. Return result, leaving @a path unmodified. + std::string add(const std::string& path, const std::string& name) const; + protected: + // Does an add() or append() call need a directory delimiter? + typedef std::pair SepOff; + SepOff needSep(const std::string& path, const std::string& name) const; + // build mSearchSkinDirs without adding duplicates + void addSearchSkinDir(const std::string& skindir); + std::string mAppName; // install directory under progams/ ie "SecondLife" std::string mExecutablePathAndName; // full path + Filename of .exe std::string mExecutableFilename; // Filename of .exe @@ -158,10 +223,18 @@ protected: std::string mDefaultCacheDir; // default cache diretory std::string mOSCacheDir; // operating system cache dir std::string mDirDelimiter; + std::string mSkinName; // caller-specified skin name std::string mSkinBaseDir; // Base for skins paths. - std::string mSkinDir; // Location for current skin info. std::string mDefaultSkinDir; // Location for default skin info. + std::string mSkinDir; // Location for current skin info. + std::string mUserDefaultSkinDir; // Location for default skin info. std::string mUserSkinDir; // Location for user-modified skin info. + // Skin directories to search, most general to most specific. This order + // works well for composing fine-grained files, in which an individual item + // in a specific file overrides the corresponding item in more general + // files. Of course, for a file-level search, iterate backwards. + std::vector mSearchSkinDirs; + std::string mLanguage; // Current viewer language std::string mLLPluginDir; // Location for plugins and plugin shell }; diff --git a/indra/llvfs/tests/lldir_test.cpp b/indra/llvfs/tests/lldir_test.cpp index ea321c5ae9..a00fc8684c 100644 --- a/indra/llvfs/tests/lldir_test.cpp +++ b/indra/llvfs/tests/lldir_test.cpp @@ -27,11 +27,161 @@ #include "linden_common.h" +#include "llstring.h" +#include "tests/StringVec.h" #include "../lldir.h" #include "../lldiriterator.h" #include "../test/lltut.h" +#include "stringize.h" +#include +#include + +using boost::assign::list_of; + +// We use ensure_equals(..., vec(list_of(...))) not because it's functionally +// required, but because ensure_equals() knows how to format a StringVec. +// Turns out that when ensure_equals() displays a test failure with just +// list_of("string")("another"), you see 'stringanother' vs. '("string", +// "another")'. +StringVec vec(const StringVec& v) +{ + return v; +} +// For some tests, use a dummy LLDir that uses memory data instead of touching +// the filesystem +struct LLDir_Dummy: public LLDir +{ + /*----------------------------- LLDir API ------------------------------*/ + LLDir_Dummy() + { + // Initialize important LLDir data members based on the filesystem + // data below. + mDirDelimiter = "/"; + mExecutableDir = "install"; + mExecutableFilename = "test"; + mExecutablePathAndName = add(mExecutableDir, mExecutableFilename); + mWorkingDir = mExecutableDir; + mAppRODataDir = "install"; + mSkinBaseDir = add(mAppRODataDir, "skins"); + mOSUserDir = "user"; + mOSUserAppDir = mOSUserDir; + mLindenUserDir = ""; + + // Make the dummy filesystem look more or less like what we expect in + // the real one. + static const char* preload[] = + { + "install/skins/default/colors.xml", + "install/skins/default/xui/en/strings.xml", + "install/skins/default/xui/fr/strings.xml", + "install/skins/default/xui/en/floater.xml", + "install/skins/default/xui/fr/floater.xml", + "install/skins/default/xui/en/newfile.xml", + "install/skins/default/xui/fr/newfile.xml", + "install/skins/default/html/en-us/welcome.html", + "install/skins/default/html/fr/welcome.html", + "install/skins/default/textures/only_default.jpeg", + "install/skins/default/future/somefile.txt", + "install/skins/steam/colors.xml", + "install/skins/steam/xui/en/strings.xml", + "install/skins/steam/xui/fr/strings.xml", + "install/skins/steam/textures/only_steam.jpeg", + "user/skins/default/colors.xml", + "user/skins/default/xui/en/strings.xml", + "user/skins/default/xui/fr/strings.xml", + // This is an attempted override that doesn't work: for a + // localized subdir, a skin must have subdir/en/filename as well + // as subdir/language/filename. + "user/skins/default/xui/fr/floater.xml", + // This is an override that only specifies the "en" version + "user/skins/default/xui/en/newfile.xml", + "user/skins/default/textures/only_user_default.jpeg", + "user/skins/steam/colors.xml", + "user/skins/steam/xui/en/strings.xml", + "user/skins/steam/xui/fr/strings.xml", + "user/skins/steam/textures/only_user_steam.jpeg" + }; + BOOST_FOREACH(const char* path, preload) + { + buildFilesystem(path); + } + } + + virtual ~LLDir_Dummy() {} + + virtual void initAppDirs(const std::string& app_name, const std::string& app_read_only_data_dir) + { + // Implement this when we write a test that needs it + } + + virtual std::string getCurPath() + { + // Implement this when we write a test that needs it + return ""; + } + + virtual U32 countFilesInDir(const std::string& dirname, const std::string& mask) + { + // Implement this when we write a test that needs it + return 0; + } + + virtual BOOL fileExists(const std::string& pathname) const + { + // Record fileExists() calls so we can check whether caching is + // working right. Certain LLDir calls should be able to make decisions + // without calling fileExists() again, having already checked existence. + mChecked.insert(pathname); + // For our simple flat set of strings, see whether the identical + // pathname exists in our set. + return (mFilesystem.find(pathname) != mFilesystem.end()); + } + + virtual std::string getLLPluginLauncher() + { + // Implement this when we write a test that needs it + return ""; + } + + virtual std::string getLLPluginFilename(std::string base_name) + { + // Implement this when we write a test that needs it + return ""; + } + + /*----------------------------- Dummy data -----------------------------*/ + void clearFilesystem() { mFilesystem.clear(); } + void buildFilesystem(const std::string& path) + { + // Split the pathname on slashes, ignoring leading, trailing, doubles + StringVec components; + LLStringUtil::getTokens(path, components, "/"); + // Ensure we have an entry representing every level of this path + std::string partial; + BOOST_FOREACH(std::string component, components) + { + append(partial, component); + mFilesystem.insert(partial); + } + } + + void clear_checked() { mChecked.clear(); } + void ensure_checked(const std::string& pathname) const + { + tut::ensure(STRINGIZE(pathname << " was not checked but should have been"), + mChecked.find(pathname) != mChecked.end()); + } + void ensure_not_checked(const std::string& pathname) const + { + tut::ensure(STRINGIZE(pathname << " was checked but should not have been"), + mChecked.find(pathname) == mChecked.end()); + } + + std::set mFilesystem; + mutable std::set mChecked; +}; namespace tut { @@ -419,5 +569,192 @@ namespace tut LLFile::rmdir(dir1); LLFile::rmdir(dir2); } -} + template<> template<> + void LLDirTest_object_t::test<6>() + { + set_test_name("findSkinnedFilenames()"); + LLDir_Dummy lldir; + /*------------------------ "default", "en" -------------------------*/ + // Setting "default" means we shouldn't consider any "*/skins/steam" + // directories; setting "en" means we shouldn't consider any "xui/fr" + // directories. + lldir.setSkinFolder("default", "en"); + ensure_equals(lldir.getSkinFolder(), "default"); + ensure_equals(lldir.getLanguage(), "en"); + + // top-level directory of a skin isn't localized + ensure_equals(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", true), + vec(list_of("install/skins/default/colors.xml") + ("user/skins/default/colors.xml"))); + // We should not have needed to check for skins/default/en. We should + // just "know" that SKINBASE is not localized. + lldir.ensure_not_checked("install/skins/default/en"); + + ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_default.jpeg"), + vec(list_of("install/skins/default/textures/only_default.jpeg"))); + // Nor should we have needed to check skins/default/textures/en + // because textures is known to be unlocalized. + lldir.ensure_not_checked("install/skins/default/textures/en"); + + StringVec expected(vec(list_of("install/skins/default/xui/en/strings.xml") + ("user/skins/default/xui/en/strings.xml"))); + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", true), + expected); + // The first time, we had to probe to find out whether xui was localized. + lldir.ensure_checked("install/skins/default/xui/en"); + lldir.clear_checked(); + // Now make the same call again -- should return same result -- + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", true), + expected); + // but this time it should remember that xui is localized. + lldir.ensure_not_checked("install/skins/default/xui/en"); + + // localized subdir with "en-us" instead of "en" + ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"), + vec(list_of("install/skins/default/html/en-us/welcome.html"))); + lldir.ensure_checked("install/skins/default/html/en"); + lldir.ensure_checked("install/skins/default/html/en-us"); + lldir.clear_checked(); + ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"), + vec(list_of("install/skins/default/html/en-us/welcome.html"))); + lldir.ensure_not_checked("install/skins/default/html/en"); + lldir.ensure_not_checked("install/skins/default/html/en-us"); + + ensure_equals(lldir.findSkinnedFilenames("future", "somefile.txt"), + vec(list_of("install/skins/default/future/somefile.txt"))); + // Test probing for an unrecognized unlocalized future subdir. + lldir.ensure_checked("install/skins/default/future/en"); + lldir.clear_checked(); + ensure_equals(lldir.findSkinnedFilenames("future", "somefile.txt"), + vec(list_of("install/skins/default/future/somefile.txt"))); + // Second time it should remember that future is unlocalized. + lldir.ensure_not_checked("install/skins/default/future/en"); + + // When language is set to "en", requesting an html file pulls up the + // "en-us" version -- not because it magically matches those strings, + // but because there's no "en" localization and it falls back on the + // default "en-us"! Note that it would probably still be better to + // make the default localization be "en" and allow "en-gb" (or + // whatever) localizations, which would work much more the way you'd + // expect. + ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"), + vec(list_of("install/skins/default/html/en-us/welcome.html"))); + + /*------------------------ "default", "fr" -------------------------*/ + // We start being able to distinguish localized subdirs from + // unlocalized when we ask for a non-English language. + lldir.setSkinFolder("default", "fr"); + ensure_equals(lldir.getLanguage(), "fr"); + + // pass merge=true to request this filename in all relevant skins + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", true), + vec(list_of + ("install/skins/default/xui/en/strings.xml") + ("install/skins/default/xui/fr/strings.xml") + ("user/skins/default/xui/en/strings.xml") + ("user/skins/default/xui/fr/strings.xml"))); + + // pass (or default) merge=false to request only most specific skin + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"), + vec(list_of + ("user/skins/default/xui/en/strings.xml") + ("user/skins/default/xui/fr/strings.xml"))); + + // The most specific skin for our dummy floater.xml is the installed + // default. Although we have a user xui/fr/floater.xml, we would also + // need a xui/en/floater.xml file to consider the user skin for this. + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "floater.xml"), + vec(list_of + ("install/skins/default/xui/en/floater.xml") + ("install/skins/default/xui/fr/floater.xml"))); + + // The user override for the default skin does define newfile.xml, but + // only an "en" version, not a "fr" version as well. Nonetheless + // that's the most specific skin we have, regardless of the existence + // of a "fr" version in the installed default skin. + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "newfile.xml"), + vec(list_of("user/skins/default/xui/en/newfile.xml"))); + + ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"), + vec(list_of + ("install/skins/default/html/en-us/welcome.html") + ("install/skins/default/html/fr/welcome.html"))); + + /*------------------------ "default", "zh" -------------------------*/ + lldir.setSkinFolder("default", "zh"); + // Because the user default skins strings.xml has only a "fr" override + // but not a "zh" override, the most localized version we can find is "en". + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"), + vec(list_of("user/skins/default/xui/en/strings.xml"))); + + /*------------------------- "steam", "en" --------------------------*/ + lldir.setSkinFolder("steam", "en"); + + ensure_equals(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", true), + vec(list_of + ("install/skins/default/colors.xml") + ("install/skins/steam/colors.xml") + ("user/skins/default/colors.xml") + ("user/skins/steam/colors.xml"))); + + ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_default.jpeg"), + vec(list_of("install/skins/default/textures/only_default.jpeg"))); + + ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_steam.jpeg"), + vec(list_of("install/skins/steam/textures/only_steam.jpeg"))); + + ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_user_default.jpeg"), + vec(list_of("user/skins/default/textures/only_user_default.jpeg"))); + + ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_user_steam.jpeg"), + vec(list_of("user/skins/steam/textures/only_user_steam.jpeg"))); + + // merge=false + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"), + vec(list_of("user/skins/steam/xui/en/strings.xml"))); + + // pass merge=true to request this filename in all relevant skins + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", true), + vec(list_of + ("install/skins/default/xui/en/strings.xml") + ("install/skins/steam/xui/en/strings.xml") + ("user/skins/default/xui/en/strings.xml") + ("user/skins/steam/xui/en/strings.xml"))); + + /*------------------------- "steam", "fr" --------------------------*/ + lldir.setSkinFolder("steam", "fr"); + + // pass merge=true to request this filename in all relevant skins + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"), + vec(list_of + ("user/skins/steam/xui/en/strings.xml") + ("user/skins/steam/xui/fr/strings.xml"))); + + // pass merge=true to request this filename in all relevant skins + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", true), + vec(list_of + ("install/skins/default/xui/en/strings.xml") + ("install/skins/default/xui/fr/strings.xml") + ("install/skins/steam/xui/en/strings.xml") + ("install/skins/steam/xui/fr/strings.xml") + ("user/skins/default/xui/en/strings.xml") + ("user/skins/default/xui/fr/strings.xml") + ("user/skins/steam/xui/en/strings.xml") + ("user/skins/steam/xui/fr/strings.xml"))); + } + + template<> template<> + void LLDirTest_object_t::test<7>() + { + set_test_name("add()"); + LLDir_Dummy lldir; + ensure_equals("both empty", lldir.add("", ""), ""); + ensure_equals("path empty", lldir.add("", "b"), "b"); + ensure_equals("name empty", lldir.add("a", ""), "a"); + ensure_equals("both simple", lldir.add("a", "b"), "a/b"); + ensure_equals("name leading slash", lldir.add("a", "/b"), "a/b"); + ensure_equals("path trailing slash", lldir.add("a/", "b"), "a/b"); + ensure_equals("both bring slashes", lldir.add("a/", "/b"), "a/b"); + } +} diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index e8934d9a9e..e7a8a52a75 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -122,7 +122,6 @@ #include - #if LL_WINDOWS # include // For _SH_DENYWR in initMarkerFile #else @@ -684,7 +683,7 @@ bool LLAppViewer::init() gDirUtilp->initAppDirs("SecondLife"); // set skin search path to default, will be overridden later // this allows simple skinned file lookups to work - gDirUtilp->setSkinFolder("default"); + gDirUtilp->setSkinFolder("default", "en"); initLogging(); @@ -768,12 +767,16 @@ bool LLAppViewer::init() &LLUI::sGLScaleFactor); LL_INFOS("InitInfo") << "UI initialized." << LL_ENDL ; - // Setup paths and LLTrans after LLUI::initClass has been called. - LLUI::setupPaths(); + // NOW LLUI::getLanguage() should work. gDirUtilp must know the language + // for this session ASAP so all the file-loading commands that follow, + // that use findSkinnedFilenames(), will include the localized files. + gDirUtilp->setSkinFolder(gDirUtilp->getSkinFolder(), LLUI::getLanguage()); + + // Setup LLTrans after LLUI::initClass has been called. LLTransUtil::parseStrings("strings.xml", default_trans_args); LLTransUtil::parseLanguageStrings("language_settings.xml"); - // Setup notifications after LLUI::setupPaths() has been called. + // Setup notifications after LLUI::initClass() has been called. LLNotifications::instance(); LL_INFOS("InitInfo") << "Notifications initialized." << LL_ENDL ; @@ -2242,8 +2245,7 @@ bool LLAppViewer::initConfiguration() OSMessageBox(msg.str(),LLStringUtil::null,OSMB_OK); return false; } - - LLUI::setupPaths(); // setup paths for LLTrans based on settings files only + LLTransUtil::parseStrings("strings.xml", default_trans_args); LLTransUtil::parseLanguageStrings("language_settings.xml"); // - set procedural settings @@ -2559,13 +2561,15 @@ bool LLAppViewer::initConfiguration() LLStartUp::setStartSLURL(start_slurl); } - const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent"); - if(skinfolder && LLStringUtil::null != skinfolder->getValue().asString()) - { - // hack to force the skin to default. - gDirUtilp->setSkinFolder(skinfolder->getValue().asString()); - //gDirUtilp->setSkinFolder("default"); - } + const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent"); + if(skinfolder && LLStringUtil::null != skinfolder->getValue().asString()) + { + // Examining "Language" may not suffice -- see LLUI::getLanguage() + // logic. Unfortunately LLUI::getLanguage() doesn't yet do us much + // good because we haven't yet called LLUI::initClass(). + gDirUtilp->setSkinFolder(skinfolder->getValue().asString(), + gSavedSettings.getString("Language")); + } if (gSavedSettings.getBOOL("SpellCheck")) { @@ -3589,8 +3593,7 @@ void LLAppViewer::migrateCacheDirectory() { gSavedSettings.setBOOL("MigrateCacheDirectory", FALSE); - std::string delimiter = gDirUtilp->getDirDelimiter(); - std::string old_cache_dir = gDirUtilp->getOSUserAppDir() + delimiter + "cache"; + std::string old_cache_dir = gDirUtilp->add(gDirUtilp->getOSUserAppDir(), "cache"); std::string new_cache_dir = gDirUtilp->getCacheDir(true); if (gDirUtilp->fileExists(old_cache_dir)) @@ -3606,8 +3609,8 @@ void LLAppViewer::migrateCacheDirectory() while (iter.next(file_name)) { if (file_name == "." || file_name == "..") continue; - std::string source_path = old_cache_dir + delimiter + file_name; - std::string dest_path = new_cache_dir + delimiter + file_name; + std::string source_path = gDirUtilp->add(old_cache_dir, file_name); + std::string dest_path = gDirUtilp->add(new_cache_dir, file_name); if (!LLFile::rename(source_path, dest_path)) { file_count++; @@ -3838,7 +3841,7 @@ bool LLAppViewer::initCache() LLDirIterator iter(dir, mask); if (iter.next(found_file)) { - old_vfs_data_file = dir + gDirUtilp->getDirDelimiter() + found_file; + old_vfs_data_file = gDirUtilp->add(dir, found_file); S32 start_pos = found_file.find_last_of('.'); if (start_pos > 0) @@ -5149,20 +5152,20 @@ void LLAppViewer::launchUpdater() // we tell the updater where to find the xml containing string // translations which it can use for its own UI std::string xml_strings_file = "strings.xml"; - std::vector xui_path_vec = LLUI::getXUIPaths(); + std::vector xui_path_vec = + gDirUtilp->findSkinnedFilenames(LLDir::XUI, xml_strings_file); std::string xml_search_paths; - std::vector::const_iterator iter; + const char* delim = ""; // build comma-delimited list of xml paths to pass to updater - for (iter = xui_path_vec.begin(); iter != xui_path_vec.end(); ) - { - std::string this_skin_dir = gDirUtilp->getDefaultSkinDir() - + gDirUtilp->getDirDelimiter() - + (*iter); - llinfos << "Got a XUI path: " << this_skin_dir << llendl; - xml_search_paths.append(this_skin_dir); - ++iter; - if (iter != xui_path_vec.end()) - xml_search_paths.append(","); // comma-delimit + BOOST_FOREACH(std::string this_skin_path, xui_path_vec) + { + // Although we already have the full set of paths with the filename + // appended, the linux-updater.bin command-line switches require us to + // snip the filename OFF and pass it as a separate switch argument. :-P + llinfos << "Got a XUI path: " << this_skin_path << llendl; + xml_search_paths.append(delim); + xml_search_paths.append(gDirUtilp->getDirName(this_skin_path)); + delim = ","; } // build the overall command-line to run the updater correctly LLAppViewer::sUpdaterInfo->mUpdateExePath = diff --git a/indra/newview/lldaycyclemanager.cpp b/indra/newview/lldaycyclemanager.cpp index 347a467a8b..8af2f4ea33 100644 --- a/indra/newview/lldaycyclemanager.cpp +++ b/indra/newview/lldaycyclemanager.cpp @@ -184,7 +184,7 @@ void LLDayCycleManager::loadPresets(const std::string& dir) { std::string file; if (!dir_iter.next(file)) break; // no more files - loadPreset(dir + file); + loadPreset(gDirUtilp->add(dir, file)); } } diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp index d741b5b133..15e0b89f6c 100644 --- a/indra/newview/llfloateruipreview.cpp +++ b/indra/newview/llfloateruipreview.cpp @@ -137,7 +137,7 @@ public: virtual ~LLFloaterUIPreview(); std::string getLocStr(S32 ID); // fetches the localization string based on what is selected in the drop-down menu - void displayFloater(BOOL click, S32 ID, bool save = false); // needs to be public so live file can call it when it finds an update + void displayFloater(BOOL click, S32 ID); // needs to be public so live file can call it when it finds an update /*virtual*/ BOOL postBuild(); /*virtual*/ void onClose(bool app_quitting); @@ -291,7 +291,8 @@ LLLocalizationResetForcer::LLLocalizationResetForcer(LLFloaterUIPreview* floater { mSavedLocalization = LLUI::sSettingGroups["config"]->getString("Language"); // save current localization setting LLUI::sSettingGroups["config"]->setString("Language", floater->getLocStr(ID));// hack language to be the one we want to preview floaters in - LLUI::setupPaths(); // forcibly reset XUI paths with this new language + // forcibly reset XUI paths with this new language + gDirUtilp->setSkinFolder(gDirUtilp->getSkinFolder(), floater->getLocStr(ID)); } // Actually reset in destructor @@ -299,7 +300,8 @@ LLLocalizationResetForcer::LLLocalizationResetForcer(LLFloaterUIPreview* floater LLLocalizationResetForcer::~LLLocalizationResetForcer() { LLUI::sSettingGroups["config"]->setString("Language", mSavedLocalization); // reset language to what it was before we changed it - LLUI::setupPaths(); // forcibly reset XUI paths with this new language + // forcibly reset XUI paths with this new language + gDirUtilp->setSkinFolder(gDirUtilp->getSkinFolder(), mSavedLocalization); } // Live file constructor @@ -488,7 +490,7 @@ BOOL LLFloaterUIPreview::postBuild() { if((found = iter.next(language_directory))) // get next directory { - std::string full_path = xui_dir + language_directory; + std::string full_path = gDirUtilp->add(xui_dir, language_directory); if(LLFile::isfile(full_path.c_str())) // if it's not a directory, skip it { continue; @@ -773,7 +775,8 @@ void LLFloaterUIPreview::onClickDisplayFloater(S32 caller_id) // Saves the current floater/panel void LLFloaterUIPreview::onClickSaveFloater(S32 caller_id) { - displayFloater(TRUE, caller_id, true); + displayFloater(TRUE, caller_id); + popupAndPrintWarning("Save-floater functionality removed, use XML schema to clean up XUI files"); } // Saves all floater/panels @@ -784,25 +787,15 @@ void LLFloaterUIPreview::onClickSaveAll(S32 caller_id) for (int index = 0; index < listSize; index++) { mFileList->selectNthItem(index); - displayFloater(TRUE, caller_id, true); + displayFloater(TRUE, caller_id); } -} - -// Given path to floater or panel XML file "filename.xml", -// returns "filename_new.xml" -static std::string append_new_to_xml_filename(const std::string& path) -{ - std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getLocalizedSkinPath(), path); - std::string::size_type extension_pos = full_filename.rfind(".xml"); - full_filename.resize(extension_pos); - full_filename += "_new.xml"; - return full_filename; + popupAndPrintWarning("Save-floater functionality removed, use XML schema to clean up XUI files"); } // Actually display the floater // Only set up a new live file if this came from a click (at which point there should be no existing live file), rather than from the live file's update itself; // otherwise, we get an infinite loop as the live file keeps recreating itself. That means this function is generally called twice. -void LLFloaterUIPreview::displayFloater(BOOL click, S32 ID, bool save) +void LLFloaterUIPreview::displayFloater(BOOL click, S32 ID) { // Convince UI that we're in a different language (the one selected on the drop-down menu) LLLocalizationResetForcer reset_forcer(this, ID); // save old language in reset forcer object (to be reset upon destruction when it falls out of scope) @@ -843,48 +836,13 @@ void LLFloaterUIPreview::displayFloater(BOOL click, S32 ID, bool save) if(!strncmp(path.c_str(),"floater_",8) || !strncmp(path.c_str(), "inspect_", 8)) // if it's a floater { - if (save) - { - LLXMLNodePtr floater_write = new LLXMLNode(); - (*floaterp)->buildFromFile(path, floater_write); // just build it - - if (!floater_write->isNull()) - { - std::string full_filename = append_new_to_xml_filename(path); - LLFILE* floater_temp = LLFile::fopen(full_filename.c_str(), "w"); - LLXMLNode::writeHeaderToFile(floater_temp); - const bool use_type_decorations = false; - floater_write->writeToFile(floater_temp, std::string(), use_type_decorations); - fclose(floater_temp); - } - } - else - { - (*floaterp)->buildFromFile(path); // just build it - (*floaterp)->openFloater((*floaterp)->getKey()); - (*floaterp)->setCanResize((*floaterp)->isResizable()); - } - + (*floaterp)->buildFromFile(path); // just build it + (*floaterp)->openFloater((*floaterp)->getKey()); + (*floaterp)->setCanResize((*floaterp)->isResizable()); } else if (!strncmp(path.c_str(),"menu_",5)) // if it's a menu { - if (save) - { - LLXMLNodePtr menu_write = new LLXMLNode(); - LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile(path, gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance(), menu_write); - - if (!menu_write->isNull()) - { - std::string full_filename = append_new_to_xml_filename(path); - LLFILE* menu_temp = LLFile::fopen(full_filename.c_str(), "w"); - LLXMLNode::writeHeaderToFile(menu_temp); - const bool use_type_decorations = false; - menu_write->writeToFile(menu_temp, std::string(), use_type_decorations); - fclose(menu_temp); - } - - delete menu; - } + // former 'save' processing excised } else // if it is a panel... { @@ -896,39 +854,21 @@ void LLFloaterUIPreview::displayFloater(BOOL click, S32 ID, bool save) LLPanel::Params panel_params; LLPanel* panel = LLUICtrlFactory::create(panel_params); // create a new panel - if (save) - { - LLXMLNodePtr panel_write = new LLXMLNode(); - panel->buildFromFile(path, panel_write); // build it - - if (!panel_write->isNull()) - { - std::string full_filename = append_new_to_xml_filename(path); - LLFILE* panel_temp = LLFile::fopen(full_filename.c_str(), "w"); - LLXMLNode::writeHeaderToFile(panel_temp); - const bool use_type_decorations = false; - panel_write->writeToFile(panel_temp, std::string(), use_type_decorations); - fclose(panel_temp); - } - } - else - { - panel->buildFromFile(path); // build it - LLRect new_size = panel->getRect(); // get its rectangle - panel->setOrigin(2,2); // reset its origin point so it's not offset by -left or other XUI attributes - (*floaterp)->setTitle(path); // use the file name as its title, since panels have no guaranteed meaningful name attribute - panel->setUseBoundingRect(TRUE); // enable the use of its outer bounding rect (normally disabled because it's O(n) on the number of sub-elements) - panel->updateBoundingRect(); // update bounding rect - LLRect bounding_rect = panel->getBoundingRect(); // get the bounding rect - LLRect new_rect = panel->getRect(); // get the panel's rect - new_rect.unionWith(bounding_rect); // union them to make sure we get the biggest one possible - LLRect floater_rect = new_rect; - floater_rect.stretch(4, 4); - (*floaterp)->reshape(floater_rect.getWidth(), floater_rect.getHeight() + floater_header_size); // reshape floater to match the union rect's dimensions - panel->reshape(new_rect.getWidth(), new_rect.getHeight()); // reshape panel to match the union rect's dimensions as well (both are needed) - (*floaterp)->addChild(panel); // add panel as child - (*floaterp)->openFloater(); // open floater (needed?) - } + panel->buildFromFile(path); // build it + LLRect new_size = panel->getRect(); // get its rectangle + panel->setOrigin(2,2); // reset its origin point so it's not offset by -left or other XUI attributes + (*floaterp)->setTitle(path); // use the file name as its title, since panels have no guaranteed meaningful name attribute + panel->setUseBoundingRect(TRUE); // enable the use of its outer bounding rect (normally disabled because it's O(n) on the number of sub-elements) + panel->updateBoundingRect(); // update bounding rect + LLRect bounding_rect = panel->getBoundingRect(); // get the bounding rect + LLRect new_rect = panel->getRect(); // get the panel's rect + new_rect.unionWith(bounding_rect); // union them to make sure we get the biggest one possible + LLRect floater_rect = new_rect; + floater_rect.stretch(4, 4); + (*floaterp)->reshape(floater_rect.getWidth(), floater_rect.getHeight() + floater_header_size); // reshape floater to match the union rect's dimensions + panel->reshape(new_rect.getWidth(), new_rect.getHeight()); // reshape panel to match the union rect's dimensions as well (both are needed) + (*floaterp)->addChild(panel); // add panel as child + (*floaterp)->openFloater(); // open floater (needed?) } if(ID == 1) @@ -964,7 +904,7 @@ void LLFloaterUIPreview::displayFloater(BOOL click, S32 ID, bool save) (*floaterp)->center(); addDependentFloater(*floaterp); - if(click && ID == 1 && !save) + if(click && ID == 1) { // set up live file to track it if(mLiveFile) diff --git a/indra/newview/llhints.cpp b/indra/newview/llhints.cpp index e15862e2a4..197408b40e 100644 --- a/indra/newview/llhints.cpp +++ b/indra/newview/llhints.cpp @@ -171,12 +171,12 @@ LLHintPopup::LLHintPopup(const LLHintPopup::Params& p) } if (p.hint_image.isProvided()) { - buildFromFile("panel_hint_image.xml", NULL, p); + buildFromFile("panel_hint_image.xml", p); getChild("hint_image")->setImage(p.hint_image()); } else { - buildFromFile( "panel_hint.xml", NULL, p); + buildFromFile( "panel_hint.xml", p); } } diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp index 7650fe9229..99b4707158 100644 --- a/indra/newview/llmediactrl.cpp +++ b/indra/newview/llmediactrl.cpp @@ -564,32 +564,13 @@ void LLMediaCtrl::navigateTo( std::string url_in, std::string mime_type) // void LLMediaCtrl::navigateToLocalPage( const std::string& subdir, const std::string& filename_in ) { - std::string language = LLUI::getLanguage(); - std::string delim = gDirUtilp->getDirDelimiter(); - std::string filename; + std::string filename(gDirUtilp->add(subdir, filename_in)); + std::string expanded_filename = gDirUtilp->findSkinnedFilename("html", filename); - filename += subdir; - filename += delim; - filename += filename_in; - - std::string expanded_filename = gDirUtilp->findSkinnedFilename("html", language, filename); - - if (! gDirUtilp->fileExists(expanded_filename)) + if (expanded_filename.empty()) { - if (language != "en") - { - expanded_filename = gDirUtilp->findSkinnedFilename("html", "en", filename); - if (! gDirUtilp->fileExists(expanded_filename)) - { - llwarns << "File " << subdir << delim << filename_in << "not found" << llendl; - return; - } - } - else - { - llwarns << "File " << subdir << delim << filename_in << "not found" << llendl; - return; - } + llwarns << "File " << filename << "not found" << llendl; + return; } if (ensureMediaSourceExists()) { @@ -597,7 +578,6 @@ void LLMediaCtrl::navigateToLocalPage( const std::string& subdir, const std::str mMediaSource->setSize(mTextureWidth, mTextureHeight); mMediaSource->navigateTo(expanded_filename, "text/html", false); } - } //////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 88727bf59b..9c25e69db0 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -815,7 +815,7 @@ void LLScriptEdCore::onBtnDynamicHelp() if (!live_help_floater) { live_help_floater = new LLFloater(LLSD()); - live_help_floater->buildFromFile("floater_lsl_guide.xml", NULL); + live_help_floater->buildFromFile("floater_lsl_guide.xml"); LLFloater* parent = dynamic_cast(getParent()); llassert(parent); if (parent) diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index 0cb6c85012..2002647fef 100644 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -242,7 +242,7 @@ LLIMWellWindow::RowPanel::RowPanel(const LLSysWellWindow* parent, const LLUUID& S32 chicletCounter, const std::string& name, const LLUUID& otherParticipantId) : LLPanel(LLPanel::Params()), mChiclet(NULL), mParent(parent) { - buildFromFile( "panel_activeim_row.xml", NULL); + buildFromFile( "panel_activeim_row.xml"); // Choose which of the pre-created chiclets (IM/group) to use. // The other one gets hidden. @@ -356,7 +356,7 @@ LLIMWellWindow::ObjectRowPanel::ObjectRowPanel(const LLUUID& notification_id, bo : LLPanel() , mChiclet(NULL) { - buildFromFile( "panel_active_object_row.xml", NULL); + buildFromFile( "panel_active_object_row.xml"); initChiclet(notification_id); diff --git a/indra/newview/lltoast.cpp b/indra/newview/lltoast.cpp index 0eec7f0afd..9dfb29b905 100644 --- a/indra/newview/lltoast.cpp +++ b/indra/newview/lltoast.cpp @@ -118,7 +118,7 @@ LLToast::LLToast(const LLToast::Params& p) { mTimer.reset(new LLToastLifeTimer(this, p.lifetime_secs)); - buildFromFile("panel_toast.xml", NULL); + buildFromFile("panel_toast.xml"); setCanDrag(FALSE); diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 1eb4bedfaf..47059b0b8c 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -1184,12 +1184,9 @@ void LLViewerMedia::clearAllCookies() LLDirIterator dir_iter(base_dir, "*_*"); while (dir_iter.next(filename)) { - target = base_dir; - target += filename; - target += gDirUtilp->getDirDelimiter(); - target += "browser_profile"; - target += gDirUtilp->getDirDelimiter(); - target += "cookies"; + target = gDirUtilp->add(base_dir, filename); + gDirUtilp->append(target, "browser_profile"); + gDirUtilp->append(target, "cookies"); lldebugs << "target = " << target << llendl; if(LLFile::isfile(target)) { @@ -1197,10 +1194,8 @@ void LLViewerMedia::clearAllCookies() } // Other accounts may have new-style cookie files too -- delete them as well - target = base_dir; - target += filename; - target += gDirUtilp->getDirDelimiter(); - target += PLUGIN_COOKIE_FILE_NAME; + target = gDirUtilp->add(base_dir, filename); + gDirUtilp->append(target, PLUGIN_COOKIE_FILE_NAME); lldebugs << "target = " << target << llendl; if(LLFile::isfile(target)) { diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 9a6c0569a9..7eb1a202a0 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -1583,49 +1583,42 @@ struct UIImageDeclarations : public LLInitParam::Block bool LLUIImageList::initFromFile() { - // construct path to canonical textures.xml in default skin dir - std::string base_file_path = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "default", "textures", "textures.xml"); + // Look for textures.xml in all the right places. Pass merge=true because + // we want to overlay textures.xml from all the skins directories. + std::vector textures_paths = + gDirUtilp->findSkinnedFilenames(LLDir::TEXTURES, "textures.xml", true); + std::vector::const_iterator pi(textures_paths.begin()), pend(textures_paths.end()); + if (pi == pend) + { + llwarns << "No textures.xml found in skins directories" << llendl; + return false; + } + // The first (most generic) file gets special validations LLXMLNodePtr root; - - if (!LLXMLNode::parseFile(base_file_path, root, NULL)) + if (!LLXMLNode::parseFile(*pi, root, NULL)) { - llwarns << "Unable to parse UI image list file " << base_file_path << llendl; + llwarns << "Unable to parse UI image list file " << *pi << llendl; return false; } if (!root->hasAttribute("version")) { - llwarns << "No valid version number in UI image list file " << base_file_path << llendl; + llwarns << "No valid version number in UI image list file " << *pi << llendl; return false; } UIImageDeclarations images; LLXUIParser parser; - parser.readXUI(root, images, base_file_path); - - // add components defined in current skin - std::string skin_update_path = gDirUtilp->getSkinDir() - + gDirUtilp->getDirDelimiter() - + "textures" - + gDirUtilp->getDirDelimiter() - + "textures.xml"; - LLXMLNodePtr update_root; - if (skin_update_path != base_file_path - && LLXMLNode::parseFile(skin_update_path, update_root, NULL)) - { - parser.readXUI(update_root, images, skin_update_path); - } - - // add components defined in user override of current skin - skin_update_path = gDirUtilp->getUserSkinDir() - + gDirUtilp->getDirDelimiter() - + "textures" - + gDirUtilp->getDirDelimiter() - + "textures.xml"; - if (skin_update_path != base_file_path - && LLXMLNode::parseFile(skin_update_path, update_root, NULL)) - { - parser.readXUI(update_root, images, skin_update_path); + parser.readXUI(root, images, *pi); + + // add components defined in the rest of the skin paths + while (++pi != pend) + { + LLXMLNodePtr update_root; + if (LLXMLNode::parseFile(*pi, update_root, NULL)) + { + parser.readXUI(update_root, images, *pi); + } } if (!images.validateBlock()) return false; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 30bb787fa7..e569c9504f 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1685,8 +1685,7 @@ LLViewerWindow::LLViewerWindow(const Params& p) LLFontGL::initClass( gSavedSettings.getF32("FontScreenDPI"), mDisplayScale.mV[VX], mDisplayScale.mV[VY], - gDirUtilp->getAppRODataDir(), - LLUI::getXUIPaths()); + gDirUtilp->getAppRODataDir()); // Create container for all sub-views LLView::Params rvp; @@ -4757,8 +4756,7 @@ void LLViewerWindow::initFonts(F32 zoom_factor) LLFontGL::initClass( gSavedSettings.getF32("FontScreenDPI"), mDisplayScale.mV[VX] * zoom_factor, mDisplayScale.mV[VY] * zoom_factor, - gDirUtilp->getAppRODataDir(), - LLUI::getXUIPaths()); + gDirUtilp->getAppRODataDir()); // Force font reloads, which can be very slow LLFontGL::loadDefaultFonts(); } diff --git a/indra/newview/llwaterparammanager.cpp b/indra/newview/llwaterparammanager.cpp index e386112334..4f52ff9778 100644 --- a/indra/newview/llwaterparammanager.cpp +++ b/indra/newview/llwaterparammanager.cpp @@ -100,7 +100,7 @@ void LLWaterParamManager::loadPresetsFromDir(const std::string& dir) break; // no more files } - std::string path = dir + file; + std::string path = gDirUtilp->add(dir, file); if (!loadPreset(path)) { llwarns << "Error loading water preset from " << path << llendl; diff --git a/indra/newview/llwlparammanager.cpp b/indra/newview/llwlparammanager.cpp index 49d9d44d74..6077208799 100644 --- a/indra/newview/llwlparammanager.cpp +++ b/indra/newview/llwlparammanager.cpp @@ -283,7 +283,7 @@ void LLWLParamManager::loadPresetsFromDir(const std::string& dir) break; // no more files } - std::string path = dir + file; + std::string path = gDirUtilp->add(dir, file); if (!loadPreset(path)) { llwarns << "Error loading sky preset from " << path << llendl; diff --git a/indra/newview/skins/paths.xml b/indra/newview/skins/paths.xml deleted file mode 100644 index 3c0da041c7..0000000000 --- a/indra/newview/skins/paths.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - xui - en - - - xui - [LANGUAGE] - - \ No newline at end of file diff --git a/indra/newview/tests/lldir_stub.cpp b/indra/newview/tests/lldir_stub.cpp index 18cf4e7419..3c0a4377d8 100644 --- a/indra/newview/tests/lldir_stub.cpp +++ b/indra/newview/tests/lldir_stub.cpp @@ -32,7 +32,7 @@ BOOL LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask void LLDir::setChatLogsDir(const std::string &path) {} void LLDir::setPerAccountChatLogsDir(const std::string &first, const std::string &last) {} void LLDir::setLindenUserDir(const std::string &first, const std::string &last) {} -void LLDir::setSkinFolder(const std::string &skin_folder) {} +void LLDir::setSkinFolder(const std::string &skin_folder, const std::string& language) {} bool LLDir::setCacheDir(const std::string &path) { return true; } void LLDir::dumpCurrentDirectories() {} diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 7c6b5403e1..e754c26733 100644 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -114,7 +114,6 @@ class ViewerManifest(LLManifest): # skins if self.prefix(src="skins"): - self.path("paths.xml") # include the entire textures directory recursively if self.prefix(src="*/textures"): self.path("*/*.tga") diff --git a/indra/viewer_components/updater/tests/llupdaterservice_test.cpp b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp index 7c016fecf9..db52e6c55f 100644 --- a/indra/viewer_components/updater/tests/llupdaterservice_test.cpp +++ b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp @@ -78,7 +78,9 @@ S32 LLDir::deleteFilesInDir(const std::string &dirname, void LLDir::setChatLogsDir(const std::string &path){} void LLDir::setPerAccountChatLogsDir(const std::string &username){} void LLDir::setLindenUserDir(const std::string &username){} -void LLDir::setSkinFolder(const std::string &skin_folder){} +void LLDir::setSkinFolder(const std::string &skin_folder, const std::string& language){} +std::string LLDir::getSkinFolder() const { return "default"; } +std::string LLDir::getLanguage() const { return "en"; } bool LLDir::setCacheDir(const std::string &path){ return true; } void LLDir::dumpCurrentDirectories() {} -- cgit v1.2.3 From fdddd37db095dbbefb17b1725db5fb9527ba3fd9 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 10 Oct 2012 17:18:26 -0400 Subject: Remove HelpUseLocal setting variable. Hide local html skin dir. We assert that the local html directory is no longer used. Remove machinery related to its use, notably HelpUseLocal, the code that checks it and the code that sets and examines special flag URL "__local". Before actually killing off the local skins/default/html directory, make viewer_manifest.py rename it but continue packaging it as html.old. If this doesn't cause a panic, we can proceed with removing it entirely. --- indra/newview/app_settings/settings.xml | 11 ----------- indra/newview/llfloaterhelpbrowser.cpp | 10 +--------- indra/newview/llstartup.cpp | 5 +---- indra/newview/llviewerhelp.cpp | 6 ------ indra/newview/viewer_manifest.py | 27 +++++++++++++++++++++++++++ 5 files changed, 29 insertions(+), 30 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 1bf773bb9e..318a18912a 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4040,17 +4040,6 @@ Value 305 - HelpUseLocal - - Comment - If set, always use this for help: skins/default/html/[LANGUAGE]/help-offline/index.html - Persist - 0 - Type - Boolean - Value - 0 - HelpURLFormat Comment diff --git a/indra/newview/llfloaterhelpbrowser.cpp b/indra/newview/llfloaterhelpbrowser.cpp index fd9c37ae73..4cb632bd6a 100644 --- a/indra/newview/llfloaterhelpbrowser.cpp +++ b/indra/newview/llfloaterhelpbrowser.cpp @@ -77,15 +77,7 @@ void LLFloaterHelpBrowser::onOpen(const LLSD& key) gSavedSettings.setBOOL("HelpFloaterOpen", TRUE); std::string topic = key.asString(); - - if (topic == "__local") - { - mBrowser->navigateToLocalPage( "help-offline" , "index.html" ); - } - else - { - mBrowser->navigateTo(LLViewerHelp::instance().getURL(topic)); - } + mBrowser->navigateTo(LLViewerHelp::instance().getURL(topic)); } //virtual diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 218c35029e..5bfdbf89e9 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -3281,11 +3281,8 @@ bool process_login_success_response() { // replace the default help URL format gSavedSettings.setString("HelpURLFormat",text); - - // don't fall back to Standalone's pre-connection static help - gSavedSettings.setBOOL("HelpUseLocal", false); } - + std::string home_location = response["home"]; if(!home_location.empty()) { diff --git a/indra/newview/llviewerhelp.cpp b/indra/newview/llviewerhelp.cpp index a8a918f259..04c2e27c9d 100644 --- a/indra/newview/llviewerhelp.cpp +++ b/indra/newview/llviewerhelp.cpp @@ -71,12 +71,6 @@ LLHelpHandler gHelpHandler; std::string LLViewerHelp::getURL(const std::string &topic) { - // allow overriding the help server with a local help file - if( gSavedSettings.getBOOL("HelpUseLocal") ) - { - return "__local"; - } - // if the help topic is empty, use the default topic std::string help_topic = topic; if (help_topic.empty()) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index e754c26733..4f1b58dfcb 100644 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -28,7 +28,10 @@ $/LicenseInfo$ """ import sys import os.path +import errno +import glob import re +import shutil import tarfile import time import random @@ -136,6 +139,30 @@ class ViewerManifest(LLManifest): self.path("*/*/*.html") self.path("*/*/*.gif") self.end_prefix("*/html") + + # The claim is that we never use local html files any + # longer. But rather than commenting out the "*/html" + # block above, let's rename every html subdirectory we + # copied as html.old. That way, if we're wrong, a user + # actually does have the relevant files; s/he just needs + # to rename every html.old directory back to html to + # recover them. (Possibly I could accomplish the rename + # with clever use of self.prefix(), but the leading "*" + # perplexes me.) + for htmldir in glob.glob(os.path.join(self.get_dst_prefix(), "*", "html")): + htmlold = htmldir + ".old" + print "Renaming %r => %r" % (htmldir, os.path.basename(htmlold)) + try: + os.rename(htmldir, htmlold) + except OSError, err: + if err.errno != errno.ENOTEMPTY: + raise + # If we already have a directory by that name and + # it's not empty, remove it and retry. + shutil.rmtree(htmlold) + # If it still blows up, let the exception propagate. + os.rename(htmldir, htmlold) + self.end_prefix("skins") # local_assets dir (for pre-cached textures) -- cgit v1.2.3 From 9e4927ece5448c94bc3f46f7be019c0ffe14a686 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 11 Oct 2012 17:29:04 -0400 Subject: Make LLTransUtil::parseStrings() merge all relevant strings.xml files. Until now, adding a xui/en/strings.xml file in any non-default skin meant you had to clone the entire file, editing only the particular entries you wanted to override. With this change, we load strings.xml file(s) from the default skin before loading the specified skin -- so a non-default skin can now provide a strings.xml file containing only the specific entries it wants to override. --- indra/llui/lltransutil.cpp | 20 ++++++++++++++++---- indra/llxml/llxmlnode.cpp | 3 ++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/indra/llui/lltransutil.cpp b/indra/llui/lltransutil.cpp index 58fa8a0828..97a72b55e1 100644 --- a/indra/llui/lltransutil.cpp +++ b/indra/llui/lltransutil.cpp @@ -31,15 +31,27 @@ #include "lltrans.h" #include "lluictrlfactory.h" #include "llxmlnode.h" - +#include "lldir.h" bool LLTransUtil::parseStrings(const std::string& xml_filename, const std::set& default_args) { + // LLUICtrlFactory::getLayeredXMLNode() just calls + // gDirUtilp->findSkinnedFilenames(merge=false) and then passes the + // resulting paths to LLXMLNode::getLayeredXMLNode(). Bypass that and call + // LLXMLNode::getLayeredXMLNode() directly: we want merge=true. + std::vector paths = + gDirUtilp->findSkinnedFilenames(LLDir::XUI, xml_filename, true); + if (paths.empty()) + { + // xml_filename not found at all in any skin -- check whether entire + // path was passed (but I hope we no longer have callers who do that) + paths.push_back(xml_filename); + } LLXMLNodePtr root; - BOOL success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root); + bool success = LLXMLNode::getLayeredXMLNode(root, paths); if (!success) { - llerrs << "Couldn't load string table" << llendl; + llerrs << "Couldn't load string table " << xml_filename << llendl; return false; } @@ -54,7 +66,7 @@ bool LLTransUtil::parseLanguageStrings(const std::string& xml_filename) if (!success) { - llerrs << "Couldn't load string table " << xml_filename << llendl; + llerrs << "Couldn't load localization table " << xml_filename << llendl; return false; } diff --git a/indra/llxml/llxmlnode.cpp b/indra/llxml/llxmlnode.cpp index 2ffb0d8503..b775249219 100644 --- a/indra/llxml/llxmlnode.cpp +++ b/indra/llxml/llxmlnode.cpp @@ -897,7 +897,8 @@ bool LLXMLNode::getLayeredXMLNode(LLXMLNodePtr& root, std::vector::const_iterator itor; - for (itor = paths.begin(), ++itor; itor != paths.end(); ++itor) + // We've already dealt with the first item, skip that one + for (itor = paths.begin() + 1; itor != paths.end(); ++itor) { std::string layer_filename = *itor; if(layer_filename.empty() || layer_filename == filename) -- cgit v1.2.3 From fdb0e001f70f40267fa5b42e2d97b7128918b5ad Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 11 Oct 2012 19:01:49 -0400 Subject: Use viewer_manifest.py magic to rename embedded html dir to html.old. Previous attempt at this same feat copied "*/html" and then iterated through every such directory in the copy-target space, renaming each individually with os.rename(). Richard kindly pointed out that it can be done more simply by using a viewer_manifest.py feature permitting wildcards even in dst= args. --- indra/newview/viewer_manifest.py | 34 +++++++--------------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 4f1b58dfcb..96b14413ae 100644 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -28,10 +28,7 @@ $/LicenseInfo$ """ import sys import os.path -import errno -import glob import re -import shutil import tarfile import time import random @@ -134,35 +131,18 @@ class ViewerManifest(LLManifest): self.path("*/*.xml") # Local HTML files (e.g. loading screen) - if self.prefix(src="*/html"): + # The claim is that we never use local html files any + # longer. But rather than commenting out this block, let's + # rename every html subdirectory as html.old. That way, if + # we're wrong, a user actually does have the relevant + # files; s/he just needs to rename every html.old + # directory back to html to recover them. + if self.prefix(src="*/html", dst="*/html.old"): self.path("*.png") self.path("*/*/*.html") self.path("*/*/*.gif") self.end_prefix("*/html") - # The claim is that we never use local html files any - # longer. But rather than commenting out the "*/html" - # block above, let's rename every html subdirectory we - # copied as html.old. That way, if we're wrong, a user - # actually does have the relevant files; s/he just needs - # to rename every html.old directory back to html to - # recover them. (Possibly I could accomplish the rename - # with clever use of self.prefix(), but the leading "*" - # perplexes me.) - for htmldir in glob.glob(os.path.join(self.get_dst_prefix(), "*", "html")): - htmlold = htmldir + ".old" - print "Renaming %r => %r" % (htmldir, os.path.basename(htmlold)) - try: - os.rename(htmldir, htmlold) - except OSError, err: - if err.errno != errno.ENOTEMPTY: - raise - # If we already have a directory by that name and - # it's not empty, remove it and retry. - shutil.rmtree(htmlold) - # If it still blows up, let the exception propagate. - os.rename(htmldir, htmlold) - self.end_prefix("skins") # local_assets dir (for pre-cached textures) -- cgit v1.2.3 From 730d13a76a4b06e6dbdd4bd5829a90bcb98b52b3 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 11 Oct 2012 19:51:07 -0400 Subject: Change LLDir::findSkinnedFilenames() to use enum instead of bool. At Richard's suggestion, changed the bool merge parameter to new enum ESkinConstraint with values CURRENT_SKIN and ALL_SKINS. This clarifies what we're requesting at the point of the call. --- indra/llui/llnotifications.cpp | 7 ++++--- indra/llui/lltransutil.cpp | 9 +++++---- indra/llui/lluicolortable.cpp | 5 +++-- indra/llvfs/lldir.cpp | 19 ++++++++++--------- indra/llvfs/lldir.h | 22 ++++++++++++---------- indra/llvfs/tests/lldir_test.cpp | 14 +++++++------- indra/newview/llviewertexturelist.cpp | 7 ++++--- 7 files changed, 45 insertions(+), 38 deletions(-) diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 4fbee8cd80..210a320f41 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -1424,10 +1424,11 @@ void addPathIfExists(const std::string& new_path, std::vector& path bool LLNotifications::loadTemplates() { llinfos << "Reading notifications template" << llendl; - // Passing findSkinnedFilenames(merge=true) makes it output all relevant - // pathnames instead of just the ones from the most specific skin. + // Passing findSkinnedFilenames(constraint=LLDir::ALL_SKINS) makes it + // output all relevant pathnames instead of just the ones from the most + // specific skin. std::vector search_paths = - gDirUtilp->findSkinnedFilenames(LLDir::XUI, "notifications.xml", true); + gDirUtilp->findSkinnedFilenames(LLDir::XUI, "notifications.xml", LLDir::ALL_SKINS); std::string base_filename = search_paths.front(); LLXMLNodePtr root; diff --git a/indra/llui/lltransutil.cpp b/indra/llui/lltransutil.cpp index 97a72b55e1..3b7e737dea 100644 --- a/indra/llui/lltransutil.cpp +++ b/indra/llui/lltransutil.cpp @@ -36,11 +36,12 @@ bool LLTransUtil::parseStrings(const std::string& xml_filename, const std::set& default_args) { // LLUICtrlFactory::getLayeredXMLNode() just calls - // gDirUtilp->findSkinnedFilenames(merge=false) and then passes the - // resulting paths to LLXMLNode::getLayeredXMLNode(). Bypass that and call - // LLXMLNode::getLayeredXMLNode() directly: we want merge=true. + // gDirUtilp->findSkinnedFilenames(constraint=LLDir::CURRENT_SKIN) and + // then passes the resulting paths to LLXMLNode::getLayeredXMLNode(). + // Bypass that and call LLXMLNode::getLayeredXMLNode() directly: we want + // constraint=LLDir::ALL_SKINS. std::vector paths = - gDirUtilp->findSkinnedFilenames(LLDir::XUI, xml_filename, true); + gDirUtilp->findSkinnedFilenames(LLDir::XUI, xml_filename, LLDir::ALL_SKINS); if (paths.empty()) { // xml_filename not found at all in any skin -- check whether entire diff --git a/indra/llui/lluicolortable.cpp b/indra/llui/lluicolortable.cpp index 2717445396..ffeff15968 100644 --- a/indra/llui/lluicolortable.cpp +++ b/indra/llui/lluicolortable.cpp @@ -207,9 +207,10 @@ bool LLUIColorTable::loadFromSettings() { bool result = false; - // pass merge=true because we want colors.xml from every skin dir + // pass constraint=LLDir::ALL_SKINS because we want colors.xml from every + // skin dir BOOST_FOREACH(std::string colors_path, - gDirUtilp->findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", true)) + gDirUtilp->findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS)) { result |= loadFromFilename(colors_path, mLoadedColors); } diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index a7d12476a4..2d0adf14e6 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -46,7 +46,6 @@ #include #include #include -#include #if LL_WINDOWS #include "lldir_win32.h" @@ -544,10 +543,10 @@ std::string LLDir::getExtension(const std::string& filepath) const std::string LLDir::findSkinnedFilenameBaseLang(const std::string &subdir, const std::string &filename, - bool merge) const + ESkinConstraint constraint) const { // This implementation is basically just as described in the declaration comments. - std::vector found(findSkinnedFilenames(subdir, filename, merge)); + std::vector found(findSkinnedFilenames(subdir, filename, constraint)); if (found.empty()) { return ""; @@ -557,10 +556,10 @@ std::string LLDir::findSkinnedFilenameBaseLang(const std::string &subdir, std::string LLDir::findSkinnedFilename(const std::string &subdir, const std::string &filename, - bool merge) const + ESkinConstraint constraint) const { // This implementation is basically just as described in the declaration comments. - std::vector found(findSkinnedFilenames(subdir, filename, merge)); + std::vector found(findSkinnedFilenames(subdir, filename, constraint)); if (found.empty()) { return ""; @@ -570,7 +569,7 @@ std::string LLDir::findSkinnedFilename(const std::string &subdir, std::vector LLDir::findSkinnedFilenames(const std::string& subdir, const std::string& filename, - bool merge) const + ESkinConstraint constraint) const { // Recognize subdirs that have no localization. static const char* sUnlocalizedData[] = @@ -582,7 +581,9 @@ std::vector LLDir::findSkinnedFilenames(const std::string& subdir, boost::end(sUnlocalizedData)); LL_DEBUGS("LLDir") << "subdir '" << subdir << "', filename '" << filename - << "', merge " << std::boolalpha << merge << LL_ENDL; + << "', constraint " + << ((constraint == CURRENT_SKIN)? "CURRENT_SKIN" : "ALL_SKINS") + << LL_ENDL; // Cache the default language directory for each subdir we've encountered. // A cache entry whose value is the empty string means "not localized, @@ -672,8 +673,8 @@ std::vector LLDir::findSkinnedFilenames(const std::string& subdir, // Here the desired filename exists in the first subsubdir. That means // this is a skindir we want to record in results. But if the caller - // passed merge=false, we must discard all previous skindirs. - if (! merge) + // passed constraint=CURRENT_SKIN, we must discard all previous skindirs. + if (constraint == CURRENT_SKIN) { results.clear(); } diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h index a242802979..ffa2676838 100644 --- a/indra/llvfs/lldir.h +++ b/indra/llvfs/lldir.h @@ -119,6 +119,8 @@ class LLDir // these methods search the various skin paths for the specified file in the following order: // getUserSkinDir(), getUserDefaultSkinDir(), getSkinDir(), getDefaultSkinDir() + /// param value for findSkinnedFilenames(), explained below + enum ESkinConstraint { CURRENT_SKIN, ALL_SKINS }; /** * Given a filename within skin, return an ordered sequence of paths to * search. Nonexistent files will be filtered out -- which means that the @@ -130,25 +132,25 @@ class LLDir * level of skin subdirectory). * @param filename Desired filename within subdir within skin, e.g. * "panel_login.xml". DO NOT prepend (e.g.) "xui" or the desired language. - * @param merge Callers perform two different kinds of processing. When - * fetching a XUI file, for instance, the existence of @a filename in the - * specified skin completely supercedes any @a filename in the default - * skin. For that case, leave the default @a merge=false. The returned - * vector will contain only + * @param constraint Callers perform two different kinds of processing. + * When fetching a XUI file, for instance, the existence of @a filename in + * the specified skin completely supercedes any @a filename in the default + * skin. For that case, leave the default @a constraint=CURRENT_SKIN. The + * returned vector will contain only * ".../current_skin/xui/en/filename", * ".../current_skin/xui/current_language/filename". * But for (e.g.) "strings.xml", we want a given skin to be able to * override only specific entries from the default skin. Any string not * defined in the specified skin will be sought in the default skin. For - * that case, pass @a merge=true. The returned vector will contain at - * least ".../default/xui/en/strings.xml", + * that case, pass @a constraint=ALL_SKINS. The returned vector will + * contain at least ".../default/xui/en/strings.xml", * ".../default/xui/current_language/strings.xml", * ".../current_skin/xui/en/strings.xml", * ".../current_skin/xui/current_language/strings.xml". */ std::vector findSkinnedFilenames(const std::string& subdir, const std::string& filename, - bool merge=false) const; + ESkinConstraint constraint=CURRENT_SKIN) const; /// Values for findSkinnedFilenames(subdir) parameter static const char *XUI, *TEXTURES, *SKINBASE; /** @@ -160,7 +162,7 @@ class LLDir */ std::string findSkinnedFilenameBaseLang(const std::string &subdir, const std::string &filename, - bool merge=false) const; + ESkinConstraint constraint=CURRENT_SKIN) const; /** * Return the "most localized" pathname from findSkinnedFilenames(), or * the empty string if no such file exists. Parameters are identical to @@ -170,7 +172,7 @@ class LLDir */ std::string findSkinnedFilename(const std::string &subdir, const std::string &filename, - bool merge=false) const; + ESkinConstraint constraint=CURRENT_SKIN) const; // random filename in common temporary directory std::string getTempFilename() const; diff --git a/indra/llvfs/tests/lldir_test.cpp b/indra/llvfs/tests/lldir_test.cpp index a00fc8684c..15a5b6d4f3 100644 --- a/indra/llvfs/tests/lldir_test.cpp +++ b/indra/llvfs/tests/lldir_test.cpp @@ -584,7 +584,7 @@ namespace tut ensure_equals(lldir.getLanguage(), "en"); // top-level directory of a skin isn't localized - ensure_equals(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", true), + ensure_equals(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS), vec(list_of("install/skins/default/colors.xml") ("user/skins/default/colors.xml"))); // We should not have needed to check for skins/default/en. We should @@ -599,13 +599,13 @@ namespace tut StringVec expected(vec(list_of("install/skins/default/xui/en/strings.xml") ("user/skins/default/xui/en/strings.xml"))); - ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", true), + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), expected); // The first time, we had to probe to find out whether xui was localized. lldir.ensure_checked("install/skins/default/xui/en"); lldir.clear_checked(); // Now make the same call again -- should return same result -- - ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", true), + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), expected); // but this time it should remember that xui is localized. lldir.ensure_not_checked("install/skins/default/xui/en"); @@ -648,7 +648,7 @@ namespace tut ensure_equals(lldir.getLanguage(), "fr"); // pass merge=true to request this filename in all relevant skins - ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", true), + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), vec(list_of ("install/skins/default/xui/en/strings.xml") ("install/skins/default/xui/fr/strings.xml") @@ -691,7 +691,7 @@ namespace tut /*------------------------- "steam", "en" --------------------------*/ lldir.setSkinFolder("steam", "en"); - ensure_equals(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", true), + ensure_equals(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS), vec(list_of ("install/skins/default/colors.xml") ("install/skins/steam/colors.xml") @@ -715,7 +715,7 @@ namespace tut vec(list_of("user/skins/steam/xui/en/strings.xml"))); // pass merge=true to request this filename in all relevant skins - ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", true), + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), vec(list_of ("install/skins/default/xui/en/strings.xml") ("install/skins/steam/xui/en/strings.xml") @@ -732,7 +732,7 @@ namespace tut ("user/skins/steam/xui/fr/strings.xml"))); // pass merge=true to request this filename in all relevant skins - ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", true), + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), vec(list_of ("install/skins/default/xui/en/strings.xml") ("install/skins/default/xui/fr/strings.xml") diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 7eb1a202a0..edc43534dd 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -1583,10 +1583,11 @@ struct UIImageDeclarations : public LLInitParam::Block bool LLUIImageList::initFromFile() { - // Look for textures.xml in all the right places. Pass merge=true because - // we want to overlay textures.xml from all the skins directories. + // Look for textures.xml in all the right places. Pass + // constraint=LLDir::ALL_SKINS because we want to overlay textures.xml + // from all the skins directories. std::vector textures_paths = - gDirUtilp->findSkinnedFilenames(LLDir::TEXTURES, "textures.xml", true); + gDirUtilp->findSkinnedFilenames(LLDir::TEXTURES, "textures.xml", LLDir::ALL_SKINS); std::vector::const_iterator pi(textures_paths.begin()), pend(textures_paths.end()); if (pi == pend) { -- cgit v1.2.3 From 6ca37068080e3b26e5cb163dfd874bc0e2f4c837 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 12 Oct 2012 08:42:18 -0400 Subject: LLUICtrlFactory::getLayeredXMLNode() gets LLDir::ESkinConstraint. At this point, LLUICtrlFactory::getLayeredXMLNode() is a pretty thin wrapper around LLDir::findSkinnedFilenames() and LLXMLNode::getLayeredXMLNode(). Until now, LLUICtrlFactory::getLayeredXMLNode() passed (by default) LLDir::CURRENT_SKIN to LLDir::findSkinnedFilenames(). But that meant that a caller such as LLTransUtil::parseStrings() that wants almost the same functionality, but with LLDir::ALL_SKINS instead, had to clone the logic from LLUICtrlFactory::getLayeredXMLNode(). Allowing its caller to pass the desired LLDir::ESkinConstraint enum value eliminates the need to clone its logic. Remove cloned logic from LLTransUtil::parseStrings(). --- indra/llui/lltransutil.cpp | 20 ++++++-------------- indra/llui/lluictrlfactory.cpp | 5 +++-- indra/llui/lluictrlfactory.h | 4 +++- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/indra/llui/lltransutil.cpp b/indra/llui/lltransutil.cpp index 3b7e737dea..80d079cbc8 100644 --- a/indra/llui/lltransutil.cpp +++ b/indra/llui/lltransutil.cpp @@ -35,21 +35,13 @@ bool LLTransUtil::parseStrings(const std::string& xml_filename, const std::set& default_args) { - // LLUICtrlFactory::getLayeredXMLNode() just calls - // gDirUtilp->findSkinnedFilenames(constraint=LLDir::CURRENT_SKIN) and - // then passes the resulting paths to LLXMLNode::getLayeredXMLNode(). - // Bypass that and call LLXMLNode::getLayeredXMLNode() directly: we want - // constraint=LLDir::ALL_SKINS. - std::vector paths = - gDirUtilp->findSkinnedFilenames(LLDir::XUI, xml_filename, LLDir::ALL_SKINS); - if (paths.empty()) - { - // xml_filename not found at all in any skin -- check whether entire - // path was passed (but I hope we no longer have callers who do that) - paths.push_back(xml_filename); - } LLXMLNodePtr root; - bool success = LLXMLNode::getLayeredXMLNode(root, paths); + // Pass LLDir::ALL_SKINS to load a composite of all the individual string + // definitions in the default skin and the current skin. This means an + // individual skin can provide an xml_filename that overrides only a + // subset of the available string definitions; any string definition not + // overridden by that skin will be sought in the default skin. + bool success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root, LLDir::ALL_SKINS); if (!success) { llerrs << "Couldn't load string table " << xml_filename << llendl; diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index 2b317b46e3..f7307cd076 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -151,11 +151,12 @@ static LLFastTimer::DeclareTimer FTM_XML_PARSE("XML Reading/Parsing"); //----------------------------------------------------------------------------- // getLayeredXMLNode() //----------------------------------------------------------------------------- -bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNodePtr& root) +bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNodePtr& root, + LLDir::ESkinConstraint constraint) { LLFastTimer timer(FTM_XML_PARSE); std::vector paths = - gDirUtilp->findSkinnedFilenames(LLDir::XUI, xui_filename); + gDirUtilp->findSkinnedFilenames(LLDir::XUI, xui_filename, constraint); if (paths.empty()) { diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h index 56e5f3eb7b..1acfd24112 100644 --- a/indra/llui/lluictrlfactory.h +++ b/indra/llui/lluictrlfactory.h @@ -31,6 +31,7 @@ #include "llinitparam.h" #include "llregistry.h" #include "llxuiparser.h" +#include "lldir.h" class LLView; @@ -212,7 +213,8 @@ fail: static void createChildren(LLView* viewp, LLXMLNodePtr node, const widget_registry_t&, LLXMLNodePtr output_node = NULL); - static bool getLayeredXMLNode(const std::string &filename, LLXMLNodePtr& root); + static bool getLayeredXMLNode(const std::string &filename, LLXMLNodePtr& root, + LLDir::ESkinConstraint constraint=LLDir::CURRENT_SKIN); private: //NOTE: both friend declarations are necessary to keep both gcc and msvc happy -- cgit v1.2.3 From 543b7ee9f7eb104497e971961fc0c39ed32ef54d Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 12 Oct 2012 11:09:25 -0400 Subject: Fix LLUI::locateSkin() failure case; clarify lldir.cpp static init. Per code review: Previous refactoring of LLUI::locateSkin() preserved odd failure behavior: it would return last-considered pathname, whether or not it exists. Changed to emit LL_WARNS log message and return empty string. Use Boost.Assign to simplify initialization of a couple static containers in lldir.cpp. --- indra/llui/llui.cpp | 8 +++--- indra/llvfs/lldir.cpp | 71 +++++++++++++++++++++++++-------------------------- 2 files changed, 38 insertions(+), 41 deletions(-) diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index 507ced9172..6d2bc1837c 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -1859,15 +1859,13 @@ std::string LLUI::locateSkin(const std::string& filename) } found_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename); -/*==========================================================================*| - // Hmm, if we got this far, previous implementation of this method would - // return this last found_file value whether or not it actually exists. if (gDirUtilp->fileExists(found_file)) { return found_file; } -|*==========================================================================*/ - return found_file; + LL_WARNS("LLUI") << "Can't find '" << filename + << "' in user settings, any skin directory or app_settings" << LL_ENDL; + return ""; } //static diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index 2d0adf14e6..4c0978b39e 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -45,8 +45,12 @@ #include #include #include +#include #include +using boost::assign::list_of; +using boost::assign::map_list_of; + #if LL_WINDOWS #include "lldir_win32.h" LLDir_Win32 gDirUtil; @@ -342,37 +346,35 @@ const std::string &LLDir::getLLPluginDir() const static std::string ELLPathToString(ELLPath location) { - typedef std::map ELLPathMap; -#define ENT(symbol) ELLPathMap::value_type(symbol, #symbol) - static ELLPathMap::value_type init[] = - { - ENT(LL_PATH_NONE), - ENT(LL_PATH_USER_SETTINGS), - ENT(LL_PATH_APP_SETTINGS), - ENT(LL_PATH_PER_SL_ACCOUNT), // returns/expands to blank string if we don't know the account name yet - ENT(LL_PATH_CACHE), - ENT(LL_PATH_CHARACTER), - ENT(LL_PATH_HELP), - ENT(LL_PATH_LOGS), - ENT(LL_PATH_TEMP), - ENT(LL_PATH_SKINS), - ENT(LL_PATH_TOP_SKIN), - ENT(LL_PATH_CHAT_LOGS), - ENT(LL_PATH_PER_ACCOUNT_CHAT_LOGS), - ENT(LL_PATH_USER_SKIN), - ENT(LL_PATH_LOCAL_ASSETS), - ENT(LL_PATH_EXECUTABLE), - ENT(LL_PATH_DEFAULT_SKIN), - ENT(LL_PATH_FONTS), - ENT(LL_PATH_LAST) - }; + typedef std::map ELLPathMap; +#define ENT(symbol) (symbol, #symbol) + static const ELLPathMap sMap = map_list_of + ENT(LL_PATH_NONE) + ENT(LL_PATH_USER_SETTINGS) + ENT(LL_PATH_APP_SETTINGS) + ENT(LL_PATH_PER_SL_ACCOUNT) // returns/expands to blank string if we don't know the account name yet + ENT(LL_PATH_CACHE) + ENT(LL_PATH_CHARACTER) + ENT(LL_PATH_HELP) + ENT(LL_PATH_LOGS) + ENT(LL_PATH_TEMP) + ENT(LL_PATH_SKINS) + ENT(LL_PATH_TOP_SKIN) + ENT(LL_PATH_CHAT_LOGS) + ENT(LL_PATH_PER_ACCOUNT_CHAT_LOGS) + ENT(LL_PATH_USER_SKIN) + ENT(LL_PATH_LOCAL_ASSETS) + ENT(LL_PATH_EXECUTABLE) + ENT(LL_PATH_DEFAULT_SKIN) + ENT(LL_PATH_FONTS) + ENT(LL_PATH_LAST) + ; #undef ENT - static const ELLPathMap sMap(boost::begin(init), boost::end(init)); - ELLPathMap::const_iterator found = sMap.find(location); - if (found != sMap.end()) - return found->second; - return STRINGIZE("Invalid ELLPath value " << location); + ELLPathMap::const_iterator found = sMap.find(location); + if (found != sMap.end()) + return found->second; + return STRINGIZE("Invalid ELLPath value " << location); } std::string LLDir::getExpandedFilename(ELLPath location, const std::string& filename) const @@ -572,13 +574,10 @@ std::vector LLDir::findSkinnedFilenames(const std::string& subdir, ESkinConstraint constraint) const { // Recognize subdirs that have no localization. - static const char* sUnlocalizedData[] = - { - "", // top-level directory not localized - "textures" // textures not localized - }; - static const std::set sUnlocalized(boost::begin(sUnlocalizedData), - boost::end(sUnlocalizedData)); + static const std::set sUnlocalized = list_of + ("") // top-level directory not localized + ("textures") // textures not localized + ; LL_DEBUGS("LLDir") << "subdir '" << subdir << "', filename '" << filename << "', constraint " -- cgit v1.2.3 From bf99126a37062d80e7887e2634b34f1da0ed309e Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 15 Oct 2012 09:38:28 -0400 Subject: Remove LLUICtrlFactory::findSkinnedFilename(): cf. LLDir method. Richard points out that LLUICtrlFactory::findSkinnedFilename() adds little value. It was called from exactly one place, and that one place could easily obtain the information another way. The concern is that it could confuse a reader of the code with regard to the other findSkinnedFilename[s]() methods in LLDir. Clarifying the code base is a Good Thing. Removing. --- indra/llui/lluictrlfactory.cpp | 8 -------- indra/llui/lluictrlfactory.h | 10 +++------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index f7307cd076..bd06476936 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -232,14 +232,6 @@ void LLUICtrlFactory::setCtrlParent(LLView* view, LLView* parent, S32 tab_group) parent->addChild(view, tab_group); } - -// Avoid directly using LLUI and LLDir in the template code -//static -std::string LLUICtrlFactory::findSkinnedFilename(const std::string& filename) -{ - return gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, filename); -} - //static void LLUICtrlFactory::copyName(LLXMLNodePtr src, LLXMLNodePtr dest) { diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h index 1acfd24112..ca6ad254b1 100644 --- a/indra/llui/lluictrlfactory.h +++ b/indra/llui/lluictrlfactory.h @@ -173,18 +173,17 @@ public: static T* createFromFile(const std::string &filename, LLView *parent, const widget_registry_t& registry) { T* widget = NULL; - - std::string skinned_filename = findSkinnedFilename(filename); + instance().pushFileName(filename); { LLXMLNodePtr root_node; if (!LLUICtrlFactory::getLayeredXMLNode(filename, root_node)) { - llwarns << "Couldn't parse XUI file: " << skinned_filename << llendl; + llwarns << "Couldn't parse XUI file: " << instance().getCurFileName() << llendl; goto fail; } - + LLView* view = getInstance()->createFromXML(root_node, parent, filename, registry, NULL); if (view) { @@ -299,9 +298,6 @@ private: // this exists to get around dependency on llview static void setCtrlParent(LLView* view, LLView* parent, S32 tab_group); - // Avoid directly using LLUI and LLDir in the template code - static std::string findSkinnedFilename(const std::string& filename); - class LLPanel* mDummyPanel; std::vector mFileNames; }; -- cgit v1.2.3 From c5373b90eb2de8f29be6801a07e937064ecb7fbb Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 15 Oct 2012 16:16:24 -0400 Subject: Add 'sourceid' settings var specifically for skin-files insertion. Add logic after (both!) LLTransUtil::parseStrings() calls to ensure that "[sourceid]" embedded in (e.g.) strings.xml content will be replaced. --- indra/newview/app_settings/settings.xml | 11 +++++++++++ indra/newview/llappviewer.cpp | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 5e50bd6e01..89f300548e 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -12285,6 +12285,17 @@ Value 0 + sourceid + + Comment + Identify referring agency to Linden web servers + Persist + 1 + Type + String + Value + + SpeakerParticipantDefaultOrder Comment diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index efa24796e5..9637bd328e 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -771,6 +771,8 @@ bool LLAppViewer::init() LLUI::setupPaths(); LLTransUtil::parseStrings("strings.xml", default_trans_args); LLTransUtil::parseLanguageStrings("language_settings.xml"); + // parseStrings() sets up the LLTrans substitution table. Add this one item. + LLTrans::setDefaultArg("[sourceid]", gSavedSettings.getString("sourceid")); // Setup notifications after LLUI::setupPaths() has been called. LLNotifications::instance(); @@ -2242,6 +2244,8 @@ bool LLAppViewer::initConfiguration() LLUI::setupPaths(); // setup paths for LLTrans based on settings files only LLTransUtil::parseStrings("strings.xml", default_trans_args); LLTransUtil::parseLanguageStrings("language_settings.xml"); + // parseStrings() sets up the LLTrans substitution table. Add this one item. + LLTrans::setDefaultArg("[sourceid]", gSavedSettings.getString("sourceid")); // - set procedural settings // Note: can't use LL_PATH_PER_SL_ACCOUNT for any of these since we haven't logged in yet gSavedSettings.setString("ClientSettingsFile", -- cgit v1.2.3 From f44340a6b424522729c6cce2761e4df1713b9035 Mon Sep 17 00:00:00 2001 From: William Todd Stinson Date: Mon, 15 Oct 2012 16:34:29 -0700 Subject: MAINT-1672: Correcting issue where the sailboat boom was not moving correctly. The isssue was that the display text for the prim was being updated, and this code path would mark the prim as having been moved when, in fact, the pipeline had not yet moved the object. --- indra/newview/llviewerobject.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 1447f133e6..75d36d3463 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -1236,12 +1236,8 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, coloru.mV[3] = 255 - coloru.mV[3]; mText->setColor(LLColor4(coloru)); mText->setString(temp_string); - - if (mDrawable.notNull()) - { - setChanged(MOVED | SILHOUETTE); - gPipeline.markMoved(mDrawable, FALSE); // undamped - } + + setChanged(MOVED | SILHOUETTE); } else if (mText.notNull()) { @@ -2101,7 +2097,6 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, setChanged(ROTATED | SILHOUETTE); } - if ( gShowObjectUpdates ) { LLColor4 color; -- cgit v1.2.3 From 13f16bbeb2fd3182b518c4171ecc2b4d2a7cbdee Mon Sep 17 00:00:00 2001 From: William Todd Stinson Date: Mon, 15 Oct 2012 18:26:05 -0700 Subject: MAINT-1640: Changing the pathfinding sub-menu to be tear-off. --- indra/newview/skins/default/xui/en/menu_viewer.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 1aa55acf2d..0adac8630e 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -956,7 +956,7 @@ create_jump_keys="true" label="Pathfinding" name="Pathfinding" - tear_off="false"> + tear_off="true"> -- cgit v1.2.3 From 988767d5ec4631ad9f0099180a92c4f186553940 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 16 Oct 2012 09:56:45 -0400 Subject: Introduce LLAppViewer::initStrings(); reload default_trans_args. Calling LLTrans::setDefaultArg() after LLTransUtil::parseStrings() is almost good enough -- but it fails to address the case in which one or more of the default_trans_args strings (e.g. "create_account_url") embeds a reference to the new substitution. So after the setDefaultArg() call, go back through default_trans_args, refetching each string to perform the substitution and updating it with a setDefaultArg() call of its own. All this is way too much logic to replicate in both LLAppViewer::initConfiguration() and init(), so break out new LLAppViewer::initStrings() method and call it from both places. --- indra/newview/llappviewer.cpp | 62 +++++++++++++++++++++++++++++++++++-------- indra/newview/llappviewer.h | 1 + 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 9637bd328e..0c4c8a66e5 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -114,6 +114,7 @@ #include "llnotificationsutil.h" #include "llleap.h" +#include "stringize.h" // Third party library includes #include @@ -768,11 +769,7 @@ bool LLAppViewer::init() LL_INFOS("InitInfo") << "UI initialized." << LL_ENDL ; // Setup paths and LLTrans after LLUI::initClass has been called. - LLUI::setupPaths(); - LLTransUtil::parseStrings("strings.xml", default_trans_args); - LLTransUtil::parseLanguageStrings("language_settings.xml"); - // parseStrings() sets up the LLTrans substitution table. Add this one item. - LLTrans::setDefaultArg("[sourceid]", gSavedSettings.getString("sourceid")); + initStrings(); // Setup notifications after LLUI::setupPaths() has been called. LLNotifications::instance(); @@ -2240,12 +2237,8 @@ bool LLAppViewer::initConfiguration() OSMessageBox(msg.str(),LLStringUtil::null,OSMB_OK); return false; } - - LLUI::setupPaths(); // setup paths for LLTrans based on settings files only - LLTransUtil::parseStrings("strings.xml", default_trans_args); - LLTransUtil::parseLanguageStrings("language_settings.xml"); - // parseStrings() sets up the LLTrans substitution table. Add this one item. - LLTrans::setDefaultArg("[sourceid]", gSavedSettings.getString("sourceid")); + + initStrings(); // setup paths for LLTrans based on settings files only // - set procedural settings // Note: can't use LL_PATH_PER_SL_ACCOUNT for any of these since we haven't logged in yet gSavedSettings.setString("ClientSettingsFile", @@ -2730,6 +2723,53 @@ bool LLAppViewer::initConfiguration() return true; // Config was successful. } +// The following logic is replicated in initConfiguration() (to be able to get +// some initial strings before we've finished initializing enough to know the +// current language) and also in init() (to initialize for real). Somehow it +// keeps growing, necessitating a method all its own. +void LLAppViewer::initStrings() +{ + LLUI::setupPaths(); + LLTransUtil::parseStrings("strings.xml", default_trans_args); + LLTransUtil::parseLanguageStrings("language_settings.xml"); + + // parseStrings() sets up the LLTrans substitution table. Add this one item. + LLTrans::setDefaultArg("[sourceid]", gSavedSettings.getString("sourceid")); + + // Now that we've set "[sourceid]", have to go back through + // default_trans_args and reinitialize all those other keys because some + // of them, in turn, reference "[sourceid]". + BOOST_FOREACH(std::string key, default_trans_args) + { + std::string brackets(key), nobrackets(key); + // Invalid to inspect key[0] if key is empty(). But then, the entire + // body of this loop is pointless if key is empty(). + if (key.empty()) + continue; + + if (key[0] != '[') + { + // key was passed without brackets. That means that 'nobrackets' + // is correct but 'brackets' is not. + brackets = STRINGIZE('[' << brackets << ']'); + } + else + { + // key was passed with brackets. That means that 'brackets' is + // correct but 'nobrackets' is not. Erase the left bracket. + nobrackets.erase(0, 1); + std::string::size_type length(nobrackets.length()); + if (length && nobrackets[length - 1] == ']') + { + nobrackets.erase(length - 1); + } + } + // Calling LLTrans::getString() is what embeds the other default + // translation strings into this one. + LLTrans::setDefaultArg(brackets, LLTrans::getString(nobrackets)); + } +} + namespace { // *TODO - decide if there's a better place for these functions. // do we need a file llupdaterui.cpp or something? -brad diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index ae3c795d1e..5a3a41690a 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -194,6 +194,7 @@ private: void initMaxHeapSize(); bool initThreads(); // Initialize viewer threads, return false on failure. bool initConfiguration(); // Initialize settings from the command line/config file. + void initStrings(); // Initialize LLTrans machinery void initUpdater(); // Initialize the updater service. bool initCache(); // Initialize local client cache. void checkMemory() ; -- cgit v1.2.3 From 6ef912da9fa18ffc4cc19bc6d564671adab6f487 Mon Sep 17 00:00:00 2001 From: William Todd Stinson Date: Tue, 16 Oct 2012 14:52:06 -0700 Subject: MAINT-1738: Repositioning the 'Show in linksets' and 'Show in characters' in-world context menu options. --- indra/newview/skins/default/xui/en/menu_object.xml | 40 +++++++++++----------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/indra/newview/skins/default/xui/en/menu_object.xml b/indra/newview/skins/default/xui/en/menu_object.xml index 52b9524b11..f6004621a6 100644 --- a/indra/newview/skins/default/xui/en/menu_object.xml +++ b/indra/newview/skins/default/xui/en/menu_object.xml @@ -29,26 +29,6 @@ - - - - - - - - - - + + + + + + + + + + Date: Tue, 16 Oct 2012 15:30:00 -0700 Subject: MAINT-1737: Moving files around to replace the Rebake_Region button with a menu option. --- indra/newview/CMakeLists.txt | 4 +- indra/newview/llagent.cpp | 2 +- .../llmenuoptionpathfindingrebakenavmesh.cpp | 269 +++++++++++++++++++++ .../newview/llmenuoptionpathfindingrebakenavmesh.h | 96 ++++++++ indra/newview/llpanelpathfindingrebakenavmesh.cpp | 269 --------------------- indra/newview/llpanelpathfindingrebakenavmesh.h | 96 -------- indra/newview/llviewerwindow.cpp | 2 +- 7 files changed, 369 insertions(+), 369 deletions(-) create mode 100644 indra/newview/llmenuoptionpathfindingrebakenavmesh.cpp create mode 100644 indra/newview/llmenuoptionpathfindingrebakenavmesh.h delete mode 100644 indra/newview/llpanelpathfindingrebakenavmesh.cpp delete mode 100644 indra/newview/llpanelpathfindingrebakenavmesh.h diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 9ada161cd8..919e5050d7 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -334,6 +334,7 @@ set(viewer_SOURCE_FILES llmediactrl.cpp llmediadataclient.cpp llmemoryview.cpp + llmenuoptionpathfindingrebakenavmesh.cpp llmeshrepository.cpp llmimetypes.cpp llmorphview.cpp @@ -398,7 +399,6 @@ set(viewer_SOURCE_FILES llpanelonlinestatus.cpp llpaneloutfitedit.cpp llpaneloutfitsinventory.cpp - llpanelpathfindingrebakenavmesh.cpp llpanelpeople.cpp llpanelpeoplemenus.cpp llpanelpermissions.cpp @@ -910,6 +910,7 @@ set(viewer_HEADER_FILES llmediactrl.h llmediadataclient.h llmemoryview.h + llmenuoptionpathfindingrebakenavmesh.h llmeshrepository.h llmimetypes.h llmorphview.h @@ -968,7 +969,6 @@ set(viewer_HEADER_FILES llpanelonlinestatus.h llpaneloutfitedit.h llpaneloutfitsinventory.h - llpanelpathfindingrebakenavmesh.h llpanelpeople.h llpanelpeoplemenus.h llpanelpermissions.h diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 447836910d..6785803f03 100755 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -51,12 +51,12 @@ #include "llhomelocationresponder.h" #include "llhudmanager.h" #include "lljoystickbutton.h" +#include "llmenuoptionpathfindingrebakenavmesh.h" #include "llmorphview.h" #include "llmoveview.h" #include "llnavigationbar.h" // to show/hide navigation bar when changing mouse look state #include "llnearbychatbar.h" #include "llnotificationsutil.h" -#include "llpanelpathfindingrebakenavmesh.h" #include "llpaneltopinfobar.h" #include "llparcel.h" #include "llrendersphere.h" diff --git a/indra/newview/llmenuoptionpathfindingrebakenavmesh.cpp b/indra/newview/llmenuoptionpathfindingrebakenavmesh.cpp new file mode 100644 index 0000000000..3c89958f7e --- /dev/null +++ b/indra/newview/llmenuoptionpathfindingrebakenavmesh.cpp @@ -0,0 +1,269 @@ +/** +* @file llmenuoptionpathfindingrebakenavmesh.cpp +* @brief Implementation of llmenuoptionpathfindingrebakenavmesh +* @author Prep@lindenlab.com +* +* $LicenseInfo:firstyear=2012&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2012, 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 +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + + +#include "llviewerprecompiledheaders.h" + +#include "llmenuoptionpathfindingrebakenavmesh.h" + +#include +#include + +#include "llagent.h" +#include "llbutton.h" +#include "llenvmanager.h" +#include "llhints.h" +#include "llnotificationsutil.h" +#include "llpanel.h" +#include "llpathfindingmanager.h" +#include "llpathfindingnavmesh.h" +#include "llpathfindingnavmeshstatus.h" +#include "lltoolbar.h" +#include "lltoolbarview.h" +#include "lltooltip.h" +#include "llviewerregion.h" + +LLPanelPathfindingRebakeNavmesh* LLPanelPathfindingRebakeNavmesh::getInstance() +{ + static LLPanelPathfindingRebakeNavmesh* panel = getPanel(); + return panel; +} + +BOOL LLPanelPathfindingRebakeNavmesh::postBuild() +{ + //Rebake button + mNavMeshRebakeButton = findChild("navmesh_btn"); + llassert(mNavMeshRebakeButton != NULL); + mNavMeshRebakeButton->setCommitCallback(boost::bind(&LLPanelPathfindingRebakeNavmesh::onNavMeshRebakeClick, this)); + LLHints::registerHintTarget("navmesh_btn", mNavMeshRebakeButton->getHandle()); + + //Sending rebake request + mNavMeshSendingButton = findChild("navmesh_btn_sending"); + llassert(mNavMeshSendingButton != NULL); + LLHints::registerHintTarget("navmesh_btn_sending", mNavMeshSendingButton->getHandle()); + + //rebaking... + mNavMeshBakingButton = findChild("navmesh_btn_baking"); + llassert(mNavMeshBakingButton != NULL); + LLHints::registerHintTarget("navmesh_btn_baking", mNavMeshBakingButton->getHandle()); + + setMode(kRebakeNavMesh_Default); + + createNavMeshStatusListenerForCurrentRegion(); + + if ( !mRegionCrossingSlot.connected() ) + { + mRegionCrossingSlot = LLEnvManagerNew::getInstance()->setRegionChangeCallback(boost::bind(&LLPanelPathfindingRebakeNavmesh::handleRegionBoundaryCrossed, this)); + } + + if (!mAgentStateSlot.connected()) + { + mAgentStateSlot = LLPathfindingManager::getInstance()->registerAgentStateListener(boost::bind(&LLPanelPathfindingRebakeNavmesh::handleAgentState, this, _1)); + } + LLPathfindingManager::getInstance()->requestGetAgentState(); + + return LLPanel::postBuild(); +} + +void LLPanelPathfindingRebakeNavmesh::draw() +{ + if (doDraw()) + { + updatePosition(); + LLPanel::draw(); + } +} + +BOOL LLPanelPathfindingRebakeNavmesh::handleToolTip( S32 x, S32 y, MASK mask ) +{ + LLToolTipMgr::instance().unblockToolTips(); + + if (mNavMeshRebakeButton->getVisible()) + { + LLToolTipMgr::instance().show(mNavMeshRebakeButton->getToolTip()); + } + else if (mNavMeshSendingButton->getVisible()) + { + LLToolTipMgr::instance().show(mNavMeshSendingButton->getToolTip()); + } + else if (mNavMeshBakingButton->getVisible()) + { + LLToolTipMgr::instance().show(mNavMeshBakingButton->getToolTip()); + } + + return LLPanel::handleToolTip(x, y, mask); +} + +LLPanelPathfindingRebakeNavmesh::LLPanelPathfindingRebakeNavmesh() + : LLPanel(), + mCanRebakeRegion(FALSE), + mRebakeNavMeshMode(kRebakeNavMesh_Default), + mNavMeshRebakeButton(NULL), + mNavMeshSendingButton(NULL), + mNavMeshBakingButton(NULL), + mNavMeshSlot(), + mRegionCrossingSlot(), + mAgentStateSlot() +{ + // make sure we have the only instance of this class + static bool b = true; + llassert_always(b); + b=false; +} + +LLPanelPathfindingRebakeNavmesh::~LLPanelPathfindingRebakeNavmesh() +{ +} + +LLPanelPathfindingRebakeNavmesh* LLPanelPathfindingRebakeNavmesh::getPanel() +{ + LLPanelPathfindingRebakeNavmesh* panel = new LLPanelPathfindingRebakeNavmesh(); + panel->buildFromFile("panel_navmesh_rebake.xml"); + return panel; +} + +void LLPanelPathfindingRebakeNavmesh::setMode(ERebakeNavMeshMode pRebakeNavMeshMode) +{ + if (pRebakeNavMeshMode == kRebakeNavMesh_Available) + { + LLNotificationsUtil::add("PathfindingRebakeNavmesh"); + } + mNavMeshRebakeButton->setVisible(pRebakeNavMeshMode == kRebakeNavMesh_Available); + mNavMeshSendingButton->setVisible(pRebakeNavMeshMode == kRebakeNavMesh_RequestSent); + mNavMeshBakingButton->setVisible(pRebakeNavMeshMode == kRebakeNavMesh_InProgress); + mRebakeNavMeshMode = pRebakeNavMeshMode; +} + +LLPanelPathfindingRebakeNavmesh::ERebakeNavMeshMode LLPanelPathfindingRebakeNavmesh::getMode() const +{ + return mRebakeNavMeshMode; +} + +void LLPanelPathfindingRebakeNavmesh::onNavMeshRebakeClick() +{ + setMode(kRebakeNavMesh_RequestSent); + LLPathfindingManager::getInstance()->requestRebakeNavMesh(boost::bind(&LLPanelPathfindingRebakeNavmesh::handleRebakeNavMeshResponse, this, _1)); +} + +void LLPanelPathfindingRebakeNavmesh::handleAgentState(BOOL pCanRebakeRegion) +{ + mCanRebakeRegion = pCanRebakeRegion; +} + +void LLPanelPathfindingRebakeNavmesh::handleRebakeNavMeshResponse(bool pResponseStatus) +{ + if (getMode() == kRebakeNavMesh_RequestSent) + { + setMode(pResponseStatus ? kRebakeNavMesh_InProgress : kRebakeNavMesh_Default); + } + + if (!pResponseStatus) + { + LLNotificationsUtil::add("PathfindingCannotRebakeNavmesh"); + } +} + +void LLPanelPathfindingRebakeNavmesh::handleNavMeshStatus(const LLPathfindingNavMeshStatus &pNavMeshStatus) +{ + ERebakeNavMeshMode rebakeNavMeshMode = kRebakeNavMesh_Default; + if (pNavMeshStatus.isValid()) + { + switch (pNavMeshStatus.getStatus()) + { + case LLPathfindingNavMeshStatus::kPending : + case LLPathfindingNavMeshStatus::kRepending : + rebakeNavMeshMode = kRebakeNavMesh_Available; + break; + case LLPathfindingNavMeshStatus::kBuilding : + rebakeNavMeshMode = kRebakeNavMesh_InProgress; + break; + case LLPathfindingNavMeshStatus::kComplete : + rebakeNavMeshMode = kRebakeNavMesh_NotAvailable; + break; + default : + rebakeNavMeshMode = kRebakeNavMesh_Default; + llassert(0); + break; + } + } + + setMode(rebakeNavMeshMode); +} + +void LLPanelPathfindingRebakeNavmesh::handleRegionBoundaryCrossed() +{ + createNavMeshStatusListenerForCurrentRegion(); + mCanRebakeRegion = FALSE; + LLPathfindingManager::getInstance()->requestGetAgentState(); +} + +void LLPanelPathfindingRebakeNavmesh::createNavMeshStatusListenerForCurrentRegion() +{ + if (mNavMeshSlot.connected()) + { + mNavMeshSlot.disconnect(); + } + + LLViewerRegion *currentRegion = gAgent.getRegion(); + if (currentRegion != NULL) + { + mNavMeshSlot = LLPathfindingManager::getInstance()->registerNavMeshListenerForRegion(currentRegion, boost::bind(&LLPanelPathfindingRebakeNavmesh::handleNavMeshStatus, this, _2)); + LLPathfindingManager::getInstance()->requestGetNavMeshForRegion(currentRegion, true); + } +} + +bool LLPanelPathfindingRebakeNavmesh::doDraw() const +{ + return (mCanRebakeRegion && (mRebakeNavMeshMode != kRebakeNavMesh_NotAvailable)); +} + +void LLPanelPathfindingRebakeNavmesh::updatePosition() +{ + S32 y_pos = 0; + S32 bottom_tb_center = 0; + + if (LLToolBar* toolbar_bottom = gToolBarView->getChild("toolbar_bottom")) + { + y_pos = toolbar_bottom->getRect().getHeight(); + bottom_tb_center = toolbar_bottom->getRect().getCenterX(); + } + + S32 left_tb_width = 0; + if (LLToolBar* toolbar_left = gToolBarView->getChild("toolbar_left")) + { + left_tb_width = toolbar_left->getRect().getWidth(); + } + + if(LLPanel* panel_ssf_container = getRootView()->getChild("state_management_buttons_container")) + { + panel_ssf_container->setOrigin(0, y_pos); + } + + S32 x_pos = bottom_tb_center-getRect().getWidth()/2 - left_tb_width + 113 /* width of stand/fly button */ + 10 /* margin */; + + setOrigin( x_pos, 0); +} diff --git a/indra/newview/llmenuoptionpathfindingrebakenavmesh.h b/indra/newview/llmenuoptionpathfindingrebakenavmesh.h new file mode 100644 index 0000000000..e41ae9cae0 --- /dev/null +++ b/indra/newview/llmenuoptionpathfindingrebakenavmesh.h @@ -0,0 +1,96 @@ +/** +* @file llmenuoptionpathfindingrebakenavmesh.h +* @brief Header file for llmenuoptionpathfindingrebakenavmesh +* @author Prep@lindenlab.com +* +* $LicenseInfo:firstyear=2012&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2012, 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 +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ +#ifndef LL_LLPANELPATHFINDINGREBAKENAVMESH_H +#define LL_LLPANELPATHFINDINGREBAKENAVMESH_H + +#include + +#include "llpanel.h" +#include "llpathfindingmanager.h" +#include "llpathfindingnavmesh.h" + +class LLButton; +class LLPathfindingNavMeshStatus; + +class LLPanelPathfindingRebakeNavmesh : public LLPanel +{ + + LOG_CLASS(LLPanelPathfindingRebakeNavmesh); + +public: + static LLPanelPathfindingRebakeNavmesh* getInstance(); + + virtual BOOL postBuild(); + + virtual void draw(); + virtual BOOL handleToolTip( S32 x, S32 y, MASK mask ); + +protected: + +private: + typedef enum + { + kRebakeNavMesh_Available, + kRebakeNavMesh_RequestSent, + kRebakeNavMesh_InProgress, + kRebakeNavMesh_NotAvailable, + kRebakeNavMesh_Default = kRebakeNavMesh_NotAvailable + } ERebakeNavMeshMode; + + LLPanelPathfindingRebakeNavmesh(); + virtual ~LLPanelPathfindingRebakeNavmesh(); + + static LLPanelPathfindingRebakeNavmesh* getPanel(); + + void setMode(ERebakeNavMeshMode pRebakeNavMeshMode); + ERebakeNavMeshMode getMode() const; + + void onNavMeshRebakeClick(); + + void handleAgentState(BOOL pCanRebakeRegion); + void handleRebakeNavMeshResponse(bool pResponseStatus); + void handleNavMeshStatus(const LLPathfindingNavMeshStatus &pNavMeshStatus); + void handleRegionBoundaryCrossed(); + + void createNavMeshStatusListenerForCurrentRegion(); + + bool doDraw() const; + void updatePosition(); + + BOOL mCanRebakeRegion; + ERebakeNavMeshMode mRebakeNavMeshMode; + + LLButton* mNavMeshRebakeButton; + LLButton* mNavMeshSendingButton; + LLButton* mNavMeshBakingButton; + + LLPathfindingNavMesh::navmesh_slot_t mNavMeshSlot; + boost::signals2::connection mRegionCrossingSlot; + LLPathfindingManager::agent_state_slot_t mAgentStateSlot; +}; + +#endif // LL_LLPANELPATHFINDINGREBAKENAVMESH_H diff --git a/indra/newview/llpanelpathfindingrebakenavmesh.cpp b/indra/newview/llpanelpathfindingrebakenavmesh.cpp deleted file mode 100644 index 7efb1a9227..0000000000 --- a/indra/newview/llpanelpathfindingrebakenavmesh.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/** -* @file llpanelpathfindingrebakenavmesh.cpp -* @brief Implementation of llpanelpathfindingrebakenavmesh -* @author Prep@lindenlab.com -* -* $LicenseInfo:firstyear=2012&license=viewerlgpl$ -* Second Life Viewer Source Code -* Copyright (C) 2012, 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 -* -* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -* $/LicenseInfo$ -*/ - - -#include "llviewerprecompiledheaders.h" - -#include "llpanelpathfindingrebakenavmesh.h" - -#include -#include - -#include "llagent.h" -#include "llbutton.h" -#include "llenvmanager.h" -#include "llhints.h" -#include "llnotificationsutil.h" -#include "llpanel.h" -#include "llpathfindingmanager.h" -#include "llpathfindingnavmesh.h" -#include "llpathfindingnavmeshstatus.h" -#include "lltoolbar.h" -#include "lltoolbarview.h" -#include "lltooltip.h" -#include "llviewerregion.h" - -LLPanelPathfindingRebakeNavmesh* LLPanelPathfindingRebakeNavmesh::getInstance() -{ - static LLPanelPathfindingRebakeNavmesh* panel = getPanel(); - return panel; -} - -BOOL LLPanelPathfindingRebakeNavmesh::postBuild() -{ - //Rebake button - mNavMeshRebakeButton = findChild("navmesh_btn"); - llassert(mNavMeshRebakeButton != NULL); - mNavMeshRebakeButton->setCommitCallback(boost::bind(&LLPanelPathfindingRebakeNavmesh::onNavMeshRebakeClick, this)); - LLHints::registerHintTarget("navmesh_btn", mNavMeshRebakeButton->getHandle()); - - //Sending rebake request - mNavMeshSendingButton = findChild("navmesh_btn_sending"); - llassert(mNavMeshSendingButton != NULL); - LLHints::registerHintTarget("navmesh_btn_sending", mNavMeshSendingButton->getHandle()); - - //rebaking... - mNavMeshBakingButton = findChild("navmesh_btn_baking"); - llassert(mNavMeshBakingButton != NULL); - LLHints::registerHintTarget("navmesh_btn_baking", mNavMeshBakingButton->getHandle()); - - setMode(kRebakeNavMesh_Default); - - createNavMeshStatusListenerForCurrentRegion(); - - if ( !mRegionCrossingSlot.connected() ) - { - mRegionCrossingSlot = LLEnvManagerNew::getInstance()->setRegionChangeCallback(boost::bind(&LLPanelPathfindingRebakeNavmesh::handleRegionBoundaryCrossed, this)); - } - - if (!mAgentStateSlot.connected()) - { - mAgentStateSlot = LLPathfindingManager::getInstance()->registerAgentStateListener(boost::bind(&LLPanelPathfindingRebakeNavmesh::handleAgentState, this, _1)); - } - LLPathfindingManager::getInstance()->requestGetAgentState(); - - return LLPanel::postBuild(); -} - -void LLPanelPathfindingRebakeNavmesh::draw() -{ - if (doDraw()) - { - updatePosition(); - LLPanel::draw(); - } -} - -BOOL LLPanelPathfindingRebakeNavmesh::handleToolTip( S32 x, S32 y, MASK mask ) -{ - LLToolTipMgr::instance().unblockToolTips(); - - if (mNavMeshRebakeButton->getVisible()) - { - LLToolTipMgr::instance().show(mNavMeshRebakeButton->getToolTip()); - } - else if (mNavMeshSendingButton->getVisible()) - { - LLToolTipMgr::instance().show(mNavMeshSendingButton->getToolTip()); - } - else if (mNavMeshBakingButton->getVisible()) - { - LLToolTipMgr::instance().show(mNavMeshBakingButton->getToolTip()); - } - - return LLPanel::handleToolTip(x, y, mask); -} - -LLPanelPathfindingRebakeNavmesh::LLPanelPathfindingRebakeNavmesh() - : LLPanel(), - mCanRebakeRegion(FALSE), - mRebakeNavMeshMode(kRebakeNavMesh_Default), - mNavMeshRebakeButton(NULL), - mNavMeshSendingButton(NULL), - mNavMeshBakingButton(NULL), - mNavMeshSlot(), - mRegionCrossingSlot(), - mAgentStateSlot() -{ - // make sure we have the only instance of this class - static bool b = true; - llassert_always(b); - b=false; -} - -LLPanelPathfindingRebakeNavmesh::~LLPanelPathfindingRebakeNavmesh() -{ -} - -LLPanelPathfindingRebakeNavmesh* LLPanelPathfindingRebakeNavmesh::getPanel() -{ - LLPanelPathfindingRebakeNavmesh* panel = new LLPanelPathfindingRebakeNavmesh(); - panel->buildFromFile("panel_navmesh_rebake.xml"); - return panel; -} - -void LLPanelPathfindingRebakeNavmesh::setMode(ERebakeNavMeshMode pRebakeNavMeshMode) -{ - if (pRebakeNavMeshMode == kRebakeNavMesh_Available) - { - LLNotificationsUtil::add("PathfindingRebakeNavmesh"); - } - mNavMeshRebakeButton->setVisible(pRebakeNavMeshMode == kRebakeNavMesh_Available); - mNavMeshSendingButton->setVisible(pRebakeNavMeshMode == kRebakeNavMesh_RequestSent); - mNavMeshBakingButton->setVisible(pRebakeNavMeshMode == kRebakeNavMesh_InProgress); - mRebakeNavMeshMode = pRebakeNavMeshMode; -} - -LLPanelPathfindingRebakeNavmesh::ERebakeNavMeshMode LLPanelPathfindingRebakeNavmesh::getMode() const -{ - return mRebakeNavMeshMode; -} - -void LLPanelPathfindingRebakeNavmesh::onNavMeshRebakeClick() -{ - setMode(kRebakeNavMesh_RequestSent); - LLPathfindingManager::getInstance()->requestRebakeNavMesh(boost::bind(&LLPanelPathfindingRebakeNavmesh::handleRebakeNavMeshResponse, this, _1)); -} - -void LLPanelPathfindingRebakeNavmesh::handleAgentState(BOOL pCanRebakeRegion) -{ - mCanRebakeRegion = pCanRebakeRegion; -} - -void LLPanelPathfindingRebakeNavmesh::handleRebakeNavMeshResponse(bool pResponseStatus) -{ - if (getMode() == kRebakeNavMesh_RequestSent) - { - setMode(pResponseStatus ? kRebakeNavMesh_InProgress : kRebakeNavMesh_Default); - } - - if (!pResponseStatus) - { - LLNotificationsUtil::add("PathfindingCannotRebakeNavmesh"); - } -} - -void LLPanelPathfindingRebakeNavmesh::handleNavMeshStatus(const LLPathfindingNavMeshStatus &pNavMeshStatus) -{ - ERebakeNavMeshMode rebakeNavMeshMode = kRebakeNavMesh_Default; - if (pNavMeshStatus.isValid()) - { - switch (pNavMeshStatus.getStatus()) - { - case LLPathfindingNavMeshStatus::kPending : - case LLPathfindingNavMeshStatus::kRepending : - rebakeNavMeshMode = kRebakeNavMesh_Available; - break; - case LLPathfindingNavMeshStatus::kBuilding : - rebakeNavMeshMode = kRebakeNavMesh_InProgress; - break; - case LLPathfindingNavMeshStatus::kComplete : - rebakeNavMeshMode = kRebakeNavMesh_NotAvailable; - break; - default : - rebakeNavMeshMode = kRebakeNavMesh_Default; - llassert(0); - break; - } - } - - setMode(rebakeNavMeshMode); -} - -void LLPanelPathfindingRebakeNavmesh::handleRegionBoundaryCrossed() -{ - createNavMeshStatusListenerForCurrentRegion(); - mCanRebakeRegion = FALSE; - LLPathfindingManager::getInstance()->requestGetAgentState(); -} - -void LLPanelPathfindingRebakeNavmesh::createNavMeshStatusListenerForCurrentRegion() -{ - if (mNavMeshSlot.connected()) - { - mNavMeshSlot.disconnect(); - } - - LLViewerRegion *currentRegion = gAgent.getRegion(); - if (currentRegion != NULL) - { - mNavMeshSlot = LLPathfindingManager::getInstance()->registerNavMeshListenerForRegion(currentRegion, boost::bind(&LLPanelPathfindingRebakeNavmesh::handleNavMeshStatus, this, _2)); - LLPathfindingManager::getInstance()->requestGetNavMeshForRegion(currentRegion, true); - } -} - -bool LLPanelPathfindingRebakeNavmesh::doDraw() const -{ - return (mCanRebakeRegion && (mRebakeNavMeshMode != kRebakeNavMesh_NotAvailable)); -} - -void LLPanelPathfindingRebakeNavmesh::updatePosition() -{ - S32 y_pos = 0; - S32 bottom_tb_center = 0; - - if (LLToolBar* toolbar_bottom = gToolBarView->getChild("toolbar_bottom")) - { - y_pos = toolbar_bottom->getRect().getHeight(); - bottom_tb_center = toolbar_bottom->getRect().getCenterX(); - } - - S32 left_tb_width = 0; - if (LLToolBar* toolbar_left = gToolBarView->getChild("toolbar_left")) - { - left_tb_width = toolbar_left->getRect().getWidth(); - } - - if(LLPanel* panel_ssf_container = getRootView()->getChild("state_management_buttons_container")) - { - panel_ssf_container->setOrigin(0, y_pos); - } - - S32 x_pos = bottom_tb_center-getRect().getWidth()/2 - left_tb_width + 113 /* width of stand/fly button */ + 10 /* margin */; - - setOrigin( x_pos, 0); -} diff --git a/indra/newview/llpanelpathfindingrebakenavmesh.h b/indra/newview/llpanelpathfindingrebakenavmesh.h deleted file mode 100644 index 48764f2aa7..0000000000 --- a/indra/newview/llpanelpathfindingrebakenavmesh.h +++ /dev/null @@ -1,96 +0,0 @@ -/** -* @file llpanelpathfindingrebakenavmesh.h -* @brief Header file for llpanelpathfindingrebakenavmesh -* @author Prep@lindenlab.com -* -* $LicenseInfo:firstyear=2012&license=viewerlgpl$ -* Second Life Viewer Source Code -* Copyright (C) 2012, 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 -* -* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -* $/LicenseInfo$ -*/ -#ifndef LL_LLPANELPATHFINDINGREBAKENAVMESH_H -#define LL_LLPANELPATHFINDINGREBAKENAVMESH_H - -#include - -#include "llpanel.h" -#include "llpathfindingmanager.h" -#include "llpathfindingnavmesh.h" - -class LLButton; -class LLPathfindingNavMeshStatus; - -class LLPanelPathfindingRebakeNavmesh : public LLPanel -{ - - LOG_CLASS(LLPanelPathfindingRebakeNavmesh); - -public: - static LLPanelPathfindingRebakeNavmesh* getInstance(); - - virtual BOOL postBuild(); - - virtual void draw(); - virtual BOOL handleToolTip( S32 x, S32 y, MASK mask ); - -protected: - -private: - typedef enum - { - kRebakeNavMesh_Available, - kRebakeNavMesh_RequestSent, - kRebakeNavMesh_InProgress, - kRebakeNavMesh_NotAvailable, - kRebakeNavMesh_Default = kRebakeNavMesh_NotAvailable - } ERebakeNavMeshMode; - - LLPanelPathfindingRebakeNavmesh(); - virtual ~LLPanelPathfindingRebakeNavmesh(); - - static LLPanelPathfindingRebakeNavmesh* getPanel(); - - void setMode(ERebakeNavMeshMode pRebakeNavMeshMode); - ERebakeNavMeshMode getMode() const; - - void onNavMeshRebakeClick(); - - void handleAgentState(BOOL pCanRebakeRegion); - void handleRebakeNavMeshResponse(bool pResponseStatus); - void handleNavMeshStatus(const LLPathfindingNavMeshStatus &pNavMeshStatus); - void handleRegionBoundaryCrossed(); - - void createNavMeshStatusListenerForCurrentRegion(); - - bool doDraw() const; - void updatePosition(); - - BOOL mCanRebakeRegion; - ERebakeNavMeshMode mRebakeNavMeshMode; - - LLButton* mNavMeshRebakeButton; - LLButton* mNavMeshSendingButton; - LLButton* mNavMeshBakingButton; - - LLPathfindingNavMesh::navmesh_slot_t mNavMeshSlot; - boost::signals2::connection mRegionCrossingSlot; - LLPathfindingManager::agent_state_slot_t mAgentStateSlot; -}; - -#endif // LL_LLPANELPATHFINDINGREBAKENAVMESH_H diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 1798d554b9..ec45a507e8 100755 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -122,11 +122,11 @@ #include "llkeyboard.h" #include "lllineeditor.h" #include "llmenugl.h" +#include "llmenuoptionpathfindingrebakenavmesh.h" #include "llmodaldialog.h" #include "llmorphview.h" #include "llmoveview.h" #include "llnavigationbar.h" -#include "llpanelpathfindingrebakenavmesh.h" #include "llpaneltopinfobar.h" #include "llpopupview.h" #include "llpreviewtexture.h" -- cgit v1.2.3 From 94bc6ac857955a4b152299730b3529262e1780c4 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 16 Oct 2012 21:04:35 -0400 Subject: Steam skin no longer needed: we can now embed "[sourceid]" in URLs. The whole point of --skin steam was to override (specifically) "create_account_url" in strings.xml, adding to each URL value a sourceid= URL parameter. Now we can do that more simply with '--set sourceid blah'. Less overhead, less maintenance, scales better to potential future sourceid values. Remove the steam skin. --- indra/newview/skins/steam/xui/da/strings.xml | 4483 ----------------------- indra/newview/skins/steam/xui/de/strings.xml | 5054 ------------------------- indra/newview/skins/steam/xui/en/strings.xml | 3910 -------------------- indra/newview/skins/steam/xui/es/strings.xml | 4961 ------------------------- indra/newview/skins/steam/xui/fr/strings.xml | 5054 ------------------------- indra/newview/skins/steam/xui/it/strings.xml | 4964 ------------------------- indra/newview/skins/steam/xui/ja/strings.xml | 5054 ------------------------- indra/newview/skins/steam/xui/pl/strings.xml | 4340 ---------------------- indra/newview/skins/steam/xui/pt/strings.xml | 4921 ------------------------- indra/newview/skins/steam/xui/ru/strings.xml | 5060 ------------------------- indra/newview/skins/steam/xui/tr/strings.xml | 5061 -------------------------- indra/newview/skins/steam/xui/zh/strings.xml | 4591 ----------------------- 12 files changed, 57453 deletions(-) delete mode 100644 indra/newview/skins/steam/xui/da/strings.xml delete mode 100644 indra/newview/skins/steam/xui/de/strings.xml delete mode 100644 indra/newview/skins/steam/xui/en/strings.xml delete mode 100644 indra/newview/skins/steam/xui/es/strings.xml delete mode 100644 indra/newview/skins/steam/xui/fr/strings.xml delete mode 100644 indra/newview/skins/steam/xui/it/strings.xml delete mode 100644 indra/newview/skins/steam/xui/ja/strings.xml delete mode 100644 indra/newview/skins/steam/xui/pl/strings.xml delete mode 100644 indra/newview/skins/steam/xui/pt/strings.xml delete mode 100644 indra/newview/skins/steam/xui/ru/strings.xml delete mode 100644 indra/newview/skins/steam/xui/tr/strings.xml delete mode 100644 indra/newview/skins/steam/xui/zh/strings.xml diff --git a/indra/newview/skins/steam/xui/da/strings.xml b/indra/newview/skins/steam/xui/da/strings.xml deleted file mode 100644 index 11d100eeff..0000000000 --- a/indra/newview/skins/steam/xui/da/strings.xml +++ /dev/null @@ -1,4483 +0,0 @@ - - - - - SECOND LIFE - - - Second Life Support Portal - - - Detekterer hardware... - - - Henter [APP_NAME]... - - - Tømmer cache... - - - Initialiserer tekstur cache... - - - Initialiserer VFS... - - - Gendanner... - - - Ændrer opløsning... - - - Logger p. [APP_NAME] kan virke laast. Vent venligst. - - - Logger på... - - - Validerer adgang - - - Udfører konto vedligeholdelse... - - - Tidligere forsø på login fejlede. Logger på, forsøg [NUMBER] - - - verden... - - - Klargør indbyggede web browser... - - - Klargør multimedia... - - - Indlæser skriftstyper... - - - Checker cache filer (kan tage 60-90 sekunder)... - - - Behandler svar ... - - - Initialiserer verden... - - - Behandler billeder... - - - Initialiserer QuickTime... - - - QuickTime ikke fundet- kunne derfor ikke initialisere. - - - QuickTime initialiseret. - - - Venter på svar fra region... - - - Tilslutter til region... - - - Henter tøj... - - - Serveren returnerede et ugyldigt eller ødelagt certifikat. Kontakt venligst administrator af dette net. - - - Et ugyldig hostnavn blev brugt for at få adgang til serveren. Check venligst din SLURL eller navnet på hosten. - - - Det certifikat der blev returneret ser ud til at være udløbet. Check venligst din systemtid på computeren. - - - Det certifikat der blev returneret af serveren kan ikke benyttes til SSL. Kontakt venligst administrator af dette net. - - - For mange certifikater i serverens certifikat streng. Kontakt venligst administrator af dette net. - - - Signaturen på certifkat der blev returneret af Second Life serveren kunne ikke bekræftes. - - - Netværksfejl: Kunne ikke etablere forbindelse, check venligst din netværksforbindelse. - - - Login fejlede. - - - Afslut - - - Den klient du benytter kan ikke længere få adgang til Second Life. Besøg venligst denne side for at hente en ny klient: -http://secondlife.com/download - -For mere information, se denne FAQ: -http://secondlife.com/viewer-access-faq - - - Valgfri klient opdatering tilgængelig: [VERSION] - - - Påkrævet opdatering a klient: [VERSION] - - - Din avatar er allerede logget på. - - - Beklager! vi kunne ikke logge dig på. -Undersøg venligst at du har indtastet det rette -Please check to make sure you entered the right - * Brugernavn (like bobsmith12 or steller.sunshine) - * Password -Check også at Caps Lock ikke er aktiveret. - - - Som en sikkerhedsforanstaltning er dit password blevet ændret. -Går venligst til din konto-side på http://secondlife.com/password -og besvar sikkerhedsspørgsmål for at nulstille dit password. -Vi undskylder besværet. - - - vi har lavet nogle ændringer i vores system og det er nødvendigt at nulstille dit password. -Går venligst til din konto-side på http://secondlife.com/password -og besvar sikkerhedsspørgsmål for at nulstille dit password. -Vi undskylder besværet. - - - Second Life er midlertidig lukket ned for vedligeholdelse. -Kun medarbejdere kan logge på for øjeblikket. -Check www.secondlife.com/status for opdateringer. - - - Second Life har midlertidig begrænset muligheden for log-in for at sikre, at brugere på systemet ikke får dårlige svartider. - - Brugere med gratis-konti vil ikke kunne logge på Second Life på dette tidspunkt. - - - Second Life kan ikke tilgås fra dennecomputer. -Hvis du mener dette er en fejl, kontakt venligst support@secondlife.com. - - - Din konto vil ikke være tilgængelig før -[TIME] Pacific Time. - - - Vi kan desværre ikke behandle forespøgsel lige nu. -Kontakt venligst Second Life supper for assitance via http://secondlife.com/support. -Hvis du ikke kan ændre dit password ring venligst +1 (866) 476-9763. - - - Data inkonsistens registret ved login. -Kontakt venligst support@secondlife.com. - - - Der foretages vedligehold på din konto lige nu. -Din konto vil ikke være tilgængelig før -[TIME] Pacific Time. -Hvis du mener dette er en fejl, kontakt venligst support@secondlife.com. - - - Forespøgsel på logout blev besvaret med en fejl fra simulatoren. - - - Systemet er i gang med at logge dig ud netop nu. -Din konto vil ikke være tilgængelig før -[TIME] Pacific Time. - - - Ikke muligt at oprette gyldig session. - - - Ikke muligt at koble til en simulator. - - - Din konto kan kun tilgå Second Life -mellem [START] [END] Pacific Time. -Kom venligst tilbage i dette tidsrum. - -Hvis du mener dette er en fejl, kontakt venligst support@secondlife.com. - - - Ugyldig parametre. -Hvis du mener dette er en fejl, kontakt venligst support@secondlife.com. - - - Fornavn parameter skal være alfanumerisk -Hvis du mener dette er en fejl, kontakt venligst support@secondlife.com. - - - Efternavn parameter skal være alfanumerisk -Hvis du mener dette er en fejl, kontakt venligst support@secondlife.com. - - - Regionen er lukket ned. -Prøv at logge på om lidt igen. - - - Avatar ikke i en region. -Prøv venligst om lidt igen. - - - The region was logging in another session. -Please try logging in again in a minute. - - - Region har lukket sidste session ned. -Prøv venligst om lidt igen. - - - Regionen er stadig ved at lukke forrige session ud. -Prøv venligst om lidt igen. - - - Region har lukket sidste session ned. -Prøv venligst om lidt igen. - - - Region er i gang med at logge ud. -Prøv venligst om lidt igen. - - - Region er i gang med at lukket sidste session ned. -Prøv venligst om lidt igen. - - - Denne region kan have problemer. Tjek venligst din forbindelse til internettet. - - - Gemmer indstillinger... - - - Logger ud... - - - Lukker ned... - - - Du er blevet frakoblet den region du var i. - - - Du blev sendt til en ugyldig region. - - - Tester frakobling af klient - - - Person - - - (intet navn) - - - Ejer: - - - Offentlig - - - (Gruppe) - - - Til salg: L$[AMOUNT] - - - Gruppe byg - - - Må ikke bygge - - - Gruppe byg - - - Ikke sikker område - - - Ingen flyvning - - - Gruppe scripts - - - Ingen Scripts - - - Land: - - - Kun et enkelt element kan trækkes ind her - - - - Klik for at se denne hjemmeside - - - Klik for at se information om denne lokation - - - Klik for at se beboers profil - - - Lær mere om denne beboer - - - Klik for at slukke for denne beboer - - - Klik for at fjern slukning for denne beboer - - - Klik for at sende IM til denne beboer - - - Klik for at betale denne beboer - - - Klik for at tilbyde teleport til denne beboer - - - Klik for at sende venneforespørgsel - - - Klik for at se denne gruppes beskrivelse - - - Klik for at se beskrivelse af denne event - - - Klik for at se denne annonce - - - Klik for at se beskrivelse for denne parcel - - - Klik for at teleportere til denne lokation - - - Klik for at se beskrivelse for dette objekt - - - Klik for at se denne lokation på kortet - - - Klik for at starte secondlife:// kommando - - - - Teleportér til - - - Vis kort for - - - Sluk - - - Fjern sluk - - - IM - - - Betal - - - Tilbyd teleport til - - - Venneforespørgsel - - - Luk (⌘W) - - - Luk (Ctrl+W) - - - Luk - - - Gendan - - - Minimér - - - Løsriv - - - Fastgør - - - Vis hjælp - - - Søger... - - - Intet fundet. - - - Henter... - - - Noter om version - - - http://wiki.secondlife.com/wiki/Release_Notes/ - - - Henter... - - - (ingen) - - - (venter) - - - (ingen) - - - Avaline opkalder [ORDER] - - - Ingen fejl - - - Element forespørgsel: fejlede - - - Element forespørgsel: fil findes ikke - - - Element forespørgsel: element ikke fundet i database - - - Slutning af fil - - - Kan ikke åbne fil - - - Fil ikke fundet - - - Tidsgrænse overskredet ved filhentning - - - Forbindelsen mistet - - - [APP_NAME] klient og server er uenige om prisen - - - Ukendt status - - - tekstur - - - lyd - - - visitkort - - - landemærke - - - ældre script - - - tøj - - - objekt - - - note - - - mappe - - - rod - - - LSL2 script - - - LSL bytecode - - - tga texture - - - kropsdel - - - foto - - - Fundne genstande - - - targa billede - - - Papirkurv - - - jpeg billede - - - animation - - - bevægelse - - - simstate - - - favorit - - - link - - - link til mappe - - - mesh - - - (Redigering Udseende) - - - Væk - - - Optaget - - - Blokeret - - - Bange - - - Vred - - - Væk - - - Baglæns salto - - - Hjertelig latter - - - Stort smil - - - Sende kys - - - Keder sig - - - Buk - - - Klap - - - Højtideligt buk - - - Græd - - - Dans 1 - - - Dans 2 - - - Dans 3 - - - Dans 4 - - - Dans 5 - - - Dans 6 - - - Dans 7 - - - Dans 8 - - - Foragt - - - Drik - - - Flov - - - Løftet finger - - - Knytnæve - - - Svævende yoga - - - Mistroisk - - - Utålmodig - - - Glædeshop - - - Kys min r.. - - - Kys - - - Grin - - - Bodybuilder - - - Nej (sur) - - - Nej - - - Æv-bæv - - - Et-to slag - - - Åben mund - - - Peace - - - Peg på andre - - - Peg på dig selv - - - Slå venstre - - - Slå højre - - - SSP - Tæl - - - SSP - Papir - - - SSP - Sten - - - SSP - Saks - - - Misfornøjet - - - Karatepark - - - Ked af det - - - Honnør - - - Råb - - - Skuldertræk - - - Smil - - - Ryg - - - Indhalér - - - Smid cigaret - - - Overrasket - - - Sværdslag - - - Ekstatisk - - - Tunge ud - - - Vink - - - Knib øje i - - - Pift - - - Blink - - - Blink (Hollywood) - - - Bekymret - - - Ja (glad) - - - Ja - - - Indlæser... - - - Offline - - - [AREA] m² L$[PRICE] - - - Ingen fundet. - - - OK - - - Fil slutter for tidligt - - - Kan ikke funde ROOT eller JOINT. - - - hvisker: - - - råber: - - - Forbinder til stemmechat... - - - Forbundet - - - Stemmechat er ikke tilladt hvor du befinder dig - - - Stemme chat er afbrudt - - - Du vil nu blive dirigeret til lokal stemme chat - - - '[OBJECTNAME]', en genstand, ejet af '[OWNERNAME]', lokaliseret i [REGIONNAME] på [REGIONPOS], har fået tilladelse til: [PERMISSIONS]. - - - '[OBJECTNAME]', en genstand, ejet af '[OWNERNAME]', lokaliseret i [REGIONNAME] på [REGIONPOS], er afvist tilladelse til: [PERMISSIONS]. - - - Tag Linden dollars (L$) fra dig - - - Reagér på dine kontrol-taster - - - Ændre dine kontrol-taster - - - Animér din avatar - - - Sæt på din avatar - - - Fjern ejerskabet og sæt til offentlig - - - Sammenkæd og adskil andre genstande - - - Tilføj og fjern sammenkødninger med andre genstande - - - Ændre dens tilladelser - - - Spor dit kamera - - - Kontrollér dit kamera - - - PG - - - Mature - - - Adult - - - logget af - - - Ukendt - - - (ukendt) - - - Estate / Hel region - - - Estate / Homestead - - - Mainland / Homestead - - - Mainland / Hel region - - - Alle filer - - - Lyde - - - Animationer - - - Billeder - - - Gem - - - Hent - - - Targa billeder - - - Bitmap billeder - - - AVI film fil - - - XAF Anim Fil - - - XML Fil - - - RAW fil - - - Komprimerede billeder - - - Hent filer - - - Vælg bibliotek - - - Sæt "til stede" - - - Sæt "væk" - - - Sæt "ledig" - - - Sæt "optaget" - - - Form - - - Hud - - - Hår - - - Øjne - - - Trøje - - - Bukser - - - Sko - - - Strømper - - - Jakke - - - Handsker - - - Undertrøje - - - Underbukser - - - Nederdel - - - Alpha - - - Tatovering - - - Fysik - - - ugyldig - - - ingen - - - Trøje - ikke på - - - Bukser - ikke på - - - Sko - ikke på - - - Strømper - ikke på - - - Jakke - ikke på - - - Handsker - ikke på - - - Undertrøje - ikke på - - - Underbukser - ikke på - - - Nederdel - ikke på - - - Alpha ikke benyttet - - - Tatovering ikke benyttet - - - Ikke noget fysisk båret - - - ugyldig - - - Opret ny figur - - - Opret nyt hud - - - Opret nyt hår - - - Opret nye øjne - - - Opret ny trøje - - - Opret nye bukser - - - Opret nye sko - - - Opret nye strømper - - - Opret ny jakke - - - Opret nye handsker - - - Opret ny undertrøje - - - Opret nye underbukser - - - Opret ny nederdel - - - Opret ny alpha - - - Opret ny tatovering - - - Opret ny fysik - - - ugyldig - - - Ny [WEARABLE_ITEM] - - - Næste - - - OK - - - Gruppe besked - - - Gruppe besked - - - Sendt af - - - Vedhæftet: - - - Se tidligere beskeder eller slå modtagelse af beskeder fra her. - - - Åben vedhæng - - - Gem vedhæng - - - Teleport tilbud - - - Nye beskeder modtaget mens du var væk...... - - - Du har %d mere besked(er) - - - Højre arm - - - Hoved - - - Venstre arm - - - Venstre ben - - - Overkrop - - - Højre ben - - - Lav - - - Middel - - - Høj - - - Tryk ESC for at skift til normalt udsyn - - - Fandt du ikke hvad du søgte? Prøv [secondlife:///app/search/all/[SEARCH_TERM] Search]. - - - Fandt du ikke hvad du søgte? Prøv [secondlife:///app/search/places/[SEARCH_TERM] Search]. - - - Træk et landemærke hertil for at tilføje den som favorit. - - - Du har ikke en kopi af denne tekstur i din beholdning - - - - - - - - - Henter indhold... - - - Intet indhold - - - - - - - - - - - - - - - - - - - - - - - - - - - - Min beholdning - - - Bibliotek - - - Teksturer - - - Lyde - - - Visitkort - - - Landemærker - - - Scripts - - - Tøj - - - Objekter - - - Noter - - - Ny mappe - - - Beholdning - - - Ukomprimerede billeder - - - Kropsdele - - - Papirkurv - - - Fotoalbum - - - Fundne genstande - - - Ukomprimerede lyde - - - Animationer - - - Bevægelser - - - Mine favoritter - - - Mine favoritter - - - Nuværende sæt - - - Start sæt - - - Mine sæt - - - Tilbehør - - - Meshes - - - Venner - - - Alle - - - Køb - - - Køb for L$ - - - Sten - - - Metal - - - Glas - - - Træ - - - Kød - - - Plastik - - - Gummi - - - Lys - - - Shift - - - Ctrl - - - Bryst - - - Hovedskal - - - Venstre skulder - - - Højre skulder - - - Venstre hånd - - - Højre hånd - - - Venstre fod - - - Højre fod - - - Rygsøjle - - - Bækken - - - Mund - - - Hage - - - Venstre øre - - - Højre øre - - - Venstre øje - - - Højre øje - - - Næse - - - H overarm - - - H underarm - - - V overarm - - - V underarm - - - Højre hofte - - - Højre lår - - - H underben - - - Venstre hofte - - - Venstre lår - - - V underben - - - Mave - - - Venstre bryst - - - Højre bryst - - - Ugyldig vedhæftningspunktt - - - [AGEYEARS] [AGEMONTHS] gammel - - - [AGEYEARS] gammel - - - [AGEMONTHS] gammel - - - [AGEWEEKS] gammel - - - [AGEDAYS] gammel - - - Med fra i dag - - - [COUNT] år - - - [COUNT] år - - - [COUNT] år - - - [COUNT] måned - - - [COUNT] måneder - - - [COUNT] måneder - - - [COUNT] uge - - - [COUNT] uger - - - [COUNT] uger - - - [COUNT] dag - - - [COUNT] dage - - - [COUNT] dage - - - [COUNT] medlem - - - [COUNT] medlemmer - - - [COUNT] medlemmer - - - Beboer - - - På prøve - - - Æresmedlemmer - - - Linden Lab medarbejder - - - Betalende medlem - - - Betalingsinfo registreret - - - Ingen betalingsinfo - - - Alders-checket - - - Ikke alders-checket - - - Center 2 - - - Øverst højre - - - Top - - - Øverst venstre - - - Centrum - - - Nederst venstre - - - Nederst midt - - - nederst højre - - - Hentet, kompilerer nu - - - Script ikke fundet på server. - - - Problem ved download - - - Ikke rettigheder til at downloade script. - - - Ikke nok rettigheder til at - - - Ukendt fejl ved download - - - Rekompilering fremskridt - - - Rekompilér - - - Nulstil fremskridt - - - nulstil - - - Sæt "running" fremskridt - - - sæt til "running" - - - Sæt "Not Running" fremskridt - - - sæt til "not running" - - - Kompleret uden fejl! - - - Kompileret uden fejl, gemmer... - - - Gemt. - - - Script ("object out of range") - - - Objekt [OBJECT] ejet af [OWNER] - - - ingen - - - - (ukendt) - - - - - - - Balance - - - Kredit - - - Debet - - - Total - - - Ingen gruppedata fundet for gruppe - - - overordnet estate - - - mainland - - - teen - - - fejl - - - alle estates ejet af [OWNER] - - - alle estates du ejer - - - alle estates du administrerer for [OWNER] - - - Godkendte beboere: ([ALLOWEDAGENTS], maks. [MAXACCESS]) - - - Godkendte grupper: ([ALLOWEDGROUPS], max. [MAXACCESS]) - - - Parcel script memory - - - Parceller listet: [PARCELS] - - - Memory brugt: [COUNT] kb ud af [MAX] kb; [AVAILABLE] kb tilgængeligt - - - Memory brugt: [COUNT] kb - - - Parcel Script URL'er - - - URL'er brugt: [COUNT] ud af [MAX]; [AVAILABLE] tilgængelige - - - URL'er brugt: [COUNT] - - - Fejl ved anmodning om information - - - Ingen parcel valgt - - - Fejl: script information er kun tilgængelig i den nuværende region - - - Henter information... - - - Du har ikke rettigheder til at undersøge denne parcel - - - Sidder på - - - Bryst - - - Hoved - - - Venstre skulder - - - Højre skulder - - - Venstre hånd - - - Højre hånd - - - Venstre fod - - - Højre fod - - - Ryg - - - Bækken - - - Mund - - - Hage - - - Venstre øre - - - Højre øre - - - Venstre øje - - - Højre øje - - - Næse - - - Højre overarm - - - Højre underarm - - - Venstre overarm - - - Venstre underarm - - - Højre hofte - - - Højre lår - - - Højre underben - - - Venstre hofte - - - Venste lår - - - Venstre underben - - - Mave - - - Højre bryst - - - Venstre bryst - - - HUD Center 2 - - - HUD Øverst til højre - - - HUD Foroven midtpå - - - HUD Øverst til venstre - - - HUD Center 1 - - - HUD Nederst til venstre - - - HUD For neden - - - HUD Nederst til højre - - - Linie [LINE], Kolonne [COLUMN] - - - [COUNT] fundet - - - Indhold i objekt - - - Nyt script - - - Beboeren du sendte en besked er 'optaget', hvilket betyder at han/hun ikke vil forstyrres. Din besked vil blive vis i hans/hendes IM panel til senere visning. - - - (Efter navn) - - - (beboer) - - - (Objekt) - - - (Gruppe) - - - (Ekstern) - - - Der er ingen regler for dette estate. - - - Der er ingen regler for dette estate. Land på dette estate sælges af estate ejeren, ikke af Linden Lab. Kontakt venligst estate ejeren for detaljer om salg. - - - - - - Gruppe ejet - - - Offentlig - - - Klik: [TELEPORT] teleport, [MAP] kort, [PROFILE] profil - - - (vil blive opdateret efter offentliggørelse) - - - Du har ikke oprettet nogen favoritter eller annoncer. Klik på plus knappen nedenfor for at oprette en favorit eller en annonce. - - - Bruger har ingen favoritter eller annoncer - - - Henter... - - - Vis først - - - Egenskaber - - - Et objekt med navnet - - - ejet af gruppen - - - ejet af en ukendt gruppe - - - ejet af - - - ejet af en ukendt bruger - - - gav dig - - - Du afslår [DESC] fra <nolink>[NAME]</nolink>. - - - Total - - - købt - - - betalte dig - - - betalt til - - - købte adgang til - - - betalte gebyr for event - - - betalte prisen for event - - - Balance - - - Kredit - - - Debet - - - Indhold - - - Anskaffede genstande - - - Annullér - - - Uploader [NAME] pris L$ [AMOUNT] - - - At købe dette koster L$ [AMOUNT] - - - Ukendt fil efternavn [.%s] -Forventet .wav, .tga, .bmp, .jpg, .jpeg, or .bvh - - - Blokér - - - Tilføj landemærke... - - - Redigér landemærke... - - - ⌃ - - - ⌘ - - - ⌥ - - - ⇧ - - - Ctrl+ - - - Alt+ - - - Shift+ - - - Fil gemt - - - Modtager - - - AM - - - PM - - - PST - - - PDT - - - Fremad - - - Venstre - - - Højre - - - Bagud - - - Nord - - - Syd - - - Vest - - - Øst - - - Op - - - Ned - - - Enhver kategori - - - Shopping - - - Land til leje - - - Grunde til leje - - - Speciel attraktion - - - Nye produkter - - - Jobs - - - Søges - - - Service - - - Personlig - - - Ingen - - - Linden sted - - - Adult - - - Kunst & kultur - - - Business - - - Uddannelse - - - Spil - - - Afslapning - - - Nybegynder venligt - - - Parker & natur - - - Beboelse - - - Fase - - - Andet - - - Leje - - - Enhver - - - Du - - - Flere medietyper - - - Afspil/Pause medie - - - Der opstod en fejl ved afvikling af kommandolinie. -Se venligst: http://wiki.secondlife.com/wiki/Client_parameters -Fejl: - - - [APP_NAME] Kommando linie brug: - - - [APP_NAME] kan ikke få adgang til fil den/det skal bruge. - -Dette kan skyldes at du har flere kopier kørende eller operativsystemet tror at filen allerede er åben. -Hvis fejlen bliver ved, genstart computer og prøv igen. -Hvis fejlen stadig bliver ved, kan det være nødvendigt at afinstallere [APP_NAME] og installere igen. - - - Fatal fejl - - - [APP_NAME] kræver en processor med AltiVec (G4 eller nyere). - - - [APP_NAME] kører allerede. -Undersøg din "task bar" for at se efter minimeret version af programmet. -Hvis fejlen fortsætter, prøv at genstarte din computer. - - - [APP_NAME] ser ud til at være "frosset" eller gået ned tidligere. -Ønsker du at sende en fejlrapport? - - - Besked - - - [APP_NAME] kan ikke detektere DirectX 9.0b eller nyere. -[APP_NAME] benytte DirectX til at detektere hardware og/eller forældede drivere der kan give problemer med stabilitet, dårlig hastighed eller nedbrud. Selvom du kan køre [APP_NAME] uden det, anbefaler vi meget at køre med DirectX 9.0b. - -Ønsker du at fortsætte? - - - Advarsel - - - Automatisk opdatering er endnu ikke implementeret på Linux. -Hent venligst den nyeste version på www.secondlife.com. - - - RegisterClass fejlede - - - Fejl - - - Ikke muligt at køre i fuldskærm med [WIDTH] x [HEIGHT]. -Afvikler i vindue. - - - Nedlukningsfejl ved lukning af vindue (DestroyWindow() fejlede) - - - Fejl ved nedlukning - - - Kan ikke oprette "GL device context" - - - Kan ikke finde passende "pixel format" - - - Kan ikke finde "pixel format" beskrivelse - - - [APP_NAME] kræver "True Color (32-bit)" for at kunne køre. -Gå venligst til din computers skærmopsætning og sæt "color mode" til 32-bit. - - - [APP_NAME] kan ikke køre, da den ikke kan finde en "8 bit alpha channel". Normalt skyldes dette et problem med en video driver. -Venligst undersøg om du har de nyeste drivere til dit videokort installeret. -Din skærm skal også være sat op til at køre "True Color (32-bit)" i din displayopsætning. -Hvis du bliver ved med at modtage denne besked, kontakt [SUPPORT_SITE]. - - - Kan ikke sætte "pixel format" - - - Kan ikke oprette "GL rendering context" - - - Kan ikke aktivere "GL rendering context" - - - [APP_NAME] kan ikke afvikles da driverne til dit videokort ikke blev installeret korrekt, er forældede, eller du benytter hardware der ikke er supporteret. Undersøg venligst om du har installeret de nyeste drivere til dit grafikkort, og selv om du har de nyeste, prøv at geninstallere dem. - -Hvis du bliver ved med at modtage denne besked, kontakt venligst [SUPPORT_SITE]. - - - Skægstubbe - - - Helt hvidt - - - Store øjne - - - Spidst - - - Armængde - - - Vedhæftet - - - Vedhæftede øreflipper - - - Nakkehår - - - Posede - - - Pandehår - - - Stikkende øjne - - - Mave størrelse - - - Stor - - - Stor bagdel - - - Stort hår: Bag - - - Stort hår: Foran - - - Stort hår: Top - - - Stort hovede - - - Store brystmuskler - - - Store spikes - - - Sort - - - Blond - - - Blondt hår - - - Rødmen - - - Rødme farve - - - Rødme gennemsigtighed - - - Kropskontur - - - Kropsfedt - - - Fregner på kroppen - - - Tyk krop - - - Kropstykkelse - - - Tynd krop - - - Hjulbenet - - - Bryst tyngdepåvirkning - - - Kavalergang - - - Bryststørrelse - - - Bredde næseryg - - - Bred - - - Størrelse øjenbryn - - - Udstående øjne - - - Udstående øjne - - - Kartoffelnæse - - - Kartoffelnæse - - - Bryst fylde - - - Brystudjævning - - - Bryst tyngde - - - Bryst inerti - - - Maks. effekt - - - Fjeder - - - Øg - - - Dæmpning - - - Maks. effekt - - - Fjeder - - - Øg - - - Dæmpning - - - Maks. effekt - - - Fjeder - - - Øg - - - Dæmpning - - - Mave omfang - - - maveudjævning - - - Mave tyngde - - - Mave inerti - - - Maks. effekt - - - Fjeder - - - Øg - - - Dæmpning - - - Bagdel omfang - - - Bagdelsudjævning - - - Bagdel tyngde - - - Bagdel modstand - - - Maks. effekt - - - Fjeder - - - Øg - - - Dæmpning - - - Maks. effekt - - - Fjeder - - - Øg - - - Dæmpning - - - Buskede øjenbryn - - - Busket hår - - - Størrelse bagdel - - - Bagdel tyngde - - - Tournure - - - Ingen tournure - - - Mere tournure - - - Chaplin - - - Kindben - - - Bryst størrelse - - - Hage form - - - Hagekløft - - - Hageskæg - - - Hage dybde - - - Stort forneden - - - Vigende hage - - - Hage frem - - - Hals under hage - - - Slet - - - Kløft - - - Tætsiddende øjne - - - Lukket - - - Lukket bagtil - - - Lukket foran - - - Lukket til venstre - - - Lukket til højre - - - Lille - - - Krave bagtil - - - Krave foran - - - Nedadvendt - - - Opadvendt - - - Rynket - - - Skæv næse - - - Svaj - - - Mørk - - - Mørkegrøn - - - Mørkere - - - Dyb - - - Standard hæle - - - Tæt - - - Dobbelthage - - - Peger nedad - - - Stort - - - Øre vinkel - - - Øre størrelse - - - Ørespidser - - - Ovalt hovede - - - Poser under øjne - - - Øjenfarve - - - Øjendybde - - - Øjennuance - - - Øjenåbning - - - Øjensymmetri - - - Øjenstørrelse - - - Øjenafstand - - - Bue på øjenbryn - - - Tæthed øjenbryn - - - Højde på øjenbryn - - - Løftede øjenbryn - - - Størrelse øjenbryn - - - Længde øjenvipper - - - Eyeliner - - - Eyeliner farve - - - Udstående øjne - - - Ansigts symmetri - - - Ansigtskonturer - - - Stor afstand mellem øjne - - - Tykke læber - - - Kvinde - - - Fingerløse - - - Fingre - - - Stor vidde - - - Flad - - - Flad bagdel - - - Fladt hovede - - - Flad snude - - - Størrelse fod - - - Pande vinkel - - - Stort foroven - - - Fregner - - - Frynser foran - - - Langt ud bagtil - - - Meget eyeliner - - - Langt frem fortil - - - Hår i siderne - - - Meget hår - - - Skinnende - - - Fingre i handsker - - - Handskelængde - - - Hår - - - Hår: Bagtil - - - Hår: Foran - - - Hår: Siderne - - - Strøget hår - - - Hår tykkelse - - - Hår tykkelse - - - Hældning - - - mest hår venstre - - - Mest hår højre - - - Hår: Volumen - - - Størrelse hånd - - - Cykelstyr - - - Længde på hovede - - - Hovedform - - - Hovedstørrelse - - - Hovedhøjde - - - Hælhøjde - - - Hælform - - - Højde - - - Høj - - - Hæje hæle - - - Høj kæbe - - - Høje såle - - - Høj og tæt - - - Højere - - - Hoftelængde - - - Hoftebredde - - - Inde - - - Indre skygge farve - - - Indre skygge gennemsigtighed - - - Inderste del af øje - - - Inderste øjenskygge - - - Indre skygge - - - Jakkelængde - - - Jakkerynker - - - Kæbevinkel - - - Kæbefremspring - - - Kæbeform - - - Saml - - - Kindehud - - - Knævinkel - - - Kalveknæet - - - Stor - - - Store hænder - - - Venstre side - - - Benlængde - - - Benmuskler - - - Mindre - - - Mindre kropsfedt - - - Mindre - - - Færre fregner - - - Mindre - - - Mindre - - - Mindre bildæk - - - Færre muskler - - - Mindre muskuløs - - - Mindre rosa - - - Mindre rund - - - Mindre - - - Mindre - - - Mindre - - - Mindre - - - Lettere - - - Læbekløft - - - Dybde læbekløft - - - Fyldige læber - - - Lyserøde læber - - - Læbeproportioner - - - Læbetykkelse - - - Læbebredde - - - Lipgloss - - - Læbestift - - - Læbestift farve - - - Lang - - - Langt hovede - - - Lange hofter - - - Bange ben - - - Lang hals - - - Lange rottehaler - - - Lang hestehale - - - Lang overkrop - - - Lange arme - - - Løse bukser - - - Løs trøje - - - Løse ærmer - - - Bildæk - - - Lav - - - Flade hæle - - - Lav kæbe - - - Flade såler - - - Lav og løs - - - Nedre - - - Nedre næseryg - - - Nedre kinder - - - Mand - - - Midterste del - - - Mere - - - Mere rødmen - - - Mere kropsfedt - - - Mere - - - Mere øjenskygge - - - Flere fregner - - - Mere - - - Mere - - - Mere læbestift - - - Mere bildæk - - - Mere underlæbe - - - Flere muskler - - - Mere muskuløs - - - Mere rosa - - - Mere rund - - - Mere - - - Mere skrå - - - Mere firkantet - - - Mere overlæbe - - - Mere lodret - - - Mere - - - Mere - - - Overskæg - - - Mundvige - - - Position mund - - - Intet hår - - - Muskuløs - - - Lange - - - Neglelak - - - Neglelak farve - - - Smal - - - Smal bagtil - - - Smal fortil - - - Smalle læber - - - Naturlig - - - Halslængde - - - Halstykkelse - - - Ingen rødmen - - - Ingen eyeliner - - - Ingen øjenskygge - - - Ingen lipgloss - - - Ingen læbestift - - - Ingen dele - - - Ingen lak - - - Ingen rød - - - Ingen spikes - - - Ingen hvid - - - Ingen rynker - - - Normal nedre - - - Normal øvre - - - Højre - - - Venstre - - - Næse størrelse - - - Næse tykkelse - - - Næsetip vinkel - - - Næsetip form - - - Næse bredde - - - Næsebor adskillelse - - - Næsebor bredde - - - Uigennemsigtig - - - Åben - - - Åben bagtil - - - Åben foran - - - Åben til venstre - - - Åben til højre - - - Orange - - - Ud - - - Ydre skygge farve - - - Ydre skygge uigennemsigtighed - - - Yderste del af øje - - - Ydre øjenskygge - - - Ydre skygge - - - Overbid - - - Skridt - - - Malede negle - - - Bleg - - - Bukser skridt - - - Pasform bukser - - - Bukser - længde - - - Bukser - vidde - - - Bukser - rynker - - - Skilning - - - Skilning - - - Brystmuskler - - - Pigmentering - - - Rottehaler - - - Pink - - - Mere pink - - - Højde sål - - - Bredde sål - - - Spids - - - Spidse hæle - - - Hestehale - - - Strutskørt - - - Forstør venstre øje - - - Forstør højre øje - - - Posede - - - Posede øjenlåg - - - Regnbue farver - - - Rødt hår - - - Almindelig - - - Højre skildning - - - Rosa teint - - - Rund - - - Rødmossethed - - - Rødmosset - - - Krøllet hår - - - Ridebukselår - - - Magert ben - - - Separat - - - Lille - - - Afklippet bagi - - - Skævt ansigt - - - "Måne" - - - Venstre op - - - Højre op - - - Afklippet bagtil - - - Måne - - - Mod venstre - - - Flyt mund - - - Mod højre - - - Trøje - bund - - - Trøje - pasform - - - Trøje - rynker - - - Sko højde - - - Kort - - - Korte arme - - - Korte ben - - - Kort hals - - - Korte rottehaler - - - Kort hestehale - - - Korte - - - Kort overkrop - - - Korte hofter - - - Skuldre - - - Side frynser - - - Bakkenbarter - - - Sidehår - - - Ned - - - Op - - - Tynd hals - - - Omfang - - - Længde nederdel - - - Skrånende pande - - - Ærmelængde - - - Ærmer - stramhed - - - Slids: Bag - - - Slids: Foran - - - Slids: Venstre - - - Slids: Højre - - - Lille - - - Små hænder - - - Lille hovede - - - Glat - - - Glat hår - - - Strømper - længde - - - Soulpatch - - - Sparsomt - - - Hår med "spikes" - - - Firkantet - - - Firkantet snude - - - Bredt hovede - - - Stræk hovede - - - Indsunket - - - Indsunket bryst - - - Dybtliggende øjne - - - Stryge tilbage - - - Stryge fremad - - - Høj - - - Indsnævring bag - - - Indsnævring foran - - - Brede hæle - - - Bred nakke - - - Bred snude - - - Tynd - - - Tynde øjenbryn - - - Tynde læber - - - Tynd næse - - - Stram hage - - - Smalle bukseben - - - Stramme bukser - - - Stram trøje - - - Stram nederdel - - - Stramme ærmer - - - Sko form - - - Tykkelse af snud - - - Overkrop - længde - - - Overkrop - muskler - - - Overkrop - mager - - - Ikke vedhæftet - - - Glat - - - Underbid - - - Unaturlig - - - Øverste næseryg - - - Øvre kinder - - - Øvre hagekløft - - - Øvre øjenlåg - - - Opadvendt - - - Meget rød - - - Talje højde - - - Velnæret - - - Hvidt hår - - - Bred - - - Bredt - - - Bredt - - - Brede læber - - - Vildt - - - Rynker - - - Tilføj til mine landemærker - - - Rediger mit landemærke - - - Se yderligere information om nuværende lokation - - - Min lokationshistorik - - - Adult region - - - Moderate region - - - Generel region - - - [APP_NAME] Opdatér - - - Opdaterer nu [APP_NAME]... - - - Installerer [APP_NAME]... - - - Din [APP_NAME] klient bliver opdateret til nyeste version. Dette kan tage noget tid, så venligst vær tålmodig. - - - Download færdig... - - - Downloader opdatering - - - Download af opdatering fejlede - - - Der opstod en fejl ved opdatering af [APP_NAME]. Hent venligst den nyeste version fra www.secondlife.com. - - - Installation af opdatering fejlede - - - Opstart af klient fejlede - - - [APP_NAME]: Genstande modtages for hurtigt fra [FROM_NAME], automatisk visning er slået fra i [TIME] sekunder - - - [APP_NAME]: Genstande modtages for hurtigt, automatisk visning er slået fra i [TIME] sekunder - - - -- Logning af IM aktiveret -- - - - [NAME] skriver... - - - (Uden navn) - - - (Modereret: Stemmer deaktiveret) - - - Tekst chat er ikke tilgængelig i denne samtale. - - - Din tekst chat er blevet deaktiveret af en gruppe moderator. - - - Klik her for privat besked (IM). - - - Til - - - (Moderator) - - - (Gemt [LONG_TIMESTAMP]) - - - Dit opkald er blevet besvaret - - - Du startede dette stemme kald - - - Du er nu med i stemme opkald - - - [NAME] startede et stemmekald - - - Tilslutter stemme opkald... - - - Forbundet, klik på Forlad for at lægge på - - - Forladt stemme opkald - - - Konference med [AGENT_NAME] - - - (IM session eksisterer ikke) - - - Du er den eneste deltager i denne samtale - - - [NAME] er logget af. - - - Tryk på [BUTTON NAME] knappen for at acceptére/tilslutte til denne stemme chat. - - - Du har blokeret denne beboer. Sendes en besked, vil beboeren fjernes fra blokeringslisten. - - - Fejl ved anmodning, prøv venligst igen senere - - - Kunne ikke etablere forbindelse, prøv igen senere - - - Du har ikke de fornødne rettigheder. - - - Denne samtale er lukket ned - - - Du har ikke den mulighed. - - - Du har ikke den mulighed. - - - Du er ikke moderator for denne samtale. - - - En gruppe moderator har deaktiveret din tekst chat. - - - Du er blevet "blokeret". - - - Ikke muligt at tilføge brugere til samtale med [RECIPIENT]. - - - Kunne ikke sende din besked til session med [RECIPIENT]. - - - Ikke muligt at sende din besked til samtalen med [RECIPIENT]. - - - Fejl under moderation. - - - Du er blevet fjernet fra denne gruppe. - - - Du er blevet fjernet fra gruppen. - - - Du har ikke længere mulighed for at deltage i samtalen - - - [SOURCES] har sagt noget nyt - - - [SOURCES] har sagt noget nyt - - - Initialisering af session er "timed out" - - - Hjemmeposition sat. - - - http://secondlife.com/landing/voicemorphing - - - [NAME] betalte dig L$[AMOUNT] [REASON]. - - - [NAME] betalte dig L$[AMOUNT]. - - - Du betalte [NAME] L$[AMOUNT] [REASON]. - - - Du betalte L$[AMOUNT]. - - - Du betalte [NAME] L$[AMOUNT]. - - - Du betalte L$[AMOUNT] [REASON]. - - - til [ITEM] - - - for en parcel land - - - for en billet til land - - - for dedikering af land - - - for at oprette gruppe - - - for at blive medlem i gruppe - - - for at uploade - - - til offentliggørelse af annonce - - - Giver L$ [AMOUNT] - - - Pris for upload er L$ [AMOUNT] - - - Prisen er L$ [AMOUNT] - - - Køber valgte land L$ [AMOUNT] - - - Dette objekt koster L$ [AMOUNT] - - - Enhver - - - Administratorer - - - Ejere - - - Online - - - Uploader... - -Krænkelsesanmeldelse - - - Ny kropsbygning - - - Ny hud - - - Nyt hår - - - Nye øjne - - - Ny trøje - - - Nye bukser - - - Nye sko - - - Nye strømper - - - Ny jakke - - - Nye handsker - - - Ny undertrøje - - - Nye underbukser - - - Ny nederdel - - - Ny alpha - - - Ny tatovering - - - Ny fysik - - - Kan ikke tages på - - - Ny bevægelse - - - Nyt script - - - Ny note - - - Ny folder - - - Indhold - - - Bevægelse - - - Mandlige bevægelser - - - Kvindelige bevægelser - - - Andre bevægelser - - - Tale bevægelser - - - Almindelige bevægelser - - - Mand - Undskyld mig - - - Mand - Skrid! - - - Mand - Pust et kys - - - Mand - Boo - - - Mand - Keder sig - - - Mand - Hey - - - Mand - Latter - - - Mand - Frastødt - - - Mand - Skuldertræk - - - Mand - Stik tunge ud - - - Mand - Wow - - - Kvinde - Kluklatter - - - Kvinde - gråd - - - Kvinde - Flov - - - Kvinde - Undskyld mig - - - Kvinde - Skrid! - - - Kvinde - Pust et kys - - - Kvinde - Boo - - - Kvinde - Keder sig - - - Kvinde - Hey - - - Kvinde - Hey baby - - - Kvinde - Latter - - - Kvinde - "Ser godt ud" - - - Kvinde - Herovre - - - Kvinde - Be´ om - - - Kvinde - Frastødt - - - Kvinde - Skuldertræk - - - Kvinde - Stik tungen ud - - - Kvinde - Wow - - - [mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt] - - - ingen/ingen - - - Kan ikke hente billeder større end [WIDTH]*[HEIGHT] - - - - Desværre er noget gået galt. - - Check venligst status.secondlifegrid.net for at se om der skulle være driftsproblemer. - Hvis du bliver ved med at have problemer, check venligst din firewall- og netværksopsætning. - - - Søndag:Mandag:Tirsdag:Onsdag:Torsdag:Fredag:Lørdag - - - Søn:Man:Tir:Ons:Tor:Fre:Lør - - - Januar:Februar:Marts:April:Maj:Juni:Juli:August:September:Oktober:November:December - - - Jan:Feb:Mar:Apr:Maj:Jun:Jul:Aug:Sep:Okt:Nov:Dec - - - [MDAY] - - - AM - - - PM - - - US$ [AMOUNT] - - - Medlemsskab - - - Roller - - - Gruppe identitet - - - Parcel håndtering - - - Parcel identitet - - - Parcel opsætning - - - Parcel beføjelser - - - Parcel adgang - - - Parcel indhold - - - Objekt håndtering - - - Regnskab - - - Beskeder - - - Chat - - - Slet valgte genstande? - - - Slet valgte genstand? - - - Der er ingen genstande i dette sæt - - - Vælg en editor via opsætningen for Ekstern editor. - - - Kan ikke benytte deb eksterne editor der er angivet. -Prøv at omkrandse stien til editor med anførselstegn. -(f.eks. "/stil til min editor" "%s") - - - Fejl ved håndtering af kommando til ekstern editor. - - - Den eksterne editor kunne ikke startes. - - - Esc - - - Space - - - Enter - - - Tab - - - Ins - - - Del - - - Backsp - - - Shift - - - Ctrl - - - Alt - - - CapsLock - - - Hjem - - - End - - - PgUp - - - PgDn - - - F1 - - - F2 - - - F3 - - - F4 - - - F5 - - - F6 - - - F7 - - - F8 - - - F9 - - - F10 - - - F11 - - - F12 - - - Tilføj - - - Træk fra - - - Multiplicer - - - Divider - - - PAD_DIVIDE - - - PAD_LEFT - - - PAD_RIGHT - - - PAD_DOWN - - - PAD_UP - - - PAD_HOME - - - PAD_END - - - PAD_PGUP - - - PAD_PGDN - - - PAD_CENTER - - - PAD_INS - - - PAD_DEL - - - PAD_Enter - - - PAD_BUTTON0 - - - PAD_BUTTON1 - - - PAD_BUTTON2 - - - PAD_BUTTON3 - - - PAD_BUTTON4 - - - PAD_BUTTON5 - - - PAD_BUTTON6 - - - PAD_BUTTON7 - - - PAD_BUTTON8 - - - PAD_BUTTON9 - - - PAD_BUTTON10 - - - PAD_BUTTON11 - - - PAD_BUTTON12 - - - PAD_BUTTON13 - - - PAD_BUTTON14 - - - PAD_BUTTON15 - - - - - - - = - - - ` - - - ; - - - [ - - - ] - - - \ - - - 0 - - - 1 - - - 2 - - - 3 - - - 4 - - - 5 - - - 6 - - - 7 - - - 8 - - - 9 - - - A - - - B - - - C - - - D - - - E - - - F - - - G - - - H - - - I - - - J - - - K - - - L - - - M - - - N - - - O - - - P - - - Q - - - R - - - S - - - T - - - U - - - V - - - W - - - X - - - Y - - - Z - - - Viser pejlelys for for partikler (blå) - - - Viser pejlelys for fysiske objekter (grøn) - - - Viser pejlelys for "scriptede" objekter (rød) - - - Viser pejlelys for "scriptede" objekter med berøringsfunktion (rød) - - - Viser pejlelys for lyd (gul) - - - Viser pejlelys for media (hvid) - - - Skjuler partikler - - diff --git a/indra/newview/skins/steam/xui/de/strings.xml b/indra/newview/skins/steam/xui/de/strings.xml deleted file mode 100644 index e0e272637c..0000000000 --- a/indra/newview/skins/steam/xui/de/strings.xml +++ /dev/null @@ -1,5054 +0,0 @@ - - - - - Second Life - - - Second Life - - - SECOND LIFE - - - Second Life-Grid: - - - Second Life Support-Portal - - - Hardware wird erfasst... - - - [APP_NAME] wird geladen... - - - Cache wird gelöscht... - - - Textur-Cache wird initialisiert... - - - VFS wird initialisiert... - - - Grafikinitialisierung fehlgeschlagen. Bitte aktualisieren Sie Ihren Grafiktreiber. - - - Wird wiederhergestellt... - - - Auflösung wird geändert... - - - Fullbright (Legacy) - - - Anmeldevorgang gestartet. [APP_NAME] reagiert möglicherweise nicht. Bitte warten. - - - Anmeldung erfolgt... - - - Authentifizierung - - - Account wird aktualisiert... - - - Ein früherer Anmeldeversuch ist fehlgeschlagen. Anmeldung, Versuch [NUMBER] - - - Welt wird geladen... - - - Integrierter Webbrowser wird initialisiert... - - - Multimedia wird initialisiert... - - - Schriftarten werden geladen... - - - Cache-Dateien werden überprüft (dauert 60-90 Sekunden)... - - - Antwort wird verarbeitet... - - - Welt wird initialisiert... - - - Bilder werden entpackt... - - - QuickTime wird initialisiert... - - - QuickTime nicht gefunden - Initialisierung nicht möglich. - - - QuickTime wurde initialisiert. - - - Regionsfähigkeiten anfordern... - - - Regionsfähigkeiten anfordern. Versuch [NUMBER]... - - - Region-Handshake... - - - Region-Verbindung... - - - Kleidung wird geladen... - - - Der Server hat ein ungültiges oder korruptes Zertifikate zurückgegeben. Bitte kontaktieren Sie den Grid-Administrator. - - - Ein ungültiger Hostname wurde verwendet, um auf den Server zuzugreifen. Bitte überprüfen Sie Ihre SLURL oder den Grid-Hostnamen. - - - Das vom Grid ausgegebene Zertifikate ist abgelaufen. Bitte überprüfen Sie Ihre Systemuhr oder kontaktieren Sie Ihren Grid-Administrator. - - - Das vom Server ausgegebene Zertifikat konnte nicht für SSL verwendet werden. Bitte kontaktieren Sie Ihren Grid-Administrator. - - - In der Zertifikatskette des Servers befanden sich zu viele Zertifikate. Bitte kontaktieren Sie Ihren Grid-Administrator. - - - Die Zertifikatsunterschrift des Gridservers konnte nicht bestätigt werden. Bitte kontaktieren Sie Ihren Grid-Administrator. - - - Netzwerkfehler: Verbindung konnte nicht hergestellt werden. Bitte überprüfen Sie Ihre Netzwerkverbindung. - - - Anmeldung fehlgeschlagen - - - Beenden - - - http://join.secondlife.com/index.php?sourceid=1206_steam&lang=de-DE - - - Mit dem von Ihnen verwendeten Viewer ist der Zugriff auf Second Life nicht mehr möglich. Laden Sie von den folgenden Seite einen neuen Viewer herunter: -http://secondlife.com/download - -Weitere Informationen finden Sie auf der folgenden FAQ-Seite: -http://secondlife.com/viewer-access-faq - - - Optionales Viewer-Update verfügbar: [VERSION] - - - Erforderliches Viewer-Update: [VERSION] - - - Dieser Agent ist bereits angemeldet. - - - Wir bitten um Entschuldigung! Wir konnten Sie nicht anmelden. -Stellen Sie sicher, dass Sie die richtigen Informationen eingegeben haben: - * Benutzername (wie robertschmidt12 oder warme.sonne) - * Kennwort -Stellen Sie außerdem sicher, dass die Umschaltsperre deaktiviert ist. - - - Ihr Kennwort wurde aus Sicherheitsgründen geändert. -Gehen Sie zur Seite „Mein Account“ unter http://secondlife.com/password -und beantworten Sie die Sicherheitsfrage, um Ihr Kennwort zurückzusetzen. -Wir entschuldigen uns für eventuell enstandene Unannehmlichkeiten. - - - Aufgrund von Systemänderungen müssen Sie Ihr Kennwort zurücksetzen. -Gehen Sie zur Seite „Mein Account“ unter http://secondlife.com/password -und beantworten Sie die Sicherheitsfrage, um Ihr Kennwort zurückzusetzen. -Wir entschuldigen uns für eventuell enstandene Unannehmlichkeiten. - - - Second Life ist vorübergehend wegen Wartung geschlossen. -Nur Mitarbeiter können sich anmelden. -Aktuelle Informationen finden Sie unter www.secondlife.com/status. - - - Die Anmeldung bei Second Life ist vorübergehend eingeschränkt, um sicherzustellen, dass Einwohner, die sich bereits inworld aufhalten, das bestmögliche Erlebnis haben. - -Benutzer mit kostenlosen Konten können sich während dieses Zeitraums nicht bei Second Life anmelden, damit die Kapazität Benutzern zur Verfügung steht, die ein gebührenpflichtiges Premium-Konto besitzen. - - - Der Zugriff auf Second Life ist von diesem Computer aus nicht möglich. -Wenn Sie der Ansicht sind, dass Sie diese Meldung fälschlicherweise erhalten haben, wenden Sie sich an -support@secondlife.com. - - - Ihr Konto ist erst ab -[TIME] Pacific Time wieder verfügbar. - - - Ihre Anfrage kann derzeit nicht bearbeitet werden. -Wenden Sie sich unter http://secondlife.com/support an den Second Life-Support. -Wenn Sie Ihr Kennwort nicht ändern können, rufen Sie die US-Nummer (866) 476-9763 an. - - - Nicht übereinstimmende Daten bei der Anmeldung festgestellt. -Wenden Sie sich an support@secondlife.com. - - - An Ihrem Konto werden gerade kleinere Wartungsarbeiten durchgeführt. -Ihr Konto ist erst ab -[TIME] Pacific Time wieder verfügbar. -Wenn Sie der Ansicht sind, dass Sie diese Meldung fälschlicherweise erhalten haben, wenden Sie sich an support@secondlife.com. - - - Abmeldeanforderung führte zu einem Simulatorfehler. - - - Das System meldet Sie gerade ab. -Ihr Konto ist erst ab -[TIME] Pacific Time wieder verfügbar. - - - Es kann keine gültige Sitzung erstellt werden. - - - Es kann keine Simulatorverbindung hergestellt werden. - - - Mit Ihrem Konto ist der Zugriff auf Second Life -nur zwischen [START] und [END] Pacific Time möglich. -Schauen Sie während dieses Zeitraums vorbei. -Wenn Sie der Ansicht sind, dass Sie diese Meldung fälschlicherweise erhalten haben, wenden Sie sich an support@secondlife.com. - - - Falsche Parameter. -Wenn Sie der Ansicht sind, dass Sie diese Meldung fälschlicherweise erhalten haben, wenden Sie sich an support@secondlife.com. - - - Vorname muss alphanumerisch sein. -Wenn Sie der Ansicht sind, dass Sie diese Meldung fälschlicherweise erhalten haben, wenden Sie sich an support@secondlife.com. - - - Nachname muss alphanumerisch sein. -Wenn Sie der Ansicht sind, dass Sie diese Meldung fälschlicherweise erhalten haben, wenden Sie sich an support@secondlife.com. - - - Die Region wird gerade offline geschaltet. -Warten Sie kurz und versuchen Sie dann noch einmal, sich anzumelden. - - - Agent nicht in Region. -Warten Sie kurz und versuchen Sie dann noch einmal, sich anzumelden. - - - Die Region war gerade dabei, eine andere Sitzung anzumelden. -Warten Sie kurz und versuchen Sie dann noch einmal, sich anzumelden. - - - Die Region war gerade dabei, die vorherige Sitzung abzumelden. -Warten Sie kurz und versuchen Sie dann noch einmal, sich anzumelden. - - - Die Region ist noch immer dabei, die vorherige Sitzung abzumelden. -Warten Sie kurz und versuchen Sie dann noch einmal, sich anzumelden. - - - Die Region hat soeben die letzte Sitzung abgemeldet. -Warten Sie kurz und versuchen Sie dann noch einmal, sich anzumelden. - - - Die Region hat den Abmeldevorgang gestartet. -Warten Sie kurz und versuchen Sie dann noch einmal, sich anzumelden. - - - Das System hat begonnen, Ihre letzte Sitzung abzumelden. -Warten Sie kurz und versuchen Sie dann noch einmal, sich anzumelden. - - - In dieser Region kann es zu Problemen kommen. Bitte überprüfen Sie Ihre Internetverbindung. - - - Ihr Einstellungen werden gespeichert... - - - Abmeldung erfolgt... - - - Programm wird beendet... - - - Die Verbindung zu der Region ist abgebrochen. - - - Sie wurden in eine ungültige Region geschickt. - - - Verbindungsabbruch wird getestet - - - Person - - - (namenlos) - - - Eigentümer: - - - Öffentlich - - - (Gruppe) - - - Zum Verkauf: [AMOUNT] L$ - - - Gruppenbau - - - Bauen aus - - - Gruppenbau - - - Unsicher - - - Fliegen aus - - - Gruppenskripte - - - Skripte aus - - - Land: - - - Sie können nur ein einzelnes Objekt hierher ziehen - - - - Sie können Artikel nicht in Ihrer Händler-Outbox rezzen - - - Einer oder mehrere dieser Artikel können nicht verkauft oder übertragen werden. - - - Nur Artikel direkt aus Ihrem Inventar können in Ihre Händler-Outbox gelegt werden - - - Artikel, die Sie tragen, können nicht in Ihre Händler-Outbox gelegt werden. - - - Sie können keine Visitenkarten in Ihre Händler-Outbox legen - - - Tiefe der verschachtelten Ordner überschreitet 3 - - - Anzahl von Unterordnern im obersten Ordner überschreitet 20 - - - Anzahl von Artikeln im obersten Ordner überschreitet 200 - - - Sie können einen Ordner nicht in einen seiner untergeordneten Ordner verschieben - - - Sie können einen Ordner nicht in sich selbst verschieben - - - Anklicken, um Webseite anzuzeigen - - - Anklicken, um Informationen zu diesem Standort anzuzeigen - - - Anklicken, um das Profil dieses Einwohners anzuzeigen - - - Mehr über diesen Einwohner - - - Klicken, um diesen Einwohner stummzuschalten - - - Klicken, um diesen Einwohner freizuschalten - - - Klicken, um diesem Einwohner eine IM zu schicken. - - - Klicken, um diesen Einwohner zu bezahlen - - - Klicken, um diesem Einwohner einen Teleport anzubieten. - - - Klicken, um diesem Einwohner ein Freundschaftsangebot zu schicken. - - - Anklicken, um Beschreibung der Gruppe anzuzeigen - - - Anklicken, um Beschreibung der Veranstaltung anzuzeigen - - - Anklicken, um diese Anzeige anzuzeigen - - - Anklicken, um Beschreibung der Parzelle anzuzeigen - - - Anklicken, um zu diesem Standort zu teleportieren - - - Anklicken, um Beschreibung des Objekts anzuzeigen - - - Klicken, um diese Position auf der Karte anzuzeigen - - - Anklicken, um Befehl secondlife:// auszuführen - - - - Teleportieren nach - - - Karte anzeigen für - - - Stummschalten - - - Stummschaltung aufheben - - - IM - - - Bezahlen - - - Teleportangebot an - - - Freundschaftsangebot - - - Schließen (⌘W) - - - Schließen (Strg+W) - - - Schließen - - - Wiederherstellen - - - Minimieren - - - Abnehmen - - - Andocken - - - Hilfe anzeigen - - - Suchen... - - - Nicht gefunden. - - - Laden... - - - Versionshinweise - - - http://wiki.secondlife.com/wiki/Release_Notes/ - - - Wird geladen... - - - (niemand) - - - (wartet) - - - (mehrere) - - - (keiner) - - - Avaline-Anfrufer [ORDER] - - - Kein Fehler - - - Asset-Anforderung: fehlgeschlagen - - - Asset-Anforderung: Datei existiert nicht - - - Asset-Anforderung: Asset in Datenbank nicht gefunden - - - Ende der Datei - - - Datei kann nicht geöffnet werden - - - Datei nicht gefunden - - - Zeitüberschreitung bei Dateiübertragung - - - Verbindung verloren - - - Viewer und Server sind sich nicht über Preis einig - - - Status unbekannt - - - Textur - - - Sound - - - Visitenkarte - - - Landmarke - - - Skript (veraltet) - - - Kleidung - - - Objekt - - - Notizkarte - - - Ordner - - - Hauptverzeichnis - - - LSL2 Skript - - - LSL Bytecode - - - tga-Textur - - - Körperteil - - - Foto - - - Fundbüro - - - targa-Bild - - - Papierkorb - - - jpeg-Bild - - - Animation - - - Geste - - - simstate - - - Favoriten - - - Link - - - Link zu Ordner - - - mesh - - - (Aussehen wird bearbeitet) - - - Abwesend - - - Beschäftigt - - - Ignoriert - - - Ängstlich - - - Verärgert - - - Abwesend - - - Rückwärtssalto - - - Lachkrampf - - - Grinsen - - - Kusshand - - - Gelangweilt - - - Verbeugen - - - Klatschen - - - Diener - - - Weinen - - - Tanz 1 - - - Tanz 2 - - - Tanz 3 - - - Tanz 4 - - - Tanz 5 - - - Tanz 6 - - - Tanz 7 - - - Tanz 8 - - - Verachten - - - Trinken - - - Verlegen - - - Drohen - - - Faust pumpen - - - Yogaflieger - - - Stirnrunzeln - - - Ungeduldig - - - Freudensprung - - - LMA - - - Küssen - - - Lachen - - - Posen - - - Nein (Bedauernd) - - - Nein - - - Ällabätsch - - - Eins-Zwei-Punch - - - Mund offen - - - Friede - - - Auf anderen zeigen - - - Auf mich zeigen - - - Linker Haken - - - Rechter Haken - - - SSP zählen - - - SSP Papier - - - SSP Stein - - - SSP Schere - - - Angewidert - - - Rundkick - - - Traurig - - - Salutieren - - - Rufen - - - Schulterzucken - - - Lächeln - - - Zigarette halten - - - Rauchen - - - Zigarette wegwerfen - - - Überraschung - - - Schwerthieb - - - Wutanfall - - - Zunge rausstrecken - - - Winken - - - Flüstern - - - Pfeifen - - - Zwinkern - - - Zwinkern (Hollywood) - - - Sorgenvoll - - - Ja (Erfreut) - - - Ja - - - Mehrfach - - - Wird geladen... - - - Offline - - - [PRICE] L$ für [AREA] m² - - - Nicht gefunden. - - - OK - - - Unvollständige Datei - - - HAUPTVERZEICHNIS oder VERBINDUNG nicht gefunden. - - - flüstert: - - - ruft: - - - Verbindung mit In-Welt-Voice-Chat... - - - Verbunden - - - Der aktuelle Standort unterstützt keine Voice-Kommunikation - - - Verbindung mit In-Welt-Voice-Chat getrennt - - - Sie werden nun wieder mit dem Chat in Ihrer Nähe verbunden - - - Dem Objekt „[OBJECTNAME]“, ein Objekt von „[OWNERNAME]“, in [REGIONNAME] [REGIONPOS], wurde folgende Berechtigung erteilt: [PERMISSIONS]. - - - Dem Objekt „[OBJECTNAME]“, ein Objekt von „[OWNERNAME]“, in [REGIONNAME] [REGIONPOS], wurde folgende Berechtigung verweigert: [PERMISSIONS]. - - - Wenn Sie dem Objekt Zugriff auf Ihr Konto gewähren, kann dieses außerdem: - - - Linden-Dollar (L$) von Ihnen nehmen - - - Steuerung festlegen - - - Steuerung neu zuweisen - - - Avatar animieren - - - An Avatar anhängen - - - Eigentum aufgeben und öffentlich machen - - - Mit Objekten verknüpfen und davon trennen - - - Verbindungen zu anderen Objekten hinzufügen und entfernen - - - Berechtigungen ändern - - - Kameraverfolgung - - - Kamerasteuerung - - - Sie teleportieren - - - Nicht verbunden - - - Generell - - - Moderat - - - Adult - - - Offline - - - Unbekannt - - - (unbekannt) - - - Grundstück / Vollständige Region - - - Grundbesitz/Homestead - - - Mainland/Homestead - - - Mainland / Vollständige Region - - - Alle Dateien - - - Sounds - - - Animationen - - - Bilder - - - Speichern - - - Laden - - - Targa-Bilder - - - Bitmap-Bilder - - - AVI-Filmdatei - - - XAF Anim-Datei - - - XML-Datei - - - RAW-Datei - - - Komprimierte Bilder - - - Dateien laden - - - Verzeichnis auswählen - - - Skripts - - - Wörterbücher - - - Nicht abwesend - - - Abwesend - - - Nicht beschäftigt - - - Beschäftigt - - - Form - - - Haut - - - Haare - - - Augen - - - Hemd - - - Hose - - - Schuhe - - - Socken - - - Jacke - - - Handschuhe - - - Unterhemd - - - Unterhose - - - Rock - - - Alpha - - - Tätowierung - - - Physik - - - ungültig - - - keine - - - Hemd nicht getragen - - - Hosen nicht getragen - - - Schuhe nicht getragen - - - Socken nicht getragen - - - Jacke nicht getragen - - - Handschuhe nicht getragen - - - Unterhemd nicht getragen - - - Unterhose nicht getragen - - - Rock nicht getragen - - - Alpha nicht getragen - - - Tätowierung nicht getragen - - - Physik nicht getragen - - - ungültig - - - Neue Form/Gestalt erstellen - - - Neue Haut erstellen - - - Neue Haare erstellen - - - Neue Augen erstellen - - - Neues Hemd erstellen - - - Neue Hose erstellen - - - Neue Schuhe erstellen - - - Neue Socken erstellen - - - Neue Jacke erstellen - - - Neue Handschuhe erstellen - - - Neues Unterhemd erstellen - - - Neue Unterhose erstellen - - - Neuer Rock erstellen - - - Neue Alpha erstellen - - - Neue Tätowierung erstellen - - - Neue Physik erstellen - - - ungültig - - - Neue/r/s [WEARABLE_ITEM] - - - Weiter - - - OK - - - Gruppenmitteilung - - - Gruppenmitteilungen - - - Gesendet von - - - Im Anhang: - - - Alte Mitteilungen anzeigen oder hier Auswahl treffen, um keine Mitteilungen mehr zu erhalten. - - - Anlage öffnen - - - Siehe Anhang - - - Teleport-Angebot - - - Sie haben neue Benachrichtigungen erhalten, während Sie abwesend waren. - - - Sie haben noch %d weitere Benachrichtigungen - - - Rechter Arm - - - Kopf - - - Linker Arm - - - Linkes Bein - - - Oberkörper - - - Rechtes Bein - - - Niedrig - - - Mittel - - - Hoch - - - ESC drücken, um zur Normalansicht zurückzukehren - - - Sie haben nicht das Richtige gefunden? Versuchen Sie es mit der [secondlife:///app/search/all/[SEARCH_TERM] Suche]. - - - Sie haben nicht das Richtige gefunden? Versuchen Sie es mit der [secondlife:///app/search/places/[SEARCH_TERM] Suche]. - - - Landmarke hier hin ziehen, um diese hinzuzufügen. - - - Sie haben keine Kopie dieser Textur in Ihrem Inventar. - - - Einkäufe aus dem Marktplatz erscheinen hier. Sie können diese dann zur Verwendung in Ihr Inventar ziehen. - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/ - - - http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4 - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/imports - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/learn_more - - - Jeder kann Artikel im Marktplatz verkaufen. - - - - Wenn Sie als Händler aktiv werden möchten, müssen Sie einen [[MARKETPLACE_CREATE_STORE_URL] Laden im Marktplatz erstellen]. - - - Ihre Outbox ist leer. - - - - Ziehen Sie Ordner in dien Bereich und klicken Sie auf „In Marktplatz übertragen“, um sie im [[MARKETPLACE_DASHBOARD_URL] Marktplatz] zum Verkauf anzubieten. - - - Keine Fehler - - - Fehler: Bevor Sie Artikel in den Marktplatz übertragen können, müssen Sie sich als Händler registrieren (kostenlos). - - - Fehler: Dieser Ordner ist leer. - - - Fehler: Dieser Artikel konnte nicht hochgeladen werden, da in Ihrem Händlerkonto zu viele Artikel nicht mit Produkten verknüpft sind. Um diesen Fehler zu beheben, melden Sie sich auf der Marktplatz-Website an und reduzieren Sie die Anzahl von Artikeln, die nicht mit Produkten verknüpft sind. - - - Fehler: Dieser Artikel enthält zu viele Objekte. Beheben Sie diesen Fehler, indem Sie Objekte zusammen in Behältern verpacken, um die Objektanzahl auf unter 200 zu verringern. - - - Fehler: Dieser Artikel enthält zu viele verschachtelte Ordnerebenen. Organisieren Sie ihn neu, sodass maximal drei verschachtelte Ordnerebenen vorhanden sind. - - - Fehler: Dieser Artikel kann nicht im Marktplatz verkauft werden. - - - Fehler: Bei diesem Artikel ist ein Problem aufgetreten. Versuchen Sie es später erneut. - - - Landmarken öffnen - - - - - - - - - Inhalte werden geladen... - - - Keine Inhalte - - - - - Ja - - - Nein - - - - - - - - - - - - - - - - - - - - - - - - - - Mein Inventar - - - Bibliothek - - - Texturen - - - Sounds - - - Visitenkarten - - - Landmarken - - - Skripts - - - Kleidung - - - Objekte - - - Notizkarten - - - Neuer Ordner - - - Inventar - - - Nicht-Komprimierte Bilder - - - Körperteile - - - Papierkorb - - - Fotoalbum - - - Fundbüro - - - Nicht-Komprimierte Sounds - - - Animationen - - - Gesten - - - Meine Favoriten - - - Meine Favoriten - - - Aktuelles Outfit - - - Ursprüngliche Outfits - - - Meine Outfits - - - Zubehör - - - Netze - - - Freunde - - - Alle - - - Keine Anhänge getragen - - - Anhänge (noch [COUNT] Positionen frei) - - - Kaufen - - - Kaufen für L$ - - - Stein - - - Metall - - - Glas - - - Holz - - - Fleisch - - - Plastik - - - Gummi - - - Hell - - - Umschalt-Taste - - - Strg - - - Brust - - - Schädel - - - Linke Schulter - - - Rechte Schulter - - - Linke Hand - - - Rechte Hand - - - Linker Fuß - - - Rechter Fuß - - - Wirbelsäule - - - Becken - - - Mund - - - Kinn - - - Linkes Ohr - - - Rechtes Ohr - - - Linker Augapfel - - - Rechter Augapfel - - - Nase - - - R Oberarm - - - R Unterarm - - - L Oberarm - - - L Unterarm - - - Rechte Hüfte - - - R Oberschenkel - - - R Unterschenkel - - - Linke Hüfte - - - L Oberschenkel - - - L Unterschenkel - - - Bauch - - - Linke Brust - - - Rechts - - - Hals - - - Avatar-Mitte - - - Ungültige Stelle für Anhang - - - [AGEYEARS] [AGEMONTHS] alt - - - [AGEYEARS] alt - - - [AGEMONTHS] alt - - - [AGEWEEKS] alt - - - [AGEDAYS] alt - - - Seit heute Mitglied - - - [COUNT] Jahr - - - [COUNT] Jahre - - - [COUNT] Jahre - - - [COUNT] Monat - - - [COUNT] Monate - - - [COUNT] Monate - - - [COUNT] Woche - - - [COUNT] Wochen - - - [COUNT] Wochen - - - [COUNT] Tag - - - [COUNT] Tage - - - [COUNT] Tage - - - [COUNT] Mitglied - - - [COUNT] Mitglieder - - - [COUNT] Mitglieder - - - Einwohner - - - Test - - - Charta-Mitglied - - - Linden Lab-Mitarbeiter - - - Zahlungsinfo verwendet - - - Zahlungsinfo archiviert - - - Keine Zahlungsinfo archiviert - - - Altersgeprüft - - - Nicht altersgeprüft - - - Mitte 2 - - - Oben rechts - - - Oben - - - Oben links - - - Mitte - - - Unten links - - - Unten - - - Unten rechts - - - Heruntergeladen, wird kompiliert - - - Skript wurde auf Server nicht gefunden. - - - Beim Herunterladen ist ein Problem aufgetreten - - - Unzureichende Rechte zum Herunterladen eines Skripts. - - - Unzureichende Berechtigungen für - - - Unbekannter Fehler beim Herunterladen - - - Rekompilierung - - - rekompilieren - - - Zurücksetzen - - - Zurücksetzen - - - Skript ausführen - - - Skript ausführen - - - Skript anhalten - - - Skript anhalten - - - Kompilieren erfolgreich abgeschlossen! - - - Kompilieren erfolgreich abgeschlossen, speichern... - - - Speichervorgang abgeschlossen. - - - Skript (Objekt außerhalb des Bereichs) - - - Objekt [OBJECT], Besitzer [OWNER] - - - keine - - - - (unbekannt) - - - - - [mthnum,datetime,utc]/[day,datetime,utc]/[year,datetime,utc] - - - - - Kontostand - - - Danksagung - - - Soll - - - Gesamtbetrag - - - Für Gruppe wurden keine Gruppendaten gefunden - - - parent estate - - - Mainland - - - Teen - - - jeder - - - Fehler - - - alle Grundbesitze gehören [OWNER] - - - alle Grundbesitze, die Sie besitzen - - - alle Grundbesitze, die Sie für [OWNER] verwalten - - - Zulässige Einwohner: ([ALLOWEDAGENTS], max [MAXACCESS]) - - - Zulässige Gruppen: ([ALLOWEDGROUPS], max [MAXACCESS]) - - - Parzellenskript-Speicher - - - Aufgeführte Parzellen: [PARCELS] - - - Verwendeter Speicher: [COUNT] KB von [MAX] KB; [AVAILABLE] KB verfügbar - - - Verwendeter Speicher: [COUNT] KB - - - Parzelleskript-URLs - - - Verwendete URLs: [COUNT] von [MAX]; [AVAILABLE] verfügbar - - - Verwendete URLs: [COUNT] - - - Fehler bei Informationsabruf - - - Keine Parzellen wurden ausgewählt - - - Fehler: Skriptinformationen sind nur für Ihre aktuelle Region verfügbar - - - Informationen werden abgerufen... - - - Sie sind nicht berechtigt, diese Parzelle zu untersuchen. - - - sitzt auf - - - Brust - - - Kopf - - - Linke Schulter - - - Rechte Schulter - - - Linke Hand - - - Rechte Hand - - - Linker Fuß - - - Rechter Fuß - - - Hinten - - - Becken - - - Mund - - - Kinn - - - Linkes Ohr - - - Rechtes Ohr - - - Linkes Auge - - - Rechtes Auge - - - Nase - - - Rechter Oberarm - - - Rechter Unterarm - - - Linker Oberarm - - - Linker Unterarm - - - Rechte Hüfte - - - Rechter Oberschenkel - - - Rechter Unterschenkel - - - Linke Hüfte - - - Linker Oberschenkel - - - Linker Unterschenkel - - - Bauch - - - Rechts - - - Linke Brust - - - HUD Mitte 2 - - - HUD oben rechts - - - HUD oben Mitte - - - HUD oben links - - - HUD Mitte 1 - - - HUD unten links - - - HUD unten - - - HUD unten rechts - - - Zeile [LINE], Spalte [COLUMN] - - - [COUNT] gefunden - - - [hour12,datetime,slt]:[min,datetime,slt] [ampm,datetime,slt] - - - [mthnum,datetime,slt]/[day,datetime,slt] - - - Objektinhalt - - - Neues Skript - - - Der Einwohner/Die Einwohnerin ist „beschäftigt”, d.h. er/sie möchte im Moment nicht gestört werden. Ihre Nachricht wird dem Einwohner/der Einwohnerin als IM angezeigt, und kann später beantwortet werden. - - - (Nach Namen) - - - (Einwohner) - - - (Objekt) - - - (Gruppe) - - - (Extern) - - - Für diesen Grundbesitz liegt kein Vertrag vor. - - - Für diesen Grundbesitz liegt kein Vertrag vor. Das Land auf diesem Grundbesitz wird vom Grundbesitzer und nicht von Linden Lab verkauft. Für Informationen zum Verkauf setzen Sie sich bitte mit dem Grundbesitzer in Verbindung. - - - - - - In Gruppenbesitz - - - Öffentlich - - - Lokale Einstellungen - - - Regionseinstellungen - - - Klicks: [TELEPORT] teleportieren, [MAP] Karte, [PROFILE] Profil - - - (wird nach Veröffentlichung aktualisiert) - - - Sie haben keine Auswahl oder Anzeigen erstelllt. Klicken Sie auf die „Plus"-Schaltfläche, um eine Auswahl oder Anzeige zu erstellen. - - - Der Einwohner hat keine Auswahl oder Anzeigen - - - Wird geladen... - - - Vorschau - - - Eigenschaften - - - Ein Objekt namens - - - im Besitz der Gruppe - - - im Besitz einer unbekannten Gruppe - - - im Besitz von - - - im Besitz eines unbekannten Einwohners - - - hat Ihnen folgendes übergeben - - - Sie lehnen [DESC] von <nolink>[NAME]</nolink> ab. - - - Gesamtbetrag - - - gekauft - - - bezahlte Ihnen - - - bezahlte an - - - kaufte Pass für - - - bezahlte Gebühr für Event - - - bezahlte Preis für Event - - - Kontostand - - - Danksagung - - - Soll - - - [weekday,datetime,utc] [mth,datetime,utc] [day,datetime,utc], [year,datetime,utc] - - - Inhalte - - - Erworbene Artikel - - - Abbrechen - - - Das Hochladen von [NAME] kostet [AMOUNT] L$ - - - Die Kosten betragen: [AMOUNT] L$ - - - Unbekanntes Dateiformat .%s -Gültige Formate: .wav, .tga, .bmp, .jpg, .jpeg oder .bvh - - - Ignorieren - - - Ignorieren - - - Freischalten - - - Freischalten - - - Zu meinen Landmarken hinzufügen... - - - Meine Landmarken bearbeiten... - - - ⌃ - - - ⌘ - - - ⌥ - - - ⇧ - - - Strg+ - - - Alt+ - - - Umschalt+ - - - Datei wurde gespeichert - - - Daten werden empfangen - - - Uhr - - - Uhr - - - PST - - - PDT - - - Vorwärts - - - Links - - - Rechts - - - Zurück - - - Norden - - - Süden - - - Westen - - - Osten - - - Nach oben - - - Nach unten - - - Alle Kategorien - - - Shopping - - - Land mieten - - - Immobilie mieten - - - Attraktionen - - - Neue Produkte - - - Stellenangebote - - - Gesucht - - - Dienstleistungen - - - Sonstiges - - - Keiner - - - Lindenort - - - Adult - - - Kunst & Kultur - - - Firmen - - - Bildung - - - Spielen - - - Treffpunkt - - - Anfängergerecht - - - Parks und Natur - - - Wohngebiet - - - Phase - - - Sonstige - - - Vermietung - - - Alle - - - Sie - - - : - - - , - - - ... - - - *** - - - ( - - - ) - - - . - - - ' - - - --- - - - Mehrere Medien - - - Medien Abspielen/Pausieren - - - Beim Parsen der Befehlszeile wurde ein Fehler festgestellt. -Weitere Informationen: http://wiki.secondlife.com/wiki/Client_parameters (EN) -Fehler: - - - [APP_NAME] Verwendung in Befehlszeile: - - - [APP_NAME] kann auf die erforderliche Datei nicht zugreifen. - -Grund hierfür ist, dass Sie entweder mehrere Instanzen gleichzeitig ausführen oder dass Ihr System denkt, eine Datei sei geöffnet. -Falls diese Nachricht erneut angezeigt wird, starten Sie bitte Ihren Computer neu und probieren Sie es noch einmal. -Falls der Fehler dann weiterhin auftritt, müssen Sie [APP_NAME] von Ihrem System de-installieren und erneut installieren. - - - Unbehebbarer Fehler - - - [APP_NAME] erfordert einen Prozessor mit AltiVec (G4 oder später). - - - [APP_NAME] läuft bereits. -Bitte sehen Sie in Ihrer Menüleiste nach, dort sollte ein Symbol für das Programm angezeigt werden. -Falls diese Nachricht erneut angezeigt wird, starten Sie Ihren Computer bitte neu. - - - [APP_NAME] scheint eingefroren zu sein oder ist abgestürzt. -Möchten Sie einen Absturz-Bericht einschicken? - - - Benachrichtigung - - - [APP_NAME] kann DirectX 9.0b oder höher nicht feststellen. -[APP_NAME] verwendet DirectX, um nach Hardware und/oder veralteten Treibern zu suchen, die zu Problemen mit der Stabilität, Leistung und Abstürzen führen können. Sie können [APP_NAME] auch so ausführen, wir empfehlen jedoch, dass DirectX 9.0b vorhanden ist und ausgeführt wird. - -Möchten Sie fortfahren? - - - Hinweis - - - Für Linux ist zur Zeit noch kein automatisches Aktualisieren möglich. -Bitte laden Sie die aktuellste Version von www.secondlife.com herunter. - - - RegisterClass fehlgeschlagen - - - Fehler - - - Vollbildschirm mit [WIDTH] x [HEIGHT] kann nicht ausgeführt werden. -Ausführung erfolgt in Fenster. - - - Fehler beim Herunterfahren während Fenster geschlossen wurde (DestroyWindow() fehlgeschlagen) - - - Fehler beim Herunterfahren - - - Kann keinen Kontext für GL-Gerät erstellen - - - Passendes Pixelformat wurde nicht gefunden - - - Beschreibung für Pixelformat nicht verfügbar - - - Um [APP_NAME] auszuführen, ist True Color (32-bit) erforderlich. -Klicken Sie öffnen Sie auf Ihrem Computer die Einstellungen für die Anzeige und stellen Sie den Bildschirm auf 32-bit Farbe ein. - - - [APP_NAME] kann nicht ausgeführt werden, da kein 8-Bit-Alpha-Kanal verfügbar ist. Dies geschieht normalerweise bei Problemen mit dem Treiber der Video-Karte. -Bitte vergewissern Sie sich, dass Sie die aktuellsten Treiber für Ihre Videokarte installiert haben. -Vergewissern Sie sich außerdem, dass Ihr Bildschirm auf True Color (32-Bit) eingestellt ist (Systemsteuerung > Anzeige > Einstellungen). -Falls diese Meldung weiterhin angezeigt wird, wenden Sie sich bitte an [SUPPORT_SITE]. - - - Pixel-Format kann nicht eingestellt werden. - - - Kann keinen Kontext für GL-Gerät erstellen - - - Kann keinen Kontext für GL-Gerät aktivieren - - - [APP_NAME] kann nicht ausgeführt werden, da die Treiber Ihrer Videokarte entweder nicht richtig installiert oder veraltet sind, oder die entsprechende Hardware nicht unterstützt wird. Bitte vergewissern Sie sich, dass Sie die aktuellsten Treiber für die Videokarte installiert haben. Falls Sie die aktuellsten Treiber bereits installiert haben, installieren Sie diese bitte erneut. - -Falls diese Meldung weiterhin angezeigt wird, wenden Sie sich bitte an [SUPPORT_SITE]. - - - Bartschatten - - - Ganz weiß - - - Anime-Augen - - - Gewölbt - - - Armlänge - - - Angewachsen - - - Angewachsene Ohrläppchen - - - Nackenfransen - - - Tränensäcke - - - Pony - - - Knopfaugen - - - Bauchgröße - - - Groß - - - Großer Hintern - - - Volumen: Hinten - - - Volumen: Vorne - - - Volumen: Oben - - - Groß - - - Große Brustmuskeln - - - Große Stacheln - - - Schwarz - - - Blond - - - Blondes Haar - - - Rouge - - - Rougefarbe - - - Rouge Deckkraft - - - Körperkonturen - - - Körperfett - - - Sommersprossen - - - breit - - - Körperbreite - - - schmal - - - o-beinig - - - Brust, Straffheit - - - Dekolleté - - - Brustgröße - - - Rückenbreite - - - Breit - - - Brauengröße - - - Glubschaugen - - - Hervortretend - - - Knollennase - - - Knollennase - - - Brust – Masse - - - Brust – Glättung - - - Brust – Schwerkraft - - - Brust – Luftwiderstand - - - Max. Effekt - - - Federn - - - Verstärkung - - - Dämpfung - - - Max. Effekt - - - Federn - - - Verstärkung - - - Dämpfung - - - Max. Effekt - - - Federn - - - Verstärkung - - - Dämpfung - - - Bauch – Masse - - - Bauch – Glättung - - - Bauch – Schwerkraft - - - Bauch – Luftwiderstand - - - Max. Effekt - - - Federn - - - Verstärkung - - - Dämpfung - - - Po – Masse - - - Po – Glättung - - - Po – Schwerkraft - - - Po – Luftwiderstand - - - Max. Effekt - - - Federn - - - Verstärkung - - - Dämpfung - - - Max. Effekt - - - Federn - - - Verstärkung - - - Dämpfung - - - Buschige Augenbrauen - - - Buschiges Haar - - - Hintern, Größe - - - Po – Schwerkraft - - - Tournürenrock - - - Ohne - - - Mit - - - Chaplin - - - Wangenknochen - - - Brustgröße - - - Kinnwinkel - - - Kinnspalte - - - Schifferfräse - - - Kinnlänge - - - Kinn ausgeprägt - - - Kinn zurück - - - Kinn nach vorne - - - Kinn-Hals - - - Transparent - - - Spalte - - - Eng stehende Augen - - - Geschlossen - - - Hinten geschlossen - - - Vorne geschlossen - - - Links geschlossen - - - Rechts geschlossen - - - Klein - - - Kragen hinten - - - Kragen vorne - - - Nach unten - - - Nach oben - - - Schlupflid - - - Krumme Nase - - - Hosenaufschlag - - - Dunkel - - - Dunkelgrün - - - Dunkler - - - Tief - - - Standardabsätze - - - Dicht - - - Doppelkinn - - - Nach unten - - - Groß - - - Ohrenwinkel - - - Ohrengröße - - - Ohrenspitzen - - - Eierkopf - - - Augenränder - - - Augenfarbe - - - Augentiefe - - - Helligkeit - - - Öffnung - - - Symmetrie - - - Augengröße - - - Augenstand - - - Brauenbogen - - - Brauendichte - - - Brauenhöhe - - - Brauenenden - - - Brauengröße - - - Wimpernlänge - - - Eyeliner - - - Farbe des Eyeliners - - - Glubschaugen - - - Gesichtsverzerrung - - - Gesichtskonturen - - - Weit auseinander - - - Volle Lippen - - - weiblich - - - Ohne Finger - - - Finger - - - Ausgestellt - - - Flach - - - Flacher Hintern - - - Flacher Kopf - - - Flache Spitze - - - Fußgröße - - - Stirnwinkel - - - Stirn ausgeprägt - - - Sommersprossen - - - Fransen, vorne - - - Hinten volles Haar - - - Starker Eyeliner - - - Vorne volles Haar - - - Seitlich volles Haar - - - Volle Seiten - - - Glänzend - - - Handschuhfinger - - - Handschuhlänge - - - Haare - - - Haare: Hinten - - - Haare: Vorne - - - Haare: Seiten - - - Haartolle - - - Haardicke - - - Haardicke - - - Haarneigung - - - Nach links - - - Nach rechts - - - Haare: Volumen - - - Handgröße - - - Zwirbelbart - - - Kopflänge - - - Kopfform - - - Kopfgröße - - - Kopfstreckung - - - Absatzhöhe - - - Absatzform - - - Größe - - - Hoch - - - Hohe Absätze - - - Hoch - - - Hohe Plattformsohlen - - - Hoch und eng - - - Höhere - - - Länge der Hüfte - - - Breite der Hüfte - - - In - - - Farbe Innenseite - - - Deckkraft: innen - - - Ecke: Nasenseite - - - Innenlid - - - Innenlid - - - Jackenlänge - - - Jackenfalten - - - Kinnansatz - - - Kinnposition - - - Kinnform - - - Zusammen - - - Hängebacken - - - Kniewinkel - - - X-beinig - - - Groß - - - Große Hände - - - Linksscheitel - - - Beinlänge - - - Beinmuskeln - - - Weniger - - - Weniger Speck - - - Weniger - - - Weniger - - - Weniger - - - Weniger - - - Weniger - - - Weniger - - - Weniger - - - Weniger - - - Weniger - - - Weniger - - - Weniger - - - Weniger - - - Weniger - - - Heller - - - Amorbogen - - - Tiefe: Amorbogen - - - Fülle - - - Pinkton - - - Lippenproportionen - - - Lippendicke - - - Mundbreite - - - Lipgloss - - - Lippenstift - - - Farbe - - - Lang - - - Langer Kopf - - - Lange Hüften - - - Lange Beine - - - Langer Hals - - - Lange Zöpfe - - - Langer Pferdeschwanz - - - Langer Oberkörper - - - Lange Arme - - - Weite Hosen - - - Weites Hemd - - - Weite Ärmel - - - Fettpölsterchen - - - Niedrig - - - Niedrig - - - Niedrig - - - Niedrig - - - Weit - - - Absenken - - - Brücke, Unterer Teil - - - Wangen, unterer Bereich - - - Männlich - - - Mittelscheitel - - - Mehr - - - Mehr - - - Mehr Speck - - - Mehr - - - Mehr - - - Mehr - - - Voller - - - Mehr - - - Mehr - - - Mehr - - - Größer - - - Mehr - - - Mehr - - - Mehr - - - Runder - - - Mehr - - - Flach - - - Eckiger - - - Mehr - - - Steil - - - Mehr - - - Mehr - - - Schnauzer - - - Mundwinkel - - - Mundposition - - - Irokese - - - Muskulös - - - Koteletten - - - Nagellack - - - Farbe - - - Schmal - - - Wenig - - - Wenig - - - Schmale Lippen - - - Natürlich - - - Halslänge - - - Halsdicke - - - Kein Rouge - - - Kein Eyeliner - - - Kein Lidschatten - - - Kein Lipgloss - - - Kein Lippenstift - - - Kein Scheitel - - - Kein Nagellack - - - Nicht rot - - - Keine Stachel - - - Kein Weiß - - - Keine Falten - - - Normal unten - - - Normal oben - - - Links - - - Rechts - - - Größe - - - Dicke - - - Nasenspitze - - - Nasenspitze - - - Nasenbreite - - - Teilung - - - Größe - - - Deckend - - - Öffnen - - - Hinten offen - - - Vorne offen - - - Links offen - - - Rechts offen - - - Orange - - - Aus - - - Farbe: Oben - - - Deckkraft: Oben - - - Äußerer Augenwinkel - - - Lidschatten: Oben - - - Lidschatten: Oben - - - Überbiss - - - Ausbeulung - - - Lackierte Nägel - - - Blass - - - Schritt - - - Passform - - - Hosenlänge - - - Hüfte - - - Falten - - - Scheitel - - - Pony scheiteln - - - Brustmuskel - - - Pigmentierung - - - Zöpfe - - - Pink - - - Mehr Pink - - - Höhe - - - Breite - - - Spitz - - - Pfennigabsätze - - - Pferdeschwanz - - - Weit ausgestellt - - - Linkes Auge größer - - - Rechtes Auge größer - - - Geschwollen - - - Geschwollene Lider - - - Regenbogenfarben - - - Rote Haare - - - Normal - - - Scheitel rechts - - - Rosiger Teint - - - Rund - - - Röte - - - Rötlich - - - Zerzauste Haare - - - Hüftspeck - - - Dürres Bein - - - Auseinander - - - Flach - - - Hinterkopf rasiert - - - Gesicht verzerren - - - Vorne rasiert - - - Links - - - Rechts - - - Hinterkopf rasiert - - - Vorne rasiert - - - Nach links - - - Mund verschieben - - - Nach rechts - - - Hemdlänge - - - Passform - - - Falten - - - Schuhart - - - Klein - - - Kurze Arme - - - Kurze Beine - - - Kurzer Hals - - - Kurze Zöpfe - - - Kurzer Pferdeschwanz - - - Kurze Koteletten - - - Kurzer Oberkörper - - - Kurze Hüften - - - Schultern - - - Seitliche Fransen - - - Koteletten - - - Seitliches Haar - - - Lang - - - Kurz - - - Dünner Hals - - - Passform - - - Rocklänge - - - Fliehende Stirn - - - Ärmellänge - - - Passform Ärmel - - - Schlitz: Hinten - - - Schlitz: Vorne - - - Schlitz: Links - - - Schlitz: Rechts - - - Klein - - - Kleine Hände - - - Klein - - - Glätten - - - Glattes Haar - - - Strumpflänge - - - Unterlippenbart - - - Wenig - - - Stachelhaare - - - Rechteck - - - Eckig - - - Gestaucht - - - Gestreckt - - - Eingefallen - - - Trichterbrust - - - Eingesunkene Augen - - - Nach hinten - - - Nach vorne - - - Groß - - - Ansatzbreite hinten - - - Ansatzbreite vorne - - - Dicke Absätze - - - Dicker Hals - - - Dick - - - Dünn - - - Dünne Augenbrauen - - - Dünne Lippen - - - Dünne Nase - - - Straffes Kinn - - - Eng - - - Enge Hosen - - - Enges Hemd - - - Enger Rock - - - Enge Ärmel - - - Spitze - - - Dicke - - - Länge des Oberkörpers - - - Muskeln - - - Dürr - - - Frei - - - Straffes Lid - - - Unterbiss - - - Unnatürlich - - - Brücke, oberer Teil - - - Obere Wangen - - - Obere Kinnspalte - - - Obere Lidfalte - - - Stupsnase - - - Sehr rot - - - Bund - - - Gut genährt - - - Weiße Haare - - - Breit - - - Breit - - - Breit - - - Breit - - - Wild - - - Falten - - - Zu meinen Landmarken hinzufügen - - - Meine Landmarken bearbeiten - - - Weitere Informationen über die aktuelle Position - - - Mein Reiseverlauf - - - Dieses Land kaufen - - - Voice hier nicht möglich - - - Fliegen ist unzulässig - - - Kein Stoßen - - - Bauen/Fallen lassen von Objekten ist verboten - - - Skripte sind unzulässig - - - Gesundheit - - - Adult-Region - - - Moderate Region - - - Generelle Region - - - Avatare sichtbar; Chat außerhalb dieser Parzelle gestattet - - - Bewegliche Objekte verhalten sich in dieser Region u. U. erst dann korrekt, wenn die Region neu geformt wird. - - - Dynamisches Pathfinding ist in dieser Region nicht aktiviert. - - - [APP_NAME] Aktualisierung - - - [APP_NAME] wird aktualisiert... - - - [APP_NAME] wird installiert... - - - Ihr [APP_NAME]-Viewer wird aktualisiert. Dies kann einen Moment dauern. Wir bitten um Ihr Verständnis. - - - Aktualisierung wird heruntergeladen... - - - Aktualisierung wird heruntergeladen - - - Herunterladen ist fehlgeschlagen - - - Beim Aktualisieren von [APP_NAME] ist ein Fehler aufgetreten. Bitte laden Sie die aktuellste Version von www.secondlife.com herunter. - - - Aktualisierung konnte nicht installiert werden - - - Viewer konnte nicht gestartet werden - - - [APP_NAME]: Zuviele Objekte auf einmal von [FROM_NAME]. Automaitsche Vorschau ist für [TIME] Sekunden nicht verfügbar. - - - [APP_NAME]: Zuviele Objekte auf einmal. Automaitsche Vorschau ist für [TIME] Sekunden nicht verfügbar. - - - -- Instant-Message-Protokoll aktiviert -- - - - [NAME] tippt... - - - (Nicht benannt) - - - (Moderiert: Stimmen in der Standardeinstellung stummgeschaltet) - - - Für diese Verbindung ist kein Text-Chat verfügbar. - - - Ihr Text-Chat wurde von einem Gruppenmoderator deaktiviert. - - - Für Instant Message hier klicken. - - - An - - - (Moderator) - - - (Gespeichert am [LONG_TIMESTAMP]) - - - Wenn Sie diese Meldung sehen, müssen Sie unter „Einstellungen“ > „Privatsphäre“ die Option „Nur IMs und Anrufe von Freunden oder Gruppen durchstellen“ deaktivieren. - - - Ihr Anruf wurde entgegengenommen - - - Sie haben einen Voice-Anruf begonnen - - - Sie sind dem Gespräch beigetreten - - - [NAME] hat einen Voice-Anruf begonnen - - - Verbindung wird hergestellt... - - - Verbunden. Klicken Sie auf Anruf beenden, um die Verbindung zu trennen - - - Anruf wurde beendet - - - Wird verbunden... - - - Ad-hoc-Konferenz - - - Konferenz mit [AGENT_NAME] - - - Inventarobjekt angeboten - - - Objekte aus dem Inventar hier her ziehen - - - (IM-Session nicht vorhanden) - - - Sie sind der einzige Benutzer in dieser Sitzung. - - - [NAME] ist offline. - - - Klicken Sie auf [BUTTON NAME], um eine Verbindung zu diesem Voice-Chat herzustellen. - - - Sie haben diesen Einwohner ignoriert. Wenn Sie eine Nachricht senden, wird dieser freigeschaltet. - - - Fehler bei Anfrage, bitte versuchen Sie es später. - - - Fehler bei Anfrage, bitte versuchen Sie es später. - - - Sie sind dazu nicht berechtigt. - - - Die Sitzung ist abgelaufen - - - Sie besitzen diese Fähigkeit nicht. - - - Sie besitzen diese Fähigkeit nicht. - - - Sie sind kein Sitzungsmoderator. - - - Ein Gruppenmoderator hat Ihren Text-Chat deaktiviert. - - - Ein Gruppenmoderator hat Ihren Text-Chat deaktiviert. - - - Es konnten keine Benutzer zur Chat-Sitzung mit [RECIPIENT] hinzugefügt werden. - - - Ihre Nachricht konnte nicht an die Chat-Sitzung mit [RECIPIENT] gesendet werden. - - - Ihre Nachricht konnte nicht an die Chat-Sitzung mit [RECIPIENT] gesendet werden. - - - Fehler während Moderation. - - - Sie wurden von der Gruppe ausgeschlossen. - - - Sie wurden von der Gruppe ausgeschlossen. - - - Sie haben nicht mehr die Berechtigung an der Chat-Sitzung teilzunehmen. - - - [SOURCES] hat etwas Neues gesagt - - - [SOURCES] haben etwas Neues gesagt - - - Die Initialisierung der Sitzung ist fehlgeschlagen - - - Position für Zuhause festgelegt. - - - http://secondlife.com/landing/voicemorphing - - - [NAME] hat Ihnen [REASON] [AMOUNT] L$ bezahlt. - - - [NAME] hat Ihnen [AMOUNT] L$ bezahlt. - - - Sie haben [REASON] [AMOUNT] L$ an [NAME] bezahlt. - - - Sie haben [AMOUNT] L$ bezahlt. - - - Sie haben [AMOUNT] L$ an [NAME] bezahlt. - - - Sie haben [REASON] [AMOUNT] L$ bezahlt. - - - Sie haben [NAME] [AMOUNT] L$ [REASON] nicht bezahlt. - - - Sie haben [AMOUNT] L$ nicht bezahlt. - - - Sie haben [NAME] [AMOUNT] L$ nicht bezahlt. - - - Sie haben [AMOUNT] L$ [REASON] nicht bezahlt. - - - für [ITEM] - - - für eine Landparzelle - - - für einen Pass - - - für die Landübertragung - - - für die Gründung einer Gruppe - - - für den Beitritt zur Gruppe - - - fürs Hochladen - - - um eine Anzeige aufzugeben - - - [AMOUNT] L$ werden bezahlt - - - Kosten für Hochladen [AMOUNT] L$ - - - Kosten: [AMOUNT] L$ - - - Ausgewähltes Land wird für [AMOUNT] L$ gekauft. - - - Dieses Objekt kostet [AMOUNT] L$ - - - Jeder - - - Offiziere - - - Eigentümer - - - Online - - - Hochladen... - -Missbrauchsbericht - - - Neue Form/Gestalt - - - Neue Haut - - - Neues Haar - - - Neue Augen - - - Neues Hemd - - - Neue Hose - - - Neue Schuhe - - - Neue Socken - - - Neue Jacke - - - Neue Handschuhe - - - Neues Unterhemd - - - Neue Unterhose - - - Neuer Rock - - - Neues Alpha - - - Neue Tätowierung - - - Neue Physik - - - Ungültiges Objekt - - - Neue Geste - - - Neues Skript - - - Neue Notiz - - - Neuer Ordner - - - Inhalt - - - Gesten - - - Männliche Gesten - - - Weibliche Gesten - - - Andere Gesten - - - Sprachgesten - - - Häufig verwendete Gesten - - - Männlich - Excuse me - - - Männlich - Get lost - - - Männlich - Kusshand - - - Männlich - Buh - - - Männlich - Gelangweilt - - - Männlich - Hey - - - Männlich - Lachen - - - Männlich - Angewidert - - - Männlich - Achselzucken - - - Männlich - Zunge herausstrecken - - - Männlich - Wow - - - Weiblich - Kichern - - - Weiblich - Weinen - - - Weiblich - Verlegen - - - Weiblich - Räuspern - - - Weiblich - Get lost - - - Weiblich - Kusshand - - - Weiblich - Buh - - - Weiblich - Gelangweilt - - - Weiblich - Hey - - - Weiblich - Hey Süße(r) - - - Weiblich - Lachen - - - Weiblich - Looking good - - - Weiblich - Over here - - - Weiblich - Please - - - Weiblich - Angewidert - - - Weiblich - Achselzucken - - - Weiblich - Zunge herausstrecken - - - Weiblich - Wow - - - /verbeugen - - - /klatschen - - - /zählen - - - /löschen - - - /lmaa - - - /Muskel - - - /nein - - - /nein! - - - /Papier - - - /auf mich zeigen - - - /auf dich zeigen - - - /Stein - - - /Schere - - - /rauchen - - - /dehnen - - - /pfeifen - - - /ja - - - /ja! - - - afk - - - Tanzen1 - - - Tanzen2 - - - Tanzen3 - - - Tanzen4 - - - Tanzen5 - - - Tanzen6 - - - Tanzen7 - - - Tanzen8 - - - [mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt] - - - Keine/Keiner - - - Bilder, die größer sind als [WIDTH]*[HEIGHT] können nicht geladen werden - - - - Trotz all unserer Bemühungen ist ein unerwarteter Fehler aufgetreten. - - Bitte überprüfen Sie status.secondlifegrid.net, um herauszufinden, ob ein Problem besteht. - Falls Sie weiterhin Problem haben, überprüfen Sie bitte Ihre Netzwerk- und Firewalleinstellungen. - - - Sonntag:Montag:Dienstag:Mittwoch:Donnerstag:Freitag:Samstag - - - So:Mo:Di:Mi:Do:Fr:Sa - - - Januar:Februar:März:April:Mai:Juni:Juli:August:September:Oktober:November:Dezember - - - Jan:Feb:Mär:Apr:Mai:Jun:Jul:Aug:Sep:Okt:Nov:Dez - - - [MDAY] - - - Uhr - - - Uhr - - - [AMOUNT] US$ - - - Mitgliedschaft - - - Rollen - - - Gruppenidentität - - - Parzellenverwaltung - - - Parzellenidentität - - - Parzelleneinstellungen - - - Parzellenfähigkeiten - - - Parzellenzugang - - - Parzelleninhalt - - - Objektmanagement - - - Kontoführung - - - Mitteilungen - - - Chat - - - Ausgewählte Objekte löschen? - - - Ausgewähltes Objekt löschen? - - - Keine Objekte in diesem Outfit - - - Wählen Sie über die Einstellung „ExternalEditor“ einen Editor aus - - - Angegebener externer Editor nicht gefunden. -Setzen Sie den Editorpfad in Anführungszeichen -(z. B. "/pfad/editor" "%s"). - - - Fehler beim Parsen des externen Editorbefehls. - - - Externer Editor konnte nicht ausgeführt werden. - - - Übersetzung fehlgeschlagen: [REASON] - - - Fehler beim Parsen der Übersetzungsantwort. - - - Esc - - - Space - - - Enter - - - Tab - - - Ins - - - Del - - - Backsp - - - Shift - - - Ctrl - - - Alt - - - CapsLock - - - Zuhause - - - End - - - PgUp - - - PgDn - - - F1 - - - F2 - - - F3 - - - F4 - - - F5 - - - F6 - - - F7 - - - F8 - - - F9 - - - F10 - - - F11 - - - F12 - - - Addieren - - - Subtrahieren - - - Multiplizieren - - - Dividieren - - - PAD_DIVIDE - - - PAD_LEFT - - - PAD_RIGHT - - - PAD_DOWN - - - PAD_UP - - - PAD_HOME - - - PAD_END - - - PAD_PGUP - - - PAD_PGDN - - - PAD_CENTER - - - PAD_INS - - - PAD_DEL - - - PAD_Enter - - - PAD_BUTTON0 - - - PAD_BUTTON1 - - - PAD_BUTTON2 - - - PAD_BUTTON3 - - - PAD_BUTTON4 - - - PAD_BUTTON5 - - - PAD_BUTTON6 - - - PAD_BUTTON7 - - - PAD_BUTTON8 - - - PAD_BUTTON9 - - - PAD_BUTTON10 - - - PAD_BUTTON11 - - - PAD_BUTTON12 - - - PAD_BUTTON13 - - - PAD_BUTTON14 - - - PAD_BUTTON15 - - - - - - - = - - - ` - - - ; - - - [ - - - ] - - - \ - - - 0 - - - 1 - - - 2 - - - 3 - - - 4 - - - 5 - - - 6 - - - 7 - - - 8 - - - 9 - - - A - - - B - - - C - - - D - - - E - - - F - - - G - - - H - - - I - - - J - - - K - - - L - - - M - - - N - - - O - - - P - - - Q - - - R - - - S - - - T - - - U - - - V - - - W - - - X - - - Y - - - Z - - - Partikel-Beacons werden angezeigt (blau) - - - Beacons für physische Objekte werden angezeigt (grün) - - - Beacons für Skriptobjekte werden angezeigt (rot) - - - Beacons für Skriptobjekte mit Berührungsfunktion werden angezeigt (rot) - - - Sound-Beacons werden angezeigt (gelb) - - - Medien-Beacons werden angezeigt (weiß) - - - Partikel werden ausgeblendet - - - Landinformationen - - - Aussehen - - - Avatar - - - Bauen - - - Chat - - - Kompass - - - Ziele - - - Gesten - - - Infos - - - Inventar - - - Karte - - - Marktplatz - - - Minikarte - - - Gehen / Rennen / Fliegen - - - Händler-Outbox - - - Leute - - - Auswahlen - - - Orte - - - Einstellungen - - - Profil - - - Suchen - - - Foto - - - Sprechen - - - Kamerasteuerungen - - - Voice-Einstellungen - - - Informationen zu dem von Ihnen besuchten Land - - - Avatar ändern - - - Kompletten Avatar auswählen - - - Objekte bauen und Terrain umformen - - - Mit Leuten in der Nähe chatten - - - Kompass - - - Ziele von Interesse - - - Gesten für Ihren Avatar - - - Wie führe ich gängige Aufgaben aus? - - - Ihr Eigentum anzeigen und benutzen - - - Weltkarte - - - Einkaufen gehen - - - Leute in der Nähe anzeigen - - - Ihren Avatar bewegen - - - Artikel zum Verkauf in den Marktplatz übertragen - - - Freunde, Gruppen und Leute in der Nähe - - - Orte, die in Ihrem Profil als Favoriten angezeigt werden sollen - - - Von Ihnen gespeicherte Orte - - - Einstellungen - - - Ihr Profil bearbeiten oder anzeigen - - - Orte, Veranstaltungen, Leute finden - - - Foto aufnehmen - - - Über Ihr Mikrofon mit Leuten in der Nähe sprechen - - - Kamerawinkel ändern - - - Lautstärkeregler für Anrufe und Leute in Ihrer Nähe in SL - - - gegenwärtig in der unteren Symbolleiste - - - gegenwärtig in der linken Symbolleiste - - - gegenwärtig in der rechten Symbolleiste - - - % zurückbehalten - - - Details - - - Bessere Details - - - Oberfläche - - - Fest - - - Wickeln - - - Vorschau - - - Normal - - - http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer - - - Keine - - - Wirkt sich auf Navmesh aus - - - Figur - - - (mehrere) - - - Sehr niedrig - - - Niedrig - - - Mittel - - - Hoch - - - Sehr hoch - - - Der Einwohner kann diese Region nicht besuchen. - - - [Benutzer] - - diff --git a/indra/newview/skins/steam/xui/en/strings.xml b/indra/newview/skins/steam/xui/en/strings.xml deleted file mode 100644 index 4f4b2b2125..0000000000 --- a/indra/newview/skins/steam/xui/en/strings.xml +++ /dev/null @@ -1,3910 +0,0 @@ - - - - - - Second Life - Second Life - SECOND LIFE - Second Life Grid - Second Life Support Portal - - - Detecting hardware... - Loading [APP_NAME]... - Clearing cache... - Initializing texture cache... - Initializing VFS... - Graphics initialization failed. Please update your graphics driver! - - - Restoring... - Changing resolution... - - - Fullbright (legacy) - - - Logging in. [APP_NAME] may appear frozen. Please wait. - Logging in... - Authenticating - Performing account maintenance... - Previous login attempt failed. Logging in, attempt [NUMBER] - Loading world... - Initializing embedded web browser... - Initializing multimedia... - Loading fonts... - Verifying cache files (can take 60-90 seconds)... - Processing response... - Initializing world... - Decoding images... - Initializing QuickTime... - QuickTime not found - unable to initialize. - QuickTime initialized successfully. - Requesting region capabilities... - Requesting region capabilities, attempt [NUMBER]... - Waiting for region handshake... - Connecting to region... - Downloading clothing... - The server returned an invalid or corrupt certificate. Please contact the Grid administrator. - An invalid hostname was used to access the server, please check your SLURL or Grid hostname. - The certificate returned by the Grid appears to be expired. Please check your system clock, or contact your Grid administrator. - The certificate returned by the server could not be used for SSL. Please contact your Grid administrator. - Too many certificates were in the servers Certificate chain. Please contact your Grid administrator. - The certificate signature returned by the Grid server could not be verified. Please contact your Grid administrator. - - Network error: Could not establish connection, please check your network connection. - Login failed. - Quit - http://join.secondlife.com/?sourceid=1206_steam - - -The viewer you are using can no longer access Second Life. Please visit the following page to download a new viewer: -http://secondlife.com/download - -For more information, see our FAQ below: -http://secondlife.com/viewer-access-faq - Optional viewer update available: [VERSION] - Required viewer update: [VERSION] - This agent is already logged in. - - Sorry! We couldn't log you in. -Please check to make sure you entered the right - * Username (like bobsmith12 or steller.sunshine) - * Password -Also, please make sure your Caps Lock key is off. - As a security precaution your password has been changed. -Please go to your account page at http://secondlife.com/password -and answer the security question to reset your password. -We are very sorry for the inconvenience. - We made some changes to our system and you will need to reset your password. -Please go to your account page at http://secondlife.com/password -and answer the security question to reset your password. -We are very sorry for the inconvenience. - Second Life is temporarily closed for maintenance. -Logins are currently restricted to employees only. -Check www.secondlife.com/status for updates. - Second Life logins are temporarily restricted in order to make sure that those in-world have the best possible experience. - -People with free accounts will not be able to access Second Life during this time, to make room for those who have paid for Second Life. - Second Life cannot be accessed from this computer. -If you feel this is an error, please contact -support@secondlife.com. - Your account is not accessible until -[TIME] Pacific Time. - We are unable to complete your request at this time. -Please contact Second Life support for assistance at http://secondlife.com/support. -If you are unable to change your password, please call (866) 476-9763. - Data inconsistency found during login. -Please contact support@secondlife.com. - Your account is undergoing minor maintenance. -Your account is not accessible until -[TIME] Pacific Time. -If you feel this is an error, please contact support@secondlife.com. - Request for logout responded with a fault from simulator. - The system is logging you out right now. -Your Account will not be available until -[TIME] Pacific Time. - Unable to create valid session. - Unable to connect to a simulator. - Your account can only access Second Life -between [START] and [END] Pacific Time. -Please come back during those hours. -If you feel this is an error, please contact support@secondlife.com. - Incorrect parameters. -If you feel this is an error, please contact support@secondlife.com. - First name parameter must be alphanumeric. -If you feel this is an error, please contact support@secondlife.com. - Last name parameter must be alphanumeric. -If you feel this is an error, please contact support@secondlife.com. - Region is going offline. -Please try logging in again in a minute. - Agent not in region. -Please try logging in again in a minute. - The region was logging in another session. -Please try logging in again in a minute. - The region was logging out the previous session. -Please try logging in again in a minute. - The region is still logging out the previous session. -Please try logging in again in a minute. - Region has logged out last session. -Please try logging in again in a minute. - Region has begun the logout process. -Please try logging in again in a minute. - The system has begun logging out your last session. -Please try logging in again in a minute. - - - - This region may be experiencing trouble. Please check your connection to the Internet. - Saving your settings... - Logging out... - Shutting down... - You have been disconnected from the region you were in. - You were sent to an invalid region. - Testing viewer disconnect - - - Person - (no name) - Owner: - Public - (Group) - For Sale: L$[AMOUNT] - Group Build - No Build - Group Build - Not Safe - No Fly - Group Scripts - No Scripts - Land: - Only a single item can be dragged here - - - You can not rez items in your merchant outbox - One or more of these objects cannot be sold or transferred. - Your merchant outbox can only accept items directly from your inventory - You can not put items you are wearing into your merchant outbox - You can not put calling cards into your merchant outbox - Depth of nested folders exceeds 3 - Subfolder count in top-level folder exceeds 20 - Item count in top-level folder exceeds 200 - - You can't move a folder into its child - You can't move a folder into itself - - - Click to view this web page - Click to view this location's information - Click to view this Resident's profile - Learn more about this Resident - Click to mute this Resident - Click to unmute this Resident - Click to IM this Resident - Click to Pay this Resident - Click to offer a teleport request to this Resident - Click to send a friend request to this Resident - Click to view this group's description - Click to view this event's description - Click to view this classified - Click to view this parcel's description - Click to teleport to this location - Click to view this object's description - Click to view this location on a map - Click to run the secondlife:// command - - - - Teleport to - Show Map for - - - Mute - Unmute - IM - Pay - Offer Teleport to - Friend Request - - - Close (⌘W) - Close (Ctrl+W) - Close - Restore - Minimize - Tear Off - Dock - Show Help - - - Searching... - None found. - - - Retrieving... - - Release Notes - - http://wiki.secondlife.com/wiki/Release_Notes/ - - - Loading... - - - - - (nobody) - - - (waiting) - - - (multiple) - - - (none) - - Avaline Caller [ORDER] - - - No error - Asset request: failed - Asset request: non-existent file - Asset request: asset not found in database - End of file - Cannot open file - File not found - File transfer timeout - Circuit gone - Viewer and server do not agree on price - Unknown status - - - - texture - sound - calling card - landmark - legacy script - clothing - object - notecard - folder - root - LSL2 script - LSL bytecode - tga texture - body part - snapshot - Lost and Found - targa image - Trash - jpeg image - animation - gesture - simstate - favorite - link - folder link - mesh - - - (Editing Appearance) - Away - Busy - Blocked - - - Afraid - Angry - Away - Backflip - Belly Laugh - BigSmile - Blow Kiss - Bored - Bow - Clap - Court Bow - Cry - Dance 1 - Dance 2 - Dance 3 - Dance 4 - Dance 5 - Dance 6 - Dance 7 - Dance 8 - Disdain - Drink - Embarrassed - Finger Wag - Fist Pump - Floating Yoga - Frown - Impatient - Jump For Joy - Kiss My Butt - Kiss - Laugh - Muscle Beach - No (Unhappy) - No - Nya-nya-nya - One-Two Punch - Open Mouth - Peace - Point at Other - Point at Self - Punch Left - Punch Right - RPS count - RPS paper - RPS rock - RPS scissors - Repulsed - Roundhouse Kick - Sad - Salute - Shout - Shrug - Smile - Smoke Idle - Smoke Inhale - Smoke Throw Down - Surprise - Sword Strike - Tantrum - TongueOut - Wave - Whisper - Whistle - Wink - Wink (Hollywood) - Worry - Yes (Happy) - Yes - - - Multiple - - - Loading... - Offline - [AREA] m² L$[PRICE] - None found. - - - OK - Premature end of file - Can't find ROOT or JOINT. - - - whispers: - shouts: - Connecting to in-world Voice Chat... - Connected - Voice not available at your current location - Disconnected from in-world Voice Chat - You will now be reconnected to Nearby Voice Chat - '[OBJECTNAME]', an object owned by '[OWNERNAME]', located in [REGIONNAME] at [REGIONPOS], has been granted permission to: [PERMISSIONS]. - '[OBJECTNAME]', an object owned by '[OWNERNAME]', located in [REGIONNAME] at [REGIONPOS], has been denied permission to: [PERMISSIONS]. - If you allow access to your account, you will also be allowing the object to: - Take Linden dollars (L$) from you - Act on your control inputs - Remap your control inputs - Animate your avatar - Attach to your avatar - Release ownership and become public - Link and delink from other objects - Add and remove joints with other objects - Change its permissions - Track your camera - Control your camera - Teleport you - Not Connected - - - General - Moderate - Adult - Offline - Unknown - - - (unknown) - - - Estate / Full Region - Estate / Homestead - Mainland / Homestead - Mainland / Full Region - - - All Files - Sounds - Animations - Images - Save - Load - Targa Images - Bitmap Images - AVI Movie File - XAF Anim File - XML File - RAW File - Compressed Images - Load Files - Choose Directory - Scripts - Dictionaries - - - - -Sleeps script for [SLEEP_TIME] seconds. - - - -float llSin(float theta) -Returns the sine of theta (theta in radians) - - -float llCos(float theta) -Returns the cosine of theta (theta in radians) - - -float llTan(float theta) -Returns the tangent of theta (theta in radians) - - -float llAtan2(float y, float x) -Returns the arctangent2 of y, x - - -float llSqrt(float val) -Returns the square root of val, or returns 0 and triggers a Math Error for imaginary results - - -float llPow(float base, float exponent) -Returns the base raised to the power exponent, or returns 0 and triggers Math Error for imaginary results - - -integer llAbs(integer val) -Returns the positive version of val - - -float llFabs(float val) -Returns the positive version of val - - -float llFrand(float mag) -Returns a pseudo random number in the range [0,mag) or (mag,0] - - -integer llFloor(float val) -Returns largest integer value <= val - - -integer llCeil(float val) -Returns smallest integer value >= val - - -integer llRound(float val) -Returns val rounded to the nearest integer - - -float llVecMag(vector v) -Returns the magnitude of v - - -vector llVecNorm(vector v) -Returns the v normalized - - -float llVecDist(vector v1, vector v2) -Returns the 3D distance between v1 and v2 - - -vector llRot2Euler(rotation q) -Returns the Euler representation (roll, pitch, yaw) of q - - -rotation llEuler2Rot(vector v) -Returns the rotation representation of Euler Angles v - - -rotation llAxes2Rot(vector fwd, vector left, vector up) -Returns the rotation defined by the coordinate axes - - -vector llRot2Fwd(rotation q) -Returns the forward vector defined by q - - -vector llRot2Left(rotation q) -Returns the left vector defined by q - - -vector llRot2Up(rotation q) -Returns the up vector defined by q - - -rotation llRotBetween(vector v1, vector v2) -Returns the rotation to rotate v1 to v2 - - -llWhisper(integer channel, string msg) -Whispers the text of msg on channel - - -llSay(integer channel, string msg) -Says the text of msg on channel - - -llShout(integer channel, string msg) -Shouts the text of msg on channel - - -integer llListen(integer channel, string name, key id, string msg) -Sets a callback for msg on channel from name and id (name, id, and/or msg can be empty) and returns an identifier that can be used to deactivate or remove the listen - - -llListenControl(integer number, integer active) -Makes a listen event callback active or inactive - - -llListenRemove(integer number) -Removes listen event callback number - - -llSensor(string name, key id, integer type, float range, float arc) -Performs a single scan for name and id with type (AGENT, ACTIVE, PASSIVE, and/or SCRIPTED) within range meters and arc radians of forward vector (name, id, and/or keytype can be empty or 0) - - -llSensorRepeat(string name, key id, integer type, float range, float arc, float rate) -Sets a callback for name and id with type (AGENT, ACTIVE, PASSIVE, and/or SCRIPTED) within range meters and arc radians of forward vector (name, id, and/or keytype can be empty or 0) and repeats every rate seconds - - -llSensorRemove() -Removes the sensor setup by llSensorRepeat - - -string llDetectedName(integer number) -Returns the name of detected object number (returns empty string if number is not a valid sensed object) - - -key llDetectedKey(integer number) -Returns the key of detected object number (returns empty key if number is not a valid sensed object) - - -key llDetectedOwner(integer number) -Returns the key of detected object's owner (returns empty key if number is not a valid sensed object) - - -integer llDetectedType(integer number) -Returns the type (AGENT, ACTIVE, PASSIVE, SCRIPTED) of detected object (returns 0 if number is not a valid sensed object) - - -vector llDetectedPos(integer number) -Returns the position of detected object number (returns <0,0,0> if number is not a valid sensed object) - - -vector llDetectedVel(integer number) -Returns the velocity of detected object number (returns <0,0,0> if number is not a valid sensed object) - - -vector llDetectedGrab(integer number) -Returns the grab offset of the user touching object (returns <0,0,0> if number is not a valid sensed object) - - -rotation llDetectedRot(integer number) -Returns the rotation of detected object number (returns <0,0,0,1> if number is not a valid sensed object) - - -integer llDetectedGroup(integer number) -Returns TRUE if detected object is part of same group as owner - - -integer llDetectedLinkNumber(integer number) -Returns the link position of the triggered event for touches and collisions only - - -llDie() -Deletes the object - - -float llGround(vector offset) -Returns the ground height below the object position + offset - - -float llCloud(vector offset) -Returns the cloud density at the object position + offset - - -vector llWind(vector offset) -Returns the wind velocity at the object position + offset - - -llSetStatus(integer status, integer value) -Sets status (STATUS_PHYSICS, STATUS_PHANTOM, STATUS_BLOCK_GRAB, STATUS_ROTATE_X, STATUS_ROTATE_Y, and/or STATUS_ROTATE_Z) to value - - -integer llGetStatus(integer status) -Returns value of status (STATUS_PHYSICS, STATUS_PHANTOM, STATUS_BLOCK_GRAB, STATUS_ROTATE_X, STATUS_ROTATE_Y, and/or STATUS_ROTATE_Z) - - -llSetScale(vector scale) -Sets the scale of the prim - - -vector llGetScale() -Returns the scale of the prim - - -llSetColor(vector color, integer face) -Sets the color on face of the prim - - -float llGetAlpha(integer face) -Returns the alpha of face - - -llSetAlpha(float alpha, integer face) -Sets the alpha on face - - -vector llGetColor(integer face) -Returns the color on face - - -llSetTexture(string texture, integer face) -Sets the texture of face or ALL_SIDES - - -llScaleTexture(float u, float v, integer face) -Sets the texture u & v scales for the chosen face or ALL_SIDES - - -llOffsetTexture(float u, float v, integer face) -Sets the texture u & v offsets for the chosen face or ALL_SIDES - - -llRotateTexture(float rotation, integer face) -Sets the texture rotation for the chosen face - - -string llGetTexture(integer face) -Returns a string that is the texture on face (the inventory name if it is a texture in the prim's inventory, otherwise the key) - - -llSetPos(vector pos) -Moves the object or prim towards pos without using physics (if the script isn't physical) - - -vector llGetPos() -Returns the position of the task in region coordinates - - -vector llGetLocalPos() -Returns the position relative to the root - - -llSetRot(rotation rot) -Sets the rotation - - -rotation llGetRot() -Returns the rotation relative to the region's axes - - -rotation llGetLocalRot() -Returns the rotation local to the root - - -llSetForce(vector force, integer local) -Applies force to the object (if the script is physical), in local coords if local == TRUE - - -vector llGetForce() -Returns the force (if the script is physical) - - -integer llTarget(vector position, float range) -Sets positions within range of position as a target and return an ID for the target - - -llTargetRemove(integer number) -Removes positional target number registered with llTarget - - -integer llRotTarget(rotation rot, float error) -Set rotations with error of rot as a rotational target and return an ID for the rotational target - - -llRotTargetRemove(integer number) -Removes rotational target number registered with llRotTarget - - -llMoveToTarget(vector target, float tau) -Critically damps to target in tau seconds (if the script is physical) - - -llStopMoveToTarget() -Stops critically damped motion - - -llApplyImpulse(vector force, integer local) -Applies impulse to object (if the script is physical), in local coords if local == TRUE - - -llApplyRotationalImpulse(vector force, integer local) -Applies rotational impulse to object (if the script is physical), in local coords if local == TRUE - - -llSetTorque(vector torque, integer local) -Sets the torque of object (if the script is physical), in local coords if local == TRUE - - -vector llGetTorque() -Returns the torque (if the script is physical) - - -llSetForceAndTorque(vector force, vector torque, integer local) -Sets the force and torque of object (if the script is physical), in local coords if local == TRUE - - -vector llGetVel() -Returns the velocity of the object - - -vector llGetAccel() -Returns the acceleration of the object relative to the region's axes - - -vector llGetOmega() -Returns the rotation velocity in radians per second - - -float llGetTimeOfDay() -Returns the time in seconds since [SECOND_LIFE] server midnight or since region up-time, whichever is smaller - - -float llGetWallclock() -Returns the time in seconds since midnight California Pacific time (PST/PDT) - - -float llGetTime() -Returns the time in seconds since the last region reset, script reset, or call to either llResetTime or llGetAndResetTime - - -llResetTime() -Sets the script timer to zero - - -float llGetAndResetTime() -Returns the script time in seconds and then resets the script timer to zero - - -llSound(string sound, float volume, integer queue, integer loop) -Plays sound at volume and whether it should loop or not. - - -llPlaySound(string sound, float volume) -Plays attached sound once at volume (0.0 - 1.0) - - -llLoopSound(string sound, float volume) -Plays attached sound looping indefinitely at volume (0.0 - 1.0) - - -llLoopSoundMaster(string sound, float volume) -Plays attached sound looping at volume (0.0 - 1.0), declares it a sync master - - -llLoopSoundSlave(string sound, float volume) -Plays attached sound looping at volume (0.0 - 1.0), synced to most audible sync master - - -llPlaySoundSlave(string sound, float volume) -Plays attached sound once at volume (0.0 - 1.0), synced to next loop of most audible sync master - - -llTriggerSound(string sound, float volume) -Plays sound at volume (0.0 - 1.0), centered at but not attached to object - - -llStopSound() -Stops currently attached sound - - -llPreloadSound(string sound) -Preloads a sound on viewers within range - - -string llGetSubString(string src, integer start, integer end) -Returns the indicated substring - - -string llDeleteSubString(string src, integer start, integer end) -Removes the indicated substring and returns the result - - -string llInsertString(string dst, integer position, string src) -Returns a destination string dst with the string src inserted starting at position pos - - -string llToUpper(string src) -Returns a string that is src with all upper-case characters - - -string llToLower(string src) -Returns a string that is src with all lower-case characters - - -llGiveMoney(key destination, integer amount) -Transfers amount of L$ from script owner to destination - - -llMakeExplosion(integer particles, float scale, float vel, float lifetime, float arc, string texture, vector offset) -Makes a round explosion of particles - - -llMakeFountain(integer particles, float scale, float vel, float lifetime, float arc, integer bounce, string texture, vector offset, float bounce_offset) -Makes a fountain of particles - - -llMakeSmoke(integer particles, float scale, float vel, float lifetime, float arc, string texture, vector offset) -Makes smoke like particles - - -llMakeFire(integer particles, float scale, float vel, float lifetime, float arc, string texture, vector offset) -Makes fire like particles - - -llRezObject(string inventory, vector pos, vector vel, rotation rot, integer param) -Instantiates owner's inventory object at pos with velocity vel and rotation rot with start parameter param - - -llLookAt(vector target, float strength, float damping) -Causes object to point its up axis (positive z) towards target, while keeping its forward axis (positive x) below the horizon - - -llStopLookAt() -Stops causing object to point at a target - - -llSetTimerEvent(float sec) -Causes the timer event to be triggered a maximum of once every sec seconds - - -llSleep(float sec) -Puts the script to sleep for sec seconds - - -float llGetMass() -Returns the mass of object that the script is attached to - - -llCollisionFilter(string name, key id, integer accept) -Sets the collision filter, exclusively or inclusively. If accept == TRUE, only accept collisions with objects name and id (either is optional), otherwise with objects not name or id - - -llTakeControls(integer controls, integer accept, integer pass_on) -Allows for intercepting keyboard and mouse clicks from the agent the script has permissions for - - -llReleaseControls() -Stops taking inputs that were taken with llTakeControls - - -llAttachToAvatar(integer attach_point) -Attaches the object to the avatar who has granted permission to the script - - -llDetachFromAvatar() -Detaches object from avatar - - -llTakeCamera(key avatar) -Moves avatar's viewpoint to task - - -llReleaseCamera(key avatar) -Returns camera to agent avatar - - -key llGetOwner() -Returns the object owner's UUID - - -llInstantMessage(key user, string message) -Sends the specified string as an Instant Message to the user - - -llEmail(string address, string subject, string message) -Sends an email to address with the subject and message - - -llGetNextEmail(string address, string subject) -Gets the next waiting email that comes from address, with specified subject - - -key llGetKey() -Returns the key of the prim the script is attached to - - -llSetBuoyancy(float buoyancy) -Sets the buoyancy of the task or object (0 is disabled, < 1.0 sinks, 1.0 floats, > 1.0 rises) - - -llSetHoverHeight(float height, integer water, float tau) -Critically damps to a height above the ground (or water) in tau seconds - - -llStopHover() -Stops hovering to a height - - -llMinEventDelay(float delay) -Sets the minimum time between events being handled - - -llSoundPreload(string sound) -Preloads a sound on viewers within range - - -llRotLookAt(rotation target, float strength, float damping) -Causes object to point its forward axis towards target - - -integer llStringLength(string str) -Returns the length of string - - -llStartAnimation(string anim) -Starts animation anim for agent that granted PERMISSION_TRIGGER_ANIMATION if the permission has not been revoked - - -llStopAnimation(string anim) -Stops animation anim for agent that granted permission - - -llPointAt(vector pos) -Makes agent that owns object point at pos - - -llStopPointAt() -Stops pointing agent that owns object - - -llTargetOmega(vector axis, float spinrate, float gain) -Rotates the object around axis at spinrate with strength gain - - -integer llGetStartParameter() -Returns an integer that is the script start/rez parameter - - -llGodLikeRezObject(key inventory, vector pos) -Rezzes directly off of UUID if owner is in God Mode - - -llRequestPermissions(key agent, integer perm) -Asks the agent for permission to run certain classes of functions - - -key llGetPermissionsKey() -Returns the key of the avatar that last granted permissions to the script - - -integer llGetPermissions() -Returns an integer bitfield with the permissions that have been granted - - -integer llGetLinkNumber() -Returns the link number of the prim containing the script (0 means not linked, 1 the prim is the root, 2 the prim is the first child, etc) - - -llSetLinkColor(integer linknumber, vector color, integer face) -Sets face to color if a task exists in the link chain at linknumber - - -llCreateLink(key target, integer parent) -Attempts to link the script's object with the target (requires that PERMISSION_CHANGE_LINKS be granted). If parent == TRUE, then the script's object becomes the root - - -llBreakLink(integer linknum) -Delinks the prim with the given link number in a linked object set (requires that PERMISSION_CHANGE_LINKS be granted) - - -llBreakAllLinks() -Delinks all prims in the link set (requires that PERMISSION_CHANGE_LINKS be granted) - - -key llGetLinkKey(integer linknumber) -Returns the key of the linked prim linknumber - - -string llGetLinkName(integer linknumber) -Returns the name of linknumber in a link set - - -integer llGetInventoryNumber(integer type) -Returns the number of items of a given type (INVENTORY_* flag) in the prim's inventory - - -string llGetInventoryName(integer type, integer number) -Returns the name of the inventory item number of a given type - - -llSetScriptState(string name, integer run) -Sets the running state of the specified script - - -float llGetEnergy() -Returns how much energy is in the object as a percentage of maximum - - -llGiveInventory(key destination, string inventory) -Gives inventory to destination - - -llRemoveInventory(string item) -Removes the named inventory item - - -llSetText(string text, vector color, float alpha) -Displays text that hovers over the prim with specific color and translucency specified with alpha - - -float llWater(vector offset) -Returns the water height below the object position + offset - - -llPassTouches(integer pass) -If pass == TRUE, touches are passed from children on to parents - - -key llRequestAgentData(key id, integer data) -Requests data about agent id. When data is available the dataserver event will be raised. - - -key llRequestInventoryData(string name) -Requests data from object's inventory object. When data is available the dataserver event will be raised. - - -llSetDamage(float damage) -Sets the amount of damage that will be done when this object hits an avatar. - - -llTeleportAgentHome(key id) -Teleports avatar on the owner's land to their home location without any warning - - -llModifyLand(integer action, integer brush) -Modifies land using the specified action on the specified brush size of land - - -llCollisionSound(string impact_sound, float impact_volume) -Suppresses default collision sounds, replaces default impact sounds with impact_sound at the volume impact_volume - - -llCollisionSprite(string impact_sprite) -Suppresses default collision sprites, replaces default impact sprite with impact_sprite (use an empty string to just suppress) - - -string llGetAnimation(key id) -Returns the name of the currently playing locomotion animation for avatar id - - -llResetScript() -Resets the script - - -llMessageLinked(integer linknum, integer num, string str, key id) -Allows scripts in the same object to communicate. Triggers a link_message event with the same parameters num, str, and id in all scripts in the prim(s) described by linknum. - - -llPushObject(key id, vector impulse, vector ang_impulse, integer local) -Applies impulse and ang_impulse to object id - - -llPassCollisions(integer pass) -If pass == TRUE, collisions are passed from children on to parents (default is FALSE) - - -string llGetScriptName() -Returns the name of the script that this function is used in - - -integer llGetNumberOfSides() -Returns the number of faces (or sides) of the prim - - -rotation llAxisAngle2Rot(vector axis, float angle) -Returns the rotation that is a generated angle about axis - - -vector llRot2Axis(rotation rot) -Returns the rotation axis represented by rot - - -float llRot2Angle(rotation rot) -Returns the rotation angle represented by rot - - -float llAcos(float val) -Returns the arccosine in radians of val - - -float llAsin(float val) -Returns the arcsine in radians of val - - -float llAngleBetween(rotation a, rotation b) -Returns angle between rotation a and b - - -key llGetInventoryKey(string name) -Returns the key that is the UUID of the inventory name - - -llAllowInventoryDrop(integer add) -If add == TRUE, users without modify permissions can still drop inventory items onto a prim - - -vector llGetSunDirection() -Returns a normalized vector of the direction of the sun in the region - - -vector llGetTextureOffset(integer face) -Returns the texture offset of face in the x and y components of a vector - - -vector llGetTextureScale(integer side) -Returns the texture scale of side in the x and y components of a vector - - -float llGetTextureRot(integer side) -Returns the texture rotation of side - - -integer llSubStringIndex(string source, string pattern) -Returns an integer that is the index in source where pattern first appears. -(Returns -1 if not found) - - -key llGetOwnerKey(key id) -Returns the owner of object id - - -vector llGetCenterOfMass() -Returns the prim's center of mass (unless called from the root prim, where it returns the object's center of mass) - - -list llListSort(list src, integer stride, integer ascending) -Sorts the list into blocks of stride, in ascending order if ascending == TRUE. -The sort order is affected by type. - - -integer llGetListLength(list src) -Returns the number of elements in the list - - -integer llList2Integer(list src, integer index) -Copies the integer at index in the list - - -float llList2Float(list src, integer index) -Copies the float at index in the list - - -string llList2String(list src, integer index) -Copies the string at index in the list - - -key llList2Key(list src, integer index) -Copies the key at index in the list - - -vector llList2Vector(list src, integer index) -Copies the vector at index in the list - - -rotation llList2Rot(list src, integer index) -Copies the rotation at index in the list - - -list llList2List(list src, integer start, integer end) -Copies the slice of the list from start to end - - -list llDeleteSubList(list src, integer start, integer end) -Removes the slice from start to end and returns the remainder of the list - - -integer llGetListEntryType(list src, integer index) -Returns the type of the index entry in the list -(TYPE_INTEGER, TYPE_FLOAT, TYPE_STRING, TYPE_KEY, TYPE_VECTOR, TYPE_ROTATION, or TYPE_INVALID if index is off list) - - -string llList2CSV(list src) -Creates a string of comma separated values from list - - -list llCSV2List(string src) -Creates a list from a string of comma separated values - - -list llListRandomize(list src, integer stride) -Returns a randomized list of blocks of size stride - - -list llList2ListStrided(list src, integer start, integer end, integer stride) -Copies the strided slice of the list from start to end - - -vector llGetRegionCorner() -Returns a vector in meters that is the global location of the south-west corner of the region which the object is in - - -list llListInsertList(list dest, list src, integer start) -Returns a list that contains all the elements from dest but with the elements from src inserted at position start - - -integer llListFindList(list src, list test) -Returns the index of the first instance of test in src. -(Returns -1 if not found) - - -string llGetObjectName() -Returns the name of the prim which the script is attached to - - -llSetObjectName(string name) -Sets the prim's name to the name parameter - - -string llGetDate() -Returns the current date in the UTC time zone in the format YYYY-MM-DD - - -integer llEdgeOfWorld(vector pos, vector dir) -Checks to see whether the border hit by dir from pos is the edge of the world (has no neighboring region) - - -integer llGetAgentInfo(key id) -Returns an integer bitfield containing the agent information about id. -Returns AGENT_FLYING, AGENT_ATTACHMENTS, AGENT_SCRIPTED, AGENT_SITTING, AGENT_ON_OBJECT, AGENT_MOUSELOOK, AGENT_AWAY, AGENT_BUSY, AGENT_TYPING, AGENT_CROUCHING, AGENT_ALWAYS_RUN, AGENT_WALKING and/or AGENT_IN_AIR. - - -llAdjustSoundVolume(float volume) -Adjusts volume of attached sound (0.0 - 1.0) - - -llSetSoundQueueing(integer queue) -Sets whether attached sounds wait for the current sound to finish (If queue == TRUE then queuing is enabled, if FALSE queuing is disabled [default]) - - -llSetSoundRadius(float radius) -Establishes a hard cut-off radius for audibility of scripted sounds (both attached and triggered) - - -string llKey2Name(key id) -Returns the name of the prim or avatar specified by id. -(The id must be a valid rezzed prim or avatar key in the current simulator, otherwise an empty string is returned.) - - -llSetTextureAnim(integer mode, integer face, integer sizex, integer sizey, float start, float length, float rate) -Animates the texture on the specified face/faces - - -llTriggerSoundLimited(string sound, float volume, vector top_north_east, vector bottom_south_west) -Plays sound at volume (0.0 - 1.0), centered at but not attached to object, limited to the box defined by vectors top_north_east and bottom_south_west - - -llEjectFromLand(key avatar) -Ejects avatar from the parcel - - -list llParseString2List(string src, list separators, list spacers) -Breaks src into a list, discarding separators, keeping spacers -(separators and spacers must be lists of strings, maximum of 8 each) - - -integer llOverMyLand(key id) -Returns TRUE if id is over land owned by the script owner, otherwise FALSE - - -key llGetLandOwnerAt(vector pos) -Returns the key of the land owner, returns NULL_KEY if public - - -key llGetNotecardLine(string name, integer line) -Returns line line of notecard name via the dataserver event - - -vector llGetAgentSize(key id) -If the avatar is in the same region, returns the size of the bounding box of the requested avatar by id, otherwise returns ZERO_VECTOR - - -integer llSameGroup(key id) -Returns TRUE if avatar id is in the same region and has the same active group, otherwise FALSE - - -key llUnSit(key id) -If avatar identified by id is sitting on the object the script is attached to or is over land owned by the object's owner, the avatar is forced to stand up - - -vector llGroundSlope(vector offset) -Returns the ground slope below the object position + offset - - -vector llGroundNormal(vector offset) -Returns the ground normal below the object position + offset - - -vector llGroundCountour(vector offset) -Returns the ground contour direction below the object position + offset - - -integer llGetAttached() -Returns the object's attachment point, or 0 if not attached - - -integer llGetFreeMemory() -Returns the number of free bytes of memory the script can use - - -string llGetRegionName() -Returns the current region name - - -float llGetRegionTimeDilation() -Returns the current time dilation as a float between 0.0 (full dilation) and 1.0 (no dilation) - - -float llGetRegionFPS() -Returns the mean region frames per second - - -llParticleSystem(list rules) -Creates a particle system based on rules. An empty list removes the particle system. -List format is [ rule1, data1, rule2, data2 . . . rulen, datan ] - - -llGroundRepel(float height, integer water, float tau) -Critically damps to height if within height*0.5 of level (either above ground level, or above the higher of land and water if water == TRUE) - - -llGiveInventoryList(key target, string folder, list inventory) -Gives inventory items to target, creating a new folder to put them in - - -llSetVehicleType(integer type) -Sets the vehicle to one of the default types - - -llSetVehicleFloatParam(integer param, float value) -Sets the specified vehicle float parameter - - -llSetVehicleVectorParam(integer param, vector vec) -Sets the specified vehicle vector parameter - - -llSetVehicleVectorParam(integer param, rotation rot) -Sets the specified vehicle rotation parameter - - -llSetVehicleFlags(integer flags) -Sets the enabled bits in 'flags' - - -llRemoveVehicleFlags(integer flags) -Removes the enabled bits in 'flags' - - -llSitTarget(vector offset, rotation rot) -Sets the sit location for the prim. If offset == <0,0,0> then the sit target is removed. - - -key llAvatarOnSitTarget() -If an avatar is seated on the sit target, returns the avatar's key, otherwise NULL_KEY - - -llAddToLandPassList(key avatar, float hours) -Adds avatar to the land pass list for hours, or indefinitely if hours is 0 - - -llSetTouchText(string text) -Displays text rather than the default 'Touch' in the pie menu - - -llSetSitText(string text) -Displays text rather than the default 'Sit Here' in the pie menu - - -llSetCameraEyeOffset(vector offset) -Sets the camera eye offset for avatars that sit on the object - - -llSetCameraAtOffset(vector offset) -Sets the point the camera is looking at to offset for avatars that sit on the object - - -string llDumpList2String(list src, string separator) -Returns the list in a single string, using separator between the entries - - -integer llScriptDanger(vector pos) -Returns TRUE if pos is over public land, sandbox land, land that doesn't allow everyone to edit and build, or land that doesn't allow outside scripts - - -llDialog(key avatar, string message, list buttons, integer chat_channel -Shows a dialog box on the avatar's screen with a message and up to 12 buttons. -If a button is pressed, the avatar says the text of the button label on chat_channel. - - -llVolumeDetect(integer detect) -If detect = TRUE, object works much like Phantom, but triggers collision_start and collision_end events when other objects start and stop interpenetrating. -Must be applied to the root prim. - - -llResetOtherScript(string name) -Resets script name - - -integer llGetScriptState(string name) -Returns TRUE if the script name is running - - -DEPRECATED! Please use llRemoteLoadScriptPin instead. - - -llSetRemoteScriptAccessPin(integer pin) -If pin is set to a non-zero number, allows a prim to have scripts remotely loaded via llRemoteLoadScriptPin when it passes in the correct pin. Otherwise, llRemoteLoadScriptPin is ignored. - - -llRemoteLoadScriptPin(key target, string name, integer pin, integer running, integer start_param) -Copies script name onto target, if the owner of this scripted object can modify target and is in the same region, and the matching pin is used. -If running == TRUE, starts the script with start_param - - -llOpenRemoteDataChannel() -Creates a channel to listen for XML-RPC calls, and will trigger a remote_data event with channel id once it is available - - -key llSendRemoteData(key channel, string dest, integer idata, string sdata) -Sends an XML-RPC request to dest through channel with payload of channel (in a string), integer idata and string sdata. -Returns a key that is the message_id for the resulting remote_data events. - - -llRemoteDataReply(key channel, key message_id, string sdata, integer idata) -Sends an XML-RPC reply to message_id on channel with payload of string sdata and integer idata - - -llCloseRemoteDataChannel(key channel) -Closes XML-RPC channel - - -string llMD5String(string src, integer nonce) -Returns a string of 32 hex characters that is a RSA Data Security, Inc. MD5 Message-Digest Algorithm of src with nonce - - -llSetPrimitiveParams(list rules) -Sets the prim's parameters according to rules - - -string llStringToBase64(string str) -Converts a string to the Base64 representation of the string - - -string llBase64ToString(string str) -Converts a Base64 string to a conventional string. -If the conversion creates any unprintable characters, they are converted to spaces. - - -string llXorBase64Strings(string s1, string s2) -DEPRECATED! Please use llXorBase64StringsCorrect instead. -Incorrectly performs an exclusive or on two Base64 strings and returns a Base64 string. s2 repeats if it is shorter than s1. Retained for backwards compatability. - - -llRemoteDataSetRegion() -DEPRECATED! Please use llOpenRemoteDataChannel instead. -If an object using remote data channels changes regions, you must call this function to reregister the remote data channels. This call is not needed if the prim does not change regions. - - -float llLog10(float val) -Returns the base 10 logarithm of val. Returns zero if val <= 0. - - -float llLog(float val) -Returns the natural logarithm of val. Returns zero if val <= 0. - - -list llGetAnimationList(key id) -Returns a list of keys of playing animations for avatar described by id - - -llSetParcelMusicURL(string url) -Sets the streaming audio URL for the parcel which the object is on - - -vector llGetRootPosition() -Returns the position (in region coordinates) of the root prim of the object which the script is attached to - - -rotation llGetRootRotation() -Returns the rotation (relative to the region) of the root prim of the object which the script is attached to - - -string llGetObjectDesc() -Returns the description of the prim the script is attached to - - -llSetObjectDesc(string name) -Sets the prim's description - - -key llGetCreator() -Returns a key for the creator of the prim - - -string llGetTimestamp() -Returns the timestamp in the UTC time zone in the format: YYYY-MM-DDThh:mm:ss.ff..fZ - - -llSetLinkAlpha(integer linknumber, float alpha, integer face) -If a prim exists in the link chain at linknumber, sets face to alpha - - -integer llGetNumberOfPrims() -Returns the number of prims in a link set the script is attached to - - -key llGetNumberOfNotecardLines(string name) -Returns number of lines in notecard name via the dataserver event (cast return value to integer) - - -list llGetBoundingBox(key object) -Returns the bounding box around the object (including any linked prims) relative to its root prim, in a list in the format [ (vector) min_corner, (vector) max_corner ] - - -vector llGetGeometricCenter() -Returns the geometric center of the linked set the script is attached to. - - -list llGetPrimitiveParams(list params) -Returns the primitive parameters specified in the params list. - - -string llIntegerToBase64(integer number) -Returns a string that is a Base64 big endian encode of number - - -integer llBase64ToInteger(string str) -Returns an integer that is the str Base64 decoded as a big endian integer - - -float llGetGMTclock() -Returns the time in seconds since midnight GMT - - -string llGetSimulatorHostname() -Returns the hostname of the machine which the script is running on (same as string in viewer Help dialog) - - -llSetLocalRot(rotation rot) -Sets the rotation of a child prim relative to the root prim - - -list llParseStringKeepNulls(string src, list separators, list spacers) -Breaks src into a list, discarding separators, keeping spacers, keeping any null values generated. -(separators and spacers must be lists of strings, maximum of 8 each) - - -llRezAtRoot(string inventory, vector pos, vector vel, rotation rot, integer param) -Instantiates owner's inventory object rotated to rot with its root at pos, moving at vel, using param as the start parameter - - -integer llGetObjectPermMask(integer mask) -Returns the requested permission mask for the root object the task is attached to - - -llSetObjectPermMask(integer mask, integer value) -Sets the given permission mask to the new value on the root object the task is attached to (requires God Mode) - - -integer llGetInventoryPermMask(string item, integer mask) -Returns the requested permission mask for the inventory item - - -llSetInventoryPermMask(string item, integer mask, integer value) -Sets the given permission mask to the new value on the inventory item (requires God Mode) - - -key llGetInventoryCreator(string item) -Returns a key for the creator of the inventory item - - -llOwnerSay(string msg) -Says msg to owner only. (Owner must be in the same region.) - - -key llRequestSimulatorData(string simulator, integer data) -Requests data about simulator. When data is available the dataserver event will be raised. - - -llForceMouselook(integer mouselook) -If mouselook is TRUE, any avatar that sits upon the prim will be forced into mouselook mode - - -float llGetObjectMass(key id) -Returns the mass of the avatar or object in the region - - -list llListReplaceList(list dest, list src, integer start, integer end) -Returns a list that is dest with start through end removed and src inserted at start - - -llLoadURL(key avatar, string message, string url) -Shows a dialog to avatar offering to load the web page at url with a message. -If user clicks yes, launches the page in their web browser. - - -llParcelMediaCommandList(list command) -Sends a list of commands, some with arguments, to a parcel to control the playback of movies and other media - - -list llParcelMediaQuery(list query) -Returns a list containing results of the sent query - - -integer llModPow(integer a, integer b, integer c) -Returns a raised to the b power, mod c. ( (a**b)%c ) -b is capped at 0xFFFF (16 bits). - - -integer llGetInventoryType(string name) -Returns the type of the inventory item name - - -llSetPayPrice(integer price, list quick_pay_buttons) -Sets the default amount on the dialog that appears when someone chooses to pay this prim - - -vector llGetCameraPos() -Returns the current camera position for the agent the task has permissions for - - -rotation llGetCameraRot() -Returns the current camera orientation for the agent the task has permissions for - - -llSetPrimURL(string url) -Updates the URL for the web page shown on the sides of the object - - -llRefreshPrimURL() -Reloads the web page shown on the sides of the object - - -string llEscapeURL(string url) -Returns an escaped/encoded version of url, replacing spaces with %20 etc. - - -string llUnescapeURL(string url) -Returns an unescaped/ unencoded version of url, replacing %20 with spaces etc. - - -llMapDestination(string simname, vector pos, vector look_at) -Opens the World Map centered on the region simname with pos highlighted. (NOTE: look_at currently does nothing.) -Only works for scripts attached to avatar, or during touch events. - - -llAddToLandBanList(key avatar, float hours) -Adds avatar to the land ban list for hours, or indefinitely if hours is 0 - - -llRemoveFromLandPassList(key avatar) -Removes avatar from the land pass list - - -llRemoveFromLandBanList(key avatar) -Removes avatar from the land ban list - - -llSetCameraParams(list rules) -Sets multiple camera parameters at once. -List format is [ rule1, data1, rule2, data2 . . . rulen, datan ] - - -llClearCameraParams() -Resets all camera parameters to default values and turns off scripted camera control - - -float llListStatistics(integer operation, list src) -Performs statistical aggregate functions on list src using LIST_STAT_* operations - - -integer llGetUnixTime() -Returns the number of seconds elapsed since 00:00 hours, Jan 1, 1970 UTC from the system clock - - -integer llGetParcelFlags(vector pos) -Returns a mask of the parcel flags (PARCEL_FLAG_*) for the parcel that includes the point pos - - -integer llGetRegionFlags() -Returns the region flags (REGION_FLAG_*) for the region the object is in - - -string llXorBase64StringsCorrect(string s1, string s2) -Correctly performs an exclusive or on two Base64 strings and returns a Base64 string. -s2 repeats if it is shorter than s1. - - -llHTTPRequest(string url, list parameters, string body) -Sends an HTTP request to the specified url with the body of the request and parameters - - -llResetLandBanList() -Removes all Residents from the land ban list - - -llResetLandPassList() -Removes all Residents from the land access/pass list - - -integer llGetObjectPrimCount(key object_id) -Returns the total number of prims for an object in the region - - -list llGetParcelPrimOwners(vector pos) -Returns a list of all Residents who own objects on the parcel at pos and with individual prim counts. -Requires owner-like permissions for the parcel. - - -integer llGetParcelPrimCount(vector pos, integer category, integer sim_wide) -Returns the number of prims on the parcel at pos of the given category. -Categories: PARCEL_COUNT_TOTAL, _OWNER, _GROUP, _OTHER, _SELECTED, _TEMP - - -integer llGetParcelMaxPrims(vector pos, integer sim_wide) -Returns the maximum number of prims allowed on the parcel at pos - - -list llGetParcelDetails(vector pos, list params) -Returns the parcel details specified in params for the parcel at pos. -Params is one or more of: PARCEL_DETAILS_NAME, _DESC, _OWNER, _GROUP, _AREA, _ID, _SEE_AVATARS - - -llSetLinkPrimitiveParams(integer linknumber, list rules) -Sets primitive parameters for linknumber based on rules - - -llSetLinkTexture(integer linknumber, string texture, integer face) -Sets the texture of face for a task that exists in the link chain at linknumber - - -string llStringTrim(string src, integer trim_type) -Trims the leading and/or trailing white spaces from a string. -trim_type can be STRING_TRIM, STRING_TRIM_HEAD or STRING_TRIM_TAIL. - - -llRegionSay(integer channel, string msg) -Broadcasts msg on channel (not 0) that can be heard anywhere in the region by a script listening on channel - - -list llGetObjectDetails(key id, list params) -Returns the object details specified in params for the object with key id. -Params are OBJECT_NAME, _DESC, _POS, _ROT, _VELOCITY, _OWNER, _GROUP, _CREATOR - - -llSetClickAction(integer action) -Sets the action performed when a prim is clicked upon - - -integer llGetRegionAgentCount() -Returns the number of avatars in the region - - -llTextBox(key avatar, string message, integer chat_channel) -Shows a window on the avatar's screen with the message. -It contains a text box for input, and if entered that text is chatted on chat_channel. - - -string llGetAgentLanguage(key avatar) -Returns the language code of the preferred interface language of the avatar - - -vector llDetectedTouchUV(integer index) -Returns the u and v coordinates in the first two components of a vector, for the texture coordinates where the prim was touched in a triggered touch event - - -integer llDetectedTouchFace(integer index) -Returns the index of the face where the avatar clicked in a triggered touch event - - -vector llDetectedTouchPos(integer index) -Returns the position where the object was touched in a triggered touch event - - -vector llDetectedTouchNormal(integer index) -Returns the surface normal for a triggered touch event - - -vector llDetectedTouchBinormal(integer index) -Returns the surface binormal for a triggered touch event - - -vector llDetectedTouchST(integer index) -Returns the s and t coordinates in the first two components of a vector, for the surface coordinates where the prim was touched in a triggered touch event - - -string llSHA1String(string src) -Returns a string of 40 hex characters that is the SHA1 security Hash of src - - -integer llGetFreeURLs() -Returns the number of available URLs for the current script - - -key llRequestURL() -Requests one HTTP:// url for use by this object. -An http_request event is triggered with the results. - - -key llRequestSecureURL() -Requests one HTTPS:// (SSL) url for use by this object. -An http_request event is triggered with the results. - - -llReleaseURL(string url) -Releases the specified URL, it will no longer be usable - - -llHTTPResponse(key request_id, integer status, string body) -Responds to request_id with status and body - - -string llGetHTTPHeader(key request_id, string header) -Returns the value for header for request_id - - -llSetPrimMediaParams(integer face, list params) -Sets the media params for a particular face on an object. If media is not already on this object, add it. -List is a set of name/value pairs in no particular order. Params not specified are unchanged, or if new media is added then set to the default specified. -The possible names are below, along with the types of values and what they mean. - - -list llGetPrimMediaParams(integer face, list params) -Returns the media params for a particular face on an object, given the desired list of names, in the order requested. -(Returns an empty list if no media exists on the face.) - - -llClearPrimMedia(integer face) -Clears (deletes) the media and all params from the given face. - - -llSetLinkPrimitiveParamsFast(integer linknumber,list rules) -Set primitive parameters for linknumber based on rules. - - -llGetLinkPrimitiveParams(integer linknumber,list rules) -Get primitive parameters for linknumber based on rules. - - -llLinkParticleSystem(integer linknumber,list rules) -Creates a particle system based on rules. Empty list removes particle system from object. -List format is [ rule1, data1, rule2, data2 . . . rulen, datan ]. - - -llSetLinkTextureAnim(integer link, integer mode, integer face, integer sizex, integer sizey, float start, float length, float rate) -Animate the texture on the specified prim's face/faces. - - -integer llGetLinkNumberOfSides(integer link) -Returns the number of sides of the specified linked prim. - - -string llGetUsername(key id) -Returns the single-word username of an avatar, iff the avatar is in the current region, otherwise the empty string. - - -key llRequestUsername(key id) -Requests single-word username of an avatar. When data is available the dataserver event will be raised. - - -string llGetDisplayName(key id) -Returns the name of an avatar, iff the avatar is in the current simulator, and the name has been cached, otherwise the same as llGetUsername. Use llRequestDisplayName if you absolutely must have the display name. - - -key llRequestDisplayName(key id) -Requests name of an avatar. When data is available the dataserver event will be raised. - - -llGetEnv(string name) -Returns a string with the requested data about the region - - -llCastRay(vector start, vector end, list params) -Casts a ray into the physics world from 'start' to 'end' and returns data according to details in params. - - -llRegionSayTo(key target, integer channel, string msg) -Sends msg on channel (not DEBUG_CHANNEL) directly to prim or avatar target anywhere within the region. - - -integer llGetSPMaxMemory() -Returns the maximum used memory for the current script. Only valid after using PROFILE_SCRIPT_MEMORY. Non-mono scripts always use 16k. - - -integer llGetUsedMemory() -Returns the current used memory for the current script. Non-mono scripts always use 16k. - - -llScriptProfiler(integer flags) -Enabled or disables script profiling options. Currently only supports PROFILE_SCRIPT_MEMORY (mono only) and PROFILE_NONE. -MAY SIGNIFICANTLY REDUCE SCRIPT PERFORMANCE! - - -integer llSetMemoryLimit(integer mem) - - -integer llGetMemoryLimit() - - -llSetLinkMedia(integer link, integer face, list params) -Set the media params for a particular face on linked prim. List is a set of name/value pairs (in no particular order). The possible names are below, along with the types of values and what they mean. If media is not already on this object, add it. Params not specified are unchanged, or if new media is added set to the default specified. - - -list llGetLinkMedia(integer link, integer face, list params) -Get the media params for a particular face on linked prim, given the desired list of names. Returns a list of values in the order requested. Returns an empty list if no media exists on the face. - - -llClearLinkMedia(integer link, integer face) -Clears (deletes) the media and all params from the given face on linked prim. - - -llSetContentType(key id, integer content_type) - - -llLinkSitTarget(integer link, vector offset, rotation rot) -Set the sit location for this object (if offset == <0,0,0> clear it) - - -key llAvatarOnLinkSitTarget(integer link) -If an avatar is sitting on the sit target, return the avatar's key, NULL_KEY otherwise - - -llSetLinkCamera(integer link, vector eye, vector at) - - -llSetVelocity(vector velocity, integer local) -Sets an objects velocity, in local coords if local == TRUE (if the script is physical) - - -llSetAngularVelocity(vector angular_velocity, integer local) -Sets an objects angular velocity, in local coords if local == TRUE (if the script is physical) - - -llSetPhysicsMaterial(integer flags, float gravity_multiplier, float restitution, float friction, float density ) -Sets the requested attributes of the root object's physics material. - - -llGetPhysicsMaterial() returns the gravity multiplier, restitution, friction, and density of the linkset as a list in that order. - - -llGetMassMKS() returns the mass of the linkset in kilograms. - - -llGenerateKey() -Retun a unique generated key - - -llSetKeyframedMotion(list keyframes, list options) -Requests that a nonphysical object be keyframed according to keyframe list. - - -key llTransferLindenDollars(key destination, integer amount) -Transfer amount of linden dollars (L$) from script owner to destination. Returns a key to a corresponding transaction_result event for the success of the transfer. - - -string llGetParcelMusicURL() -Gets the streaming audio URL for the parcel of land on which the object is located. - - -integer llSetRegionPos(vector pos) -Sets the position anywhere within the region (if the object isn't physical) - - -llNavigateTo(vector point, list options) -For AI Character: Navigate to destination. - - -llCreateCharacter(list options) -Convert linkset to AI Character which can navigate the world. - - -llPursue(key target, list options) -For AI Character: Chase after a target. - - -llWanderWithin(vector center, float radius, list options) -For AI Character: Wander within a specified volume. - - -llFleeFrom(vector source, float radius, list options) -For AI Character: Flee from a point. - - -llPatrolPoints(list points, list options) -For AI Character: Patrol a list of points. - - -llExecCharacterCmd(integer cmd, list options) -For AI Character: Execute a character command. - - -llDeleteCharacter() -Convert linkset from AI Character to Physics object. - - -llUpdateCharacter(list options) -Change the AI Character's settings. - - -llEvade(key target, list options) -For AI Character: Evade a specified target. - - -list llGetClosestNavPoint(vector point, list options) -For AI Character: Get the closest navigable point to the point provided. - - - - - Not Away - Away - Not Busy - Busy - - - Shape - Skin - Hair - Eyes - Shirt - Pants - Shoes - Socks - Jacket - Gloves - Undershirt - Underpants - Skirt - Alpha - Tattoo - Physics - invalid - none - - - Shirt not worn - Pants not worn - Shoes not worn - Socks not worn - Jacket not worn - Gloves not worn - Undershirt not worn - Underpants not worn - Skirt not worn - Alpha not worn - Tattoo not worn - Physics not worn - invalid - - - Create new shape - Create new skin - Create new hair - Create new eyes - Create new shirt - Create new pants - Create new shoes - Create new socks - Create new jacket - Create new gloves - Create new undershirt - Create new underpants - Create new skirt - Create new alpha - Create new tattoo - Create new physics - invalid - - - New [WEARABLE_ITEM] - - - - Next - OK - Group Notice - Group Notices - Sent by - Attached: - View past notices or opt-out of receiving these messages here. - Open Attachment - Save Attachment - - Teleport offering - - New notifications arrived while you were away. - - You have %d more notification - - - - Right Arm - Head - Left Arm - Left Leg - Torso - Right Leg - - - Low - Mid - High - - - Press ESC to return to World View - - - Didn't find what you're looking for? Try [secondlife:///app/search/all/[SEARCH_TERM] Search]. - Didn't find what you're looking for? Try [secondlife:///app/search/places/[SEARCH_TERM] Search]. - Drag a landmark here to add it to your favorites. - You do not have a copy of this texture in your inventory - Your Marketplace purchases will appear here. You may then drag them into your inventory to use them. - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/ - http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4 - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/imports - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/learn_more - Anyone can sell items on the Marketplace. - - -If you'd like to become a merchant, you'll need to [[MARKETPLACE_CREATE_STORE_URL] create a Marketplace store]. - - Your outbox is empty. - - -Drag folders to this area and click "Send to Marketplace" to list them for sale on the [[MARKETPLACE_DASHBOARD_URL] Marketplace]. - - - No errors - Error: Before sending items to the Marketplace you will need to set yourself up as a merchant (free of charge). - Error: This folder has no contents. - Error: This item failed to upload because your merchant account has too many items unassociated with products. To fix this error, log in to the marketplace website and reduce your unassociated item count. - - Error: This item contains too many objects. Fix this error by placing objects together in boxes to reduce the total count to less than 200. - Error: This item contains too many levels of nested folders. Reorganize it to a maximum of 3 levels of nested folders. - Error: This item can not be sold on the marketplace. - Error: There was a problem with this item. Try again later. - - Open landmarks - - - - - - - - - Loading contents... - No contents - - - - Yes - No - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - My Inventory - Library - Textures - Sounds - Calling Cards - Landmarks - Scripts - Clothing - Objects - Notecards - New Folder - Inventory - Uncompressed Images - Body Parts - Trash - Photo Album - Lost And Found - Uncompressed Sounds - Animations - Gestures - My Favorites - - My Favorites - My Favorites - My Favorites - Current Outfit - Initial Outfits - My Outfits - Accessories - Meshes - Received Items - Merchant Outbox - - - Friends - All - - No attachments worn - Attachments ([COUNT] slots remain) - - - - Buy - Buy for L$ - - Stone - Metal - Glass - Wood - Flesh - Plastic - Rubber - Light - - - Shift - Ctrl - - - Chest - Skull - Left Shoulder - Right Shoulder - Left Hand - Right Hand - Left Foot - Right Foot - Spine - Pelvis - Mouth - Chin - Left Ear - Right Ear - Left Eyeball - Right Eyeball - Nose - R Upper Arm - R Forearm - L Upper Arm - L Forearm - Right Hip - R Upper Leg - R Lower Leg - Left Hip - L Upper Leg - L Lower Leg - Stomach - Left Pec - Right Pec - Neck - Avatar Center - Invalid Attachment Point - - - [AGEYEARS] [AGEMONTHS] old - [AGEYEARS] old - [AGEMONTHS] old - [AGEWEEKS] old - [AGEDAYS] old - Joined today - - - [COUNT] year - [COUNT] years - [COUNT] years - [COUNT] month - [COUNT] months - [COUNT] months - [COUNT] week - [COUNT] weeks - [COUNT] weeks - [COUNT] day - [COUNT] days - [COUNT] days - - [COUNT] member - [COUNT] members - [COUNT] members - - - Resident - Trial - Charter Member - Linden Lab Employee - Payment Info Used - Payment Info On File - No Payment Info On File - Age-verified - Not Age-verified - - - Center 2 - Top Right - Top - Top Left - Center - Bottom Left - Bottom - Bottom Right - - - Downloaded, now compiling - Script not found on server. - Problem downloading - Insufficient permissions to download a script. - Insufficient permissions for - Unknown failure to download - Recompilation Progress - recompile - Reset Progress - reset - Set Running Progress - set running - Set Not Running Progress - set not running - - - Compile successful! - Compile successful, saving... - Save complete. - Script (object out of range) - - - Object [OBJECT] owned by [OWNER] - - - none - - (Unknown) - - - [mthnum,datetime,utc]/[day,datetime,utc]/[year,datetime,utc] - - - Balance - Credits - Debits - Total - No group data found for group - - - parent estate - mainland - teen - - - anyone - - - - error - - all estates owned by [OWNER] - - all estates that you own - - all estates that you manage for [OWNER] - - Allowed Residents: ([ALLOWEDAGENTS], max [MAXACCESS]) - Allowed groups: ([ALLOWEDGROUPS], max [MAXACCESS]) - - - Parcel Script Memory - Parcels Listed: [PARCELS] - Memory used: [COUNT] kb out of [MAX] kb; [AVAILABLE] kb available - Memory used: [COUNT] kb - Parcel Script URLs - URLs used: [COUNT] out of [MAX]; [AVAILABLE] available - URLs used: [COUNT] - Error requesting information - No Parcel Selected - Error: script information is only available in your current region - Retrieving information... - You do not have permission to examine this parcel - - Sitting On - Chest - Head - Left Shoulder - Right Shoulder - Left Hand - Right Hand - Left Foot - Right Foot - Back - Pelvis - Mouth - Chin - Left Ear - Right Ear - Left Eye - Right Eye - Nose - Right Upper Arm - Right Lower Arm - Left Upper Arm - Left Lower Arm - Right Hip - Right Upper Leg - Right Lower Leg - Left Hip - Left Upper Leg - Left Lower Leg - Belly - Right Pec - Left Pec - HUD Center 2 - HUD Top Right - HUD Top Center - HUD Top Left - HUD Center 1 - HUD Bottom Left - HUD Bottom - HUD Bottom Right - - - Line [LINE], Column [COLUMN] - - - [COUNT] found - [hour12,datetime,slt]:[min,datetime,slt] [ampm,datetime,slt] - - - [mthnum,datetime,slt]/[day,datetime,slt] - - - Content of object - New Script - - - The Resident you messaged is in 'busy mode' which means they have requested not to be disturbed. Your message will still be shown in their IM panel for later viewing. - - - (By name) - (Resident) - (Object) - (Group) - (External) - - - There is no Covenant provided for this Estate. - There is no Covenant provided for this Estate. The land on this estate is being sold by the Estate owner, not Linden Lab. Please contact the Estate Owner for sales details. - - - - - - Group Owned - Public - - - Local Settings - Region Settings - - - Clicks: [TELEPORT] teleport, [MAP] map, [PROFILE] profile - (will update after publish) - - - You haven't created any Picks or Classifieds. Click the Plus button below to create a Pick or Classified. - User has no picks or classifieds - Loading... - - - Preview - Properties - - - An object named - owned by the group - owned by an unknown group - owned by - owned by an unknown user - gave you - You decline [DESC] from <nolink>[NAME]</nolink>. - - - Total - bought - paid you - paid into - bought pass to - paid fee for event - paid prize for event - Balance - Credits - Debits - [weekday,datetime,utc] [mth,datetime,utc] [day,datetime,utc], [year,datetime,utc] - - - Contents - - - Acquired Items - Cancel - Uploading [NAME] costs L$ [AMOUNT] - Buying this costs L$ [AMOUNT] - - Unknown file extension .%s -Expected .wav, .tga, .bmp, .jpg, .jpeg, or .bvh - - Block - Block - Unblock - Unblock - Add to My Landmarks... - Edit my Landmark... - - - - - - - Ctrl+ - Alt+ - Shift+ - - - File Saved - Receiving - - - AM - PM - PST - PDT - - - Forward - Left - Right - Back - North - South - West - East - Up - Down - - - Any Category - Shopping - Land Rental - Property Rental - Special Attraction - New Products - Employment - Wanted - Service - Personal - - - None - Linden Location - Adult - Arts & Culture - Business - Educational - Gaming - Hangout - Newcomer Friendly - Parks & Nature - Residential - - Stage - Other - Rental - Any - You - - - : - , - ... - *** - ( - ) - . - ' - --- - - - Multiple Media - Play/Pause Media - - - - An error was found parsing the command line. -Please see: http://wiki.secondlife.com/wiki/Client_parameters -Error: - - [APP_NAME] Command line usage: - - [APP_NAME] is unable to access a file that it needs. - -This can be because you somehow have multiple copies running, or your system incorrectly thinks a file is open. -If this message persists, restart your computer and try again. -If it continues to persist, you may need to completely uninstall [APP_NAME] and reinstall it. - - Fatal Error - [APP_NAME] requires a processor with AltiVec (G4 or later). - - [APP_NAME] is already running. -Check your task bar for a minimized copy of the program. -If this message persists, restart your computer. - - - [APP_NAME] appears to have frozen or crashed on the previous run. -Would you like to send a crash report? - - Notification - - [APP_NAME] is unable to detect DirectX 9.0b or greater. -[APP_NAME] uses DirectX to detect hardware and/or outdated drivers that can cause stability problems, poor performance and crashes. While you can run [APP_NAME] without it, we highly recommend running with DirectX 9.0b. - -Do you wish to continue? - - Warning - - Automatic updating is not yet implemented for Linux. -Please download the latest version from www.secondlife.com. - - RegisterClass failed - Error - - Unable to run fullscreen at [WIDTH] x [HEIGHT]. -Running in window. - - Shutdown Error while destroying window (DestroyWindow() failed) - Shutdown Error - Can't make GL device context - Can't find suitable pixel format - Can't get pixel format description - - [APP_NAME] requires True Color (32-bit) to run. -Please go to your computer's display settings and set the color mode to 32-bit. - - - [APP_NAME] is unable to run because it can't get an 8 bit alpha channel. Usually this is due to video card driver issues. -Please make sure you have the latest video card drivers installed. -Also be sure your monitor is set to True Color (32-bit) in Control Panels > Display > Settings. -If you continue to receive this message, contact the [SUPPORT_SITE]. - - Can't set pixel format - Can't create GL rendering context - Can't activate GL rendering context - - [APP_NAME] is unable to run because your video card drivers did not install properly, are out of date, or are for unsupported hardware. Please make sure you have the latest video card drivers and even if you do have the latest, try reinstalling them. - -If you continue to receive this message, contact the [SUPPORT_SITE]. - - - -5 O'Clock Shadow - -All White -Anime Eyes -Arced -Arm Length -Attached -Attached Earlobes - - -Back Fringe - -Baggy -Bangs - -Beady Eyes -Belly Size -Big -Big Butt - -Big Hair: Back -Big Hair: Front -Big Hair: Top -Big Head -Big Pectorals -Big Spikes -Black -Blonde -Blonde Hair -Blush -Blush Color -Blush Opacity -Body Definition -Body Fat -Body Freckles -Body Thick -Body Thickness -Body Thin - -Bow Legged -Breast Buoyancy -Breast Cleavage -Breast Size -Bridge Width -Broad -Brow Size -Bug Eyes -Bugged Eyes -Bulbous -Bulbous Nose - -Breast Mass -Breast Smoothing -Breast Gravity -Breast Drag - -Max Effect -Spring -Gain -Damping - -Max Effect -Spring -Gain -Damping - -Max Effect -Spring -Gain -Damping - -Belly Mass -Belly Smoothing -Belly Gravity -Belly Drag - -Max Effect -Spring -Gain -Damping - -Butt Mass -Butt Smoothing -Butt Gravity -Butt Drag - -Max Effect -Spring -Gain -Damping - -Max Effect -Spring -Gain -Damping - -Bushy Eyebrows -Bushy Hair -Butt Size -Butt Gravity -Bustle Skirt -No Bustle -More Bustle - -Chaplin -Cheek Bones -Chest Size -Chin Angle -Chin Cleft -Chin Curtains - -Chin Depth -Chin Heavy -Chin In -Chin Out -Chin-Neck -Clear -Cleft -Close Set Eyes -Closed -Closed Back -Closed Front -Closed Left -Closed Right -Coin Purse -Collar Back - - - -Collar Front - - - -Corner Down - -Corner Up -Creased -Crooked Nose - -Cuff Flare -Dark -Dark Green -Darker -Deep -Default Heels - -Dense - -Double Chin -Downturned -Duffle Bag -Ear Angle -Ear Size -Ear Tips -Egg Head -Eye Bags -Eye Color -Eye Depth -Eye Lightness -Eye Opening -Eye Pop -Eye Size -Eye Spacing - -Eyebrow Arc -Eyebrow Density - -Eyebrow Height -Eyebrow Points -Eyebrow Size - -Eyelash Length -Eyeliner -Eyeliner Color - -Eyes Bugged - - - - - - - - -Face Shear -Facial Definition -Far Set Eyes - -Fat Lips - -Female -Fingerless -Fingers -Flared Cuffs -Flat -Flat Butt -Flat Head -Flat Toe -Foot Size -Forehead Angle -Forehead Heavy -Freckles - -Front Fringe - -Full Back -Full Eyeliner -Full Front -Full Hair Sides -Full Sides -Glossy -Glove Fingers - -Glove Length - - -Hair -Hair: Back -Hair: Front -Hair: Sides -Hair Sweep -Hair Thickness -Hair Thickness -Hair Tilt -Hair Tilted Left -Hair Tilted Right -Hair: Volume -Hand Size -Handlebars -Head Length -Head Shape -Head Size -Head Stretch -Heel Height -Heel Shape -Height -High -High Heels -High Jaw -High Platforms -High and Tight -Higher -Hip Length -Hip Width -In -Inner Shadow Color -Inner Shadow Opacity -Inner Eye Corner -Inner Eye Shadow -Inner Shadow - - -Jacket Length - -Jacket Wrinkles -Jaw Angle -Jaw Jut -Jaw Shape -Join -Jowls -Knee Angle -Knock Kneed - -Large -Large Hands -Left Part -Leg Length -Leg Muscles -Less -Less Body Fat -Less Curtains -Less Freckles -Less Full -Less Gravity -Less Love -Less Muscles -Less Muscular -Less Rosy -Less Round -Less Saddle -Less Square -Less Volume -Less soul -Lighter -Lip Cleft -Lip Cleft Depth -Lip Fullness -Lip Pinkness -Lip Ratio -Lip Thickness -Lip Width -Lipgloss -Lipstick -Lipstick Color -Long -Long Head -Long Hips -Long Legs -Long Neck -Long Pigtails -Long Ponytail -Long Torso -Long arms - - -Loose Pants -Loose Shirt -Loose Sleeves - -Love Handles -Low -Low Heels -Low Jaw -Low Platforms -Low and Loose -Lower -Lower Bridge -Lower Cheeks - -Male -Middle Part -More -More Blush -More Body Fat -More Curtains -More Eyeshadow -More Freckles -More Full -More Gravity -More Lipstick -More Love -More Lower Lip -More Muscles -More Muscular -More Rosy -More Round -More Saddle -More Sloped -More Square -More Upper Lip -More Vertical -More Volume -More soul -Moustache - -Mouth Corner -Mouth Position -Mowhawk -Muscular -Mutton Chops - -Nail Polish -Nail Polish Color -Narrow -Narrow Back -Narrow Front -Narrow Lips -Natural -Neck Length -Neck Thickness -No Blush -No Eyeliner -No Eyeshadow - -No Lipgloss -No Lipstick -No Part -No Polish -No Red -No Spikes -No White -No Wrinkles -Normal Lower -Normal Upper -Nose Left -Nose Right -Nose Size -Nose Thickness -Nose Tip Angle -Nose Tip Shape -Nose Width -Nostril Division -Nostril Width - - -Opaque -Open -Open Back -Open Front -Open Left -Open Right -Orange -Out -Outer Shadow Color -Outer Shadow Opacity -Outer Eye Corner -Outer Eye Shadow -Outer Shadow -Overbite - -Package -Painted Nails -Pale -Pants Crotch -Pants Fit -Pants Length - - -Pants Waist -Pants Wrinkles -Part -Part Bangs -Pectorals -Pigment -Pigtails -Pink -Pinker -Platform Height -Platform Width -Pointy -Pointy Heels - -Ponytail -Poofy Skirt -Pop Left Eye -Pop Right Eye -Puffy -Puffy Eyelids -Rainbow Color -Red Hair - -Regular - -Right Part -Rosy Complexion -Round - -Ruddiness -Ruddy -Rumpled Hair -Saddle Bags - - -Scrawny Leg -Separate - - -Shallow -Shear Back -Shear Face -Shear Front - -Shear Left Up - -Shear Right Up -Sheared Back -Sheared Front -Shift Left -Shift Mouth -Shift Right -Shirt Bottom - -Shirt Fit - -Shirt Wrinkles -Shoe Height - -Short -Short Arms -Short Legs -Short Neck -Short Pigtails -Short Ponytail -Short Sideburns -Short Torso -Short hips -Shoulders - -Side Fringe -Sideburns - -Sides Hair -Sides Hair Down -Sides Hair Up - -Skinny Neck -Skirt Fit -Skirt Length -Slanted Forehead -Sleeve Length - - - -Sleeve Looseness -Slit: Back -Slit: Front -Slit: Left -Slit: Right -Small -Small Hands -Small Head -Smooth -Smooth Hair -Socks Length - - -Soulpatch - -Sparse -Spiked Hair -Square -Square Toe -Squash Head - -Stretch Head -Sunken -Sunken Chest -Sunken Eyes -Sweep Back -Sweep Forward - -Tall -Taper Back -Taper Front -Thick Heels -Thick Neck -Thick Toe - -Thin -Thin Eyebrows -Thin Lips -Thin Nose -Tight Chin -Tight Cuffs -Tight Pants -Tight Shirt -Tight Skirt -Tight Sleeves - -Toe Shape -Toe Thickness -Torso Length -Torso Muscles -Torso Scrawny -Unattached -Uncreased -Underbite -Unnatural -Upper Bridge -Upper Cheeks -Upper Chin Cleft - -Upper Eyelid Fold -Upturned -Very Red -Waist Height - - -Well-Fed -White Hair -Wide -Wide Back -Wide Front -Wide Lips -Wild -Wrinkles - - - Add to My Landmarks - Edit my Landmark - See more info about the current location - My location history - Buy this land - Voice not available here - Flying not allowed - No pushing - Building/dropping objects not allowed - Scripts not allowed - Health - Adult Region - Moderate Region - General Region - Avatars visible and chat allowed outside of this parcel - Objects that move may not behave correctly in this region until the region is rebaked. - Dynamic pathfinding is not enabled on this region. - - - [APP_NAME] Update - - - Now updating [APP_NAME]... - - - Installing [APP_NAME]... - - - Your [APP_NAME] Viewer is being updated to the latest release. This may take some time, so please be patient. - - - Downloading update... - - - Downloading update - - - Failed to download update - - - An error occurred while updating [APP_NAME]. Please download the latest version from www.secondlife.com. - - - Failed to install update - - - Failed to start viewer - - - - [APP_NAME]: Items coming in too fast from [FROM_NAME], automatic preview disabled for [TIME] seconds - [APP_NAME]: Items coming in too fast, automatic preview disabled for [TIME] seconds - - - -- Instant message logging enabled -- - [NAME] is typing... - (Unnamed) - (Moderated: Voices off by default) - Text chat is not available for this call. - Your text chat has been disabled by a Group Moderator. - Click here to instant message. - To - (Moderator) - (Saved [LONG_TIMESTAMP]) - To see this message, you must uncheck 'Only friends and groups can call or IM me' in Preferences/Privacy. - - - Your call has been answered - You started a voice call - You joined the voice call - [NAME] started a voice call - - - Joining voice call... - - - Connected, click Leave Call to hang up - - - Left voice call - - - Connecting... - - - Ad-hoc Conference - - - Conference with [AGENT_NAME] - - - Inventory item offered - - - Drag items from inventory here - - - - - (IM Session Doesn't Exist) - - - You are the only user in this session. - - - [NAME] is offline. - - - Click the [BUTTON NAME] button to accept/connect to this voice chat. - - - You have blocked this Resident. Sending a message will automatically unblock them. - - - - Error making request, please try again later. - - - Error making request, please try again later. - - - You do not have sufficient permissions. - - - The session no longer exists - - - You do not have that ability. - - - You do not have that ability. - - - You are not a session moderator. - - - - A group moderator disabled your text chat. - - - A group moderator disabled your text chat. - - - Unable to add users to chat session with [RECIPIENT]. - - - - Unable to send your message to the chat session with [RECIPIENT]. - - - Unable to send your message to the chat session with [RECIPIENT]. - - - Error while moderating. - - - - You have been removed from the group. - - - You have been removed from the group. - - - You no longer have the ability to be in the chat session. - - - [SOURCES] has said something new - - - [SOURCES] have said something new - - - The session initialization is timed out - - - Home position set. - - http://secondlife.com/landing/voicemorphing - - - [NAME] paid you L$[AMOUNT] [REASON]. - [NAME] paid you L$[AMOUNT]. - You paid [NAME] L$[AMOUNT] [REASON]. - You paid L$[AMOUNT]. - You paid [NAME] L$[AMOUNT]. - You paid L$[AMOUNT] [REASON]. - You failed to pay [NAME] L$[AMOUNT] [REASON]. - You failed to pay L$[AMOUNT]. - You failed to pay [NAME] L$[AMOUNT]. - You failed to pay L$[AMOUNT] [REASON]. - for [ITEM] - for a parcel of land - for a land access pass - for deeding land - to create a group - to join a group - to upload - to publish a classified ad - - Giving L$ [AMOUNT] - Uploading costs L$ [AMOUNT] - This costs L$ [AMOUNT] - Buying selected land for L$ [AMOUNT] - This object costs L$ [AMOUNT] - - Everyone - Officers - Owners - Online - - Uploading... - -Abuse Report - - - New Shape - New Skin - New Hair - New Eyes - New Shirt - New Pants - New Shoes - New Socks - New Jacket - New Gloves - New Undershirt - New Underpants - New Skirt - New Alpha - New Tattoo - New Physics - Invalid Wearable - New Gesture - New Script - New Note - New Folder - Contents - Gesture - Male Gestures - Female Gestures - Other Gestures - Speech Gestures - Common Gestures - - Male - Excuse me - Male - Get lost - Male - Blow kiss - Male - Boo - Male - Bored - Male - Hey - Male - Laugh - Male - Repulsed - Male - Shrug - Male - Stick tougue out - Male - Wow - - Female - Chuckle - Female - Cry - Female - Embarrassed - Female - Excuse me - Female - Get lost - Female - Blow kiss - Female - Boo - Female - Bored - Female - Hey - Female - Hey baby - Female - Laugh - Female - Looking good - Female - Over here - Female - Please - Female - Repulsed - Female - Shrug - Female - Stick tougue out - Female - Wow - - /bow - /clap - /count - /extinguish - /kmb - /muscle - /no - /no! - /paper - /pointme - /pointyou - /rock - /scissor - /smoke - /stretch - /whistle - /yes - /yes! - afk - dance1 - dance2 - dance3 - dance4 - dance5 - dance6 - dance7 - dance8 - - - [mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt] - - none/none - Can't load images larger than [WIDTH]*[HEIGHT] - - - - - - - Despite our best efforts, something unexpected has gone wrong. - - Please check status.secondlifegrid.net to see if there is a known problem with the service. - If you continue to experience problems, please check your network and firewall setup. - - - - Sunday:Monday:Tuesday:Wednesday:Thursday:Friday:Saturday - Sun:Mon:Tue:Wed:Thu:Fri:Sat - January:February:March:April:May:June:July:August:September:October:November:December - Jan:Feb:Mar:Apr:May:Jun:Jul:Aug:Sep:Oct:Nov:Dec - [MDAY] - AM - PM - - - US$ [AMOUNT] - - - Membership - Roles - Group Identity - Parcel Management - Parcel Identity - Parcel Settings - Parcel Powers - Parcel Access - Parcel Content - Object Management - Accounting - Notices - Chat - - - Delete selected items? - Delete selected item? - - There are no items in this outfit - - - Select an editor using the ExternalEditor setting. - Cannot find the external editor you specified. -Try enclosing path to the editor with double quotes. -(e.g. "/path to my/editor" "%s") - Error parsing the external editor command. - External editor failed to run. - - - Translation failed: [REASON] - Error parsing translation response. - - - Esc - Space - Enter - Tab - Ins - Del - Backsp - Shift - Ctrl - Alt - CapsLock - Left - Right - Up - Down - Home - End - PgUp - PgDn - - F1 - F2 - F3 - F4 - F5 - F6 - F7 - F8 - F9 - F10 - F11 - F12 - - Add - Subtract - Multiply - Divide - PAD_DIVIDE - PAD_LEFT - PAD_RIGHT - PAD_DOWN - PAD_UP - PAD_HOME - PAD_END - PAD_PGUP - PAD_PGDN - PAD_CENTER - PAD_INS - PAD_DEL - PAD_Enter - PAD_BUTTON0 - PAD_BUTTON1 - PAD_BUTTON2 - PAD_BUTTON3 - PAD_BUTTON4 - PAD_BUTTON5 - PAD_BUTTON6 - PAD_BUTTON7 - PAD_BUTTON8 - PAD_BUTTON9 - PAD_BUTTON10 - PAD_BUTTON11 - PAD_BUTTON12 - PAD_BUTTON13 - PAD_BUTTON14 - PAD_BUTTON15 - - - - = - ` - ; - [ - ] - \ - - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - - A - B - C - D - E - F - G - H - I - J - K - L - M - N - O - P - Q - R - S - T - U - V - W - X - Y - Z - - - - Viewing particle beacons (blue) - Viewing physical object beacons (green) - Viewing scripted object beacons (red) - Viewing scripted object with touch function beacons (red) - Viewing sound beacons (yellow) - Viewing media beacons (white) - Hiding Particles - - - - About land - Appearance - Avatar - Build - Chat - Compass - Destinations - Gestures - How to - Inventory - Map - Marketplace - Mini-map - Walk / run / fly - Merchant outbox - People - Picks - Places - Preferences - Profile - Search - Snapshot - Speak - Camera controls - Voice settings - - Information about the land you're visiting - Change your avatar - Choose a complete avatar - Building objects and reshaping terrain - Chat with people nearby using text - Compass - Destinations of interest - Gestures for your avatar - How to do common tasks - View and use your belongings - Map of the world - Go shopping - Show nearby people - Moving your avatar - Transfer items to your marketplace for sale - Friends, groups, and nearby people - Places to show as favorites in your profile - Places you've saved - Preferences - Edit or view your profile - Find places, events, people - Take a picture - Speak with people nearby using your microphone - Changing camera angle - Volume controls for calls and people near you in world - - currently in your bottom toolbar - currently in your left toolbar - currently in your right toolbar - - - Retain% - Detail - Better Detail - Surface - Solid - Wrap - Preview - Normal - - - http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer - None - Affects navmesh - Character - (Multiple) - - - Very Low - Low - Medium - High - Very High - - The Resident cannot visit this region. - - - [User] - - diff --git a/indra/newview/skins/steam/xui/es/strings.xml b/indra/newview/skins/steam/xui/es/strings.xml deleted file mode 100644 index daa3199cec..0000000000 --- a/indra/newview/skins/steam/xui/es/strings.xml +++ /dev/null @@ -1,4961 +0,0 @@ - - - - - SECOND LIFE - - - Portal de Soporte de Second Life - - - Identificando el hardware... - - - Instalando [APP_NAME]... - - - Limpiando la caché... - - - Iniciando la caché de las texturas... - - - Iniciando VFS... - - - Error de inicialización de gráficos. Actualiza tu controlador de gráficos. - - - Restaurando... - - - Cambiando la resolución... - - - Brillo al máximo (antiguo) - - - Iniciando la sesión. [APP_NAME] debe de aparecer congelado. Por favor, espere. - - - Iniciando la sesión... - - - Autenticando - - - Realizando el mantenimiento de la cuenta... - - - Ha fallado el intento previo de iniciar sesión. Iniciando sesión, intento [NUMBER] - - - Cargando el mundo... - - - Iniciando el navegador web incorporado... - - - Iniciando multimedia... - - - Cargando las fuentes... - - - Comprobando los archivos de la caché (puede tardar entre 60 y 90 segundos)... - - - Procesando la respuesta... - - - Iniciando el mundo... - - - Decodificando las imágenes... - - - Iniciando QuickTime... - - - No se ha encontrado QuickTime. Imposible iniciarlo. - - - QuickTime se ha iniciado adecuadamente. - - - Solicitando capacidades de la región... - - - Solicitando capacidades de la región, intento [NUMBER]... - - - Esperando la conexión con la región... - - - Conectando con la región... - - - Descargando la ropa... - - - El servidor devolvió un certificado no válido o dañado. Ponte en contacto con el administrador de la cuadrícula. - - - El nombre de host utilizado para acceder al servidor no es válido. Comprueba tu SLURL o el nombre de host de la cuadrícula. - - - Parece que el certificado que devolvió la cuadrícula está caducado. Comprueba el reloj del sistema o consulta al administrador de la cuadrícula. - - - El certificado que devolvió el servidor no puede utilizarse para SSL. Ponte en contacto con el administrador de la cuadrícula. - - - La cadena de certificado del servidor contenía demasiados certificados. Ponte en contacto con el administrador de la cuadrícula. - - - No se pudo verificar la firma del certificado devuelta por el servidor de la cuadrícula. Ponte en contacto con el administrador de la cuadrícula. - - - Error de red: no se ha podido conectar; por favor, revisa tu conexión a internet. - - - Error en el inicio de sesión. - - - Salir - - - http://join.secondlife.com/index.php?sourceid=1206_steam&lang=es-ES - - - Ya no puedes acceder a Second Life con el visor que estás utilizando. Visita la siguiente página para descargar un nuevo visor: -http://secondlife.com/download. - -Si deseas obtener más información, consulta las preguntas frecuentes que aparecen a continuación: -http://secondlife.com/viewer-access-faq - - - Actualización opcional del visor disponible: [VERSIÓN] - - - Actualización necesaria del visor: [VERSIÓN] - - - El agente ya ha iniciado sesión. - - - Lo sentimos. No ha sido posible iniciar sesión. -Comprueba si has introducido correctamente - * El nombre de usuario (como juangarcia12 o estrella.polar) - * Contraseña -Asimismo, asegúrate de que la tecla Mayús esté desactivada. - - - Como precaución de seguridad, se ha modificado tu contraseña. -Dirígete a la página de tu cuenta en http://secondlife.com/password -y responde a la pregunta de seguridad para restablecer la contraseña. -Lamentamos las molestias. - - - Hemos realizado unos cambios en nuestro sistema, por lo que deberás restablecer la contraseña. -Dirígete a la página de tu cuenta en http://secondlife.com/password -y responde a la pregunta de seguridad para restablecer la contraseña. -Lamentamos las molestias. - - - Second Life no está disponible temporalmente debido a tareas de mantenimiento. -Actualmente, solo se permite iniciar sesión a los empleados. -Consulta www.secondlife.com/status si deseas obtener actualizaciones. - - - Las conexiones a Second Life se han restringido provisionalmente para garantizar que los usuarios que ya están conectados tengan la mejor experiencia posible. - -Durante este tiempo, las personas con cuentas gratuitas no podrán acceder a Second Life, ya que tienen prioridad los usuarios con una cuenta de pago. - - - No se puede acceder a Second Life desde este ordenador. -Si crees que se trata de un error, ponte en contacto con -support@secondlife.com. - - - No se podrá acceder a tu cuenta hasta las -[HORA] (horario de la costa del Pacífico). - - - En este momento no podemos completar la solicitud. -Si deseas obtener asistencia, ponte en contacto con el departamento de soporte de Second Life a través de la página http://secondlife.com/support. -Si no puedes cambiar la contraseña, llama al número (866) 476-9763. - - - Se han detectado datos incorrectos en el inicio de sesión. -Ponte en contacto con support@secondlife.com. - - - Se están realizando tareas rutinarias de mantenimiento en tu cuenta. -No se podrá acceder a tu cuenta hasta las -[HORA] (horario de la costa del Pacífico). -Si crees que se trata de un error, ponte en contacto con support@secondlife.com. - - - La solicitud de cierre de sesión ha obtenido como resultado un error del simulador. - - - El sistema está cerrando tu sesión en estos momentos. -Tu cuenta no estará disponible hasta las -[HORA] (horario de la costa del Pacífico). - - - No se ha podido crear una sesión válida. - - - No se ha podido establecer la conexión con un simulador. - - - Tu cuenta solo puede acceder a Second Life -entre las [INICIO] y las [FIN] (horario de la costa del Pacífico). -Inténtalo de nuevo durante ese horario. -Si crees que se trata de un error, ponte en contacto con support@secondlife.com. - - - Parámetros incorrectos. -Si crees que se trata de un error, ponte en contacto con support@secondlife.com. - - - El parámetro correspondiente al nombre debe contener caracteres alfanuméricos. -Si crees que se trata de un error, ponte en contacto con support@secondlife.com. - - - El parámetro correspondiente al apellido debe contener caracteres alfanuméricos. -Si crees que se trata de un error, ponte en contacto con support@secondlife.com. - - - La región se está desconectando. -Intenta iniciar sesión de nuevo en unos instantes. - - - El agente no se encuentra en la región. -Intenta iniciar sesión de nuevo en unos instantes. - - - A esta región ya se ha accedido en otra sesión. -Intenta iniciar sesión de nuevo en unos instantes. - - - Se ha salido de la región en la sesión anterior. -Intenta iniciar sesión de nuevo en unos instantes. - - - La región aún está cerrando la sesión anterior. -Intenta iniciar sesión de nuevo en unos instantes. - - - Se ha salido de la región en la última sesión. -Intenta iniciar sesión de nuevo en unos instantes. - - - La región ha comenzado el proceso de cierre de sesión. -Intenta iniciar sesión de nuevo en unos instantes. - - - El sistema ha comenzado a cerrar la última sesión. -Intenta iniciar sesión de nuevo en unos instantes. - - - Esta región puede estar teniendo problemas. Por favor, comprueba tu conexión a Internet. - - - Guardando tus configuraciones... - - - Cerrando sesión... - - - Cerrando... - - - Has sido desconectado de la región en la que estabas. - - - Has sido enviado a una región no válida. - - - Probando la desconexión del visor - - - Persona - - - (sin nombre) - - - Propietario: - - - Público - - - (Grupo) - - - En venta: [AMOUNT] L$ - - - Construir el grupo - - - No construir - - - Construir el grupo - - - No seguro - - - No volar - - - Scripts el grupo - - - No scripts - - - Terreno: - - - Aquí se puede arrastrar sólo un ítem - - - - No puedes colocar objetos en tu buzón de salida de comerciante - - - Uno o varios de estos objetos no se pueden vender o transferir. - - - Tu buzón de salida de comerciante sólo puede aceptar objetos procedentes directamente de tu inventario - - - No puedes poner artículos que llevas puestos en el buzón de salida de comerciante - - - No puedes poner tarjetas de visita en tu buzón de salida de comerciante - - - La profundidad de carpetas anidadas excede de 3 - - - El número de subcarpetas de la carpeta de nivel superior excede de 20 - - - El número de elementos de la carpeta de nivel superior excede de 200 - - - No puedes mover una carpeta a su carpeta secundaria - - - No puedes mover una carpeta dentro de sí misma - - - Pulsa para ver esta página web - - - Pulsa para ver la información de este lugar - - - Pulsa para ver el perfil del Residente - - - Obtén más información acerca de este residente. - - - Pulsa para silenciar a este Residente - - - Pulsa para quitar el silencio a este Residente - - - Pulsa para enviar un MI a este Residente - - - Pulsa para pagar a este Residente - - - Pulsa para enviar una petición de teleporte a este Residente - - - Pulsa para enviar una petición de amistad a este Residente - - - Pulsa para ver la descripción de este grupo - - - Pulsa para ver la descripción de este evento - - - Pulsa para ver este clasificado - - - Pulsa para ver la descripción de esta parcela - - - Pulsa para teleportarte a esta posición - - - Pulsa para ver la descripción de este objeto - - - Pulsa para ver en el mapa esta localización - - - Pulsa para ejecutar el comando secondlife:// - - - - Teleportarse a - - - Mostrarla en el mapa - - - Silenciar - - - Quitar el silencio - - - MI - - - Pagar - - - Ofrecer teleporte a - - - Petición de amistad - - - Cerrar (⌘W) - - - Cerrar (Ctrl+W) - - - Cerrar - - - Maximizar - - - Minimizar - - - Separar la ventana - - - Fijar - - - Ver la Ayuda - - - Buscando... - - - No se ha encontrado. - - - Reintentando... - - - Notas de la versión - - - http://wiki.secondlife.com/wiki/Release_Notes/ - - - Cargando... - - - (nadie) - - - (esperando) - - - (ninguno) - - - Avaline: [ORDER] - - - No hay ningún error - - - Petición de asset: fallida - - - Petición de asset: el archivo no existe - - - Petición de asset: no se encontró el asset en la base de datos - - - Fin del archivo - - - No puede abrirse el archivo - - - No se ha encontrado el archivo - - - Tiempo de transferencia del archivo - - - Circuito desconectado - - - No concuerda el precio en el visor y en el servidor - - - Estado desconocido - - - la textura - - - el sonido - - - la tarjeta de visita - - - el hito - - - el script antiguo - - - esa ropa - - - el objeto - - - la nota - - - la carpeta - - - la ruta - - - ese script de LSL2 - - - el código intermedio de LSL - - - esa textura tga - - - esa parte del cuerpo - - - la foto - - - Objetos Perdidos - - - esa imagen targa - - - la Papelera - - - esa imagen jpeg - - - la animación - - - el gesto - - - simstate - - - ese favorito - - - el enlace - - - enlace de la carpeta - - - red - - - (Edición de Apariencia) - - - Ausente - - - Ocupado - - - Ignorado - - - Susto - - - Enfado - - - Ausente - - - Salto mortal atrás - - - Carcajada - - - Gran sonrisa - - - Mandar un beso - - - Aburrimiento - - - Reverencia - - - Aplauso - - - Reverencia floreada - - - Llanto - - - Baile 1 - - - Baile 2 - - - Baile 3 - - - Baile 4 - - - Baile 5 - - - Baile 6 - - - Baile 7 - - - Baile 8 - - - Desdén - - - Beber - - - Azorarse - - - Negar con el dedo - - - Éxito con el puño - - - Yoga flotando - - - Fruncir el ceño - - - Impaciente - - - Salto de alegría - - - Bésame el culo - - - Besar - - - Reír - - - Sacar músculo - - - No (con enfado) - - - No - - - Ña-Ña-Ña - - - Puñetazo uno-dos - - - Abrir la boca - - - 'V' con los dedos - - - Señalar a otro/a - - - Señalarse - - - Puñetazo izquierdo - - - Puñetazo derecho - - - PPT cuenta - - - PPT papel - - - PPT piedra - - - PPT tijera - - - Repulsa - - - Patada circular - - - Triste - - - Saludo militar - - - Gritar - - - Encogerse de hombros - - - Sonreír - - - Fumar: en la mano - - - Fumar - - - Fumar: tirar el cigarro - - - Sorpresa - - - Estocadas - - - Berrinche - - - Sacar la lengua - - - Agitar la mano - - - Cuchichear - - - Pitar - - - Guiño - - - Guiño (Hollywood) - - - Preocuparse - - - Sí (contento) - - - Sí - - - Cargando... - - - Sin conexión - - - [PRICE] L$ por [AREA] m² - - - No se ha encontrado. - - - OK - - - Fin prematuro del archivo - - - No se puede encontrar ROOT o JOINT. - - - susurra: - - - grita: - - - Conectando al chat de voz... - - - Conectado - - - La voz no está disponible en su localización actual - - - Desconectado del chat de voz - - - Vas a ser reconectado al chat de voz con los cercanos - - - '[OBJECTNAME]', un objeto propiedad de '[OWNERNAME]', localizado en [REGIONNAME] con la posición [REGIONPOS], ha recibido permiso para: [PERMISSIONS]. - - - A '[OBJECTNAME]', un objeto propiedad de '[OWNERNAME]', localizado en [REGIONNAME] con la posición [REGIONPOS], se le ha denegado el permiso para: [PERMISSIONS]. - - - Si autorizas el acceso a tu cuenta, también permitirás al objeto: - - - Cogerle a usted dólares Linden (L$) - - - Actuar en sus controles de entrada - - - Reconfigurar sus controles de entrada - - - Ejecutar animaciones en su avatar - - - Anexarse a su avatar - - - Anular la propiedad y que pase a ser público - - - Enlazar y desenlazar de otros objetos - - - Añadir y quitar uniones con otros objetos - - - Cambiar sus permisos - - - Seguir su cámara - - - Controlar su cámara - - - Teleportarte - - - General - - - Moderado - - - Adulto - - - Desconectado - - - Desconocido - - - (desconocido) - - - Estado /Región completa - - - Estado / Homestead - - - Continente / Homestead - - - Continente / Región completa - - - Todos los archivos - - - Sonidos - - - Animaciones - - - Imágenes - - - Guardar - - - Cargar - - - Imágenes Targa - - - Imágenes de mapa de bits - - - Archivo de película AVI - - - Archivo de anim. XAF - - - Archivo XML - - - Archivo RAW - - - Imágenes comprimidas - - - Cargar archivos - - - Elegir directorio - - - Scripts - - - Diccionarios - - - Salir del estado ausente - - - Pasar al estado ausente - - - Salir del estado ocupado - - - Pasar al estado ocupado - - - Forma - - - Piel - - - Pelo - - - Ojos - - - Camisa - - - Pantalón - - - Zapatos - - - Calcetines - - - Chaqueta - - - Guantes - - - Camiseta - - - Ropa interior - - - Falda - - - Alfa - - - Tatuaje - - - Física - - - inválido/a - - - ninguno - - - Camisa no puesta - - - Pantalones no puestos - - - Zapatos no puestos - - - Calcetines no puestos - - - Chaqueta no puesta - - - Guantes no puestos - - - Camiseta no puesta - - - Ropa interior no puesta - - - Falda no puesta - - - Alfa no puesta - - - Tatuaje no puesto - - - Física no puesta - - - no válido/a - - - Crear una anatomía nueva - - - Crear una piel nueva - - - Crear pelo nuevo - - - Crear ojos nuevos - - - Crear una camisa nueva - - - Crear unos pantalones nuevos - - - Crear unos zapatos nuevos - - - Crear unos calcetines nuevos - - - Crear una chaqueta nueva - - - Crear unos guantes nuevos - - - Crear una camiseta nueva - - - Crear ropa interior nueva - - - Crear una falda nueva - - - Crear una capa alfa nueva - - - Crear un tatuaje nuevo - - - Crear nueva física - - - no válido/a - - - Nuevo [WEARABLE_ITEM] - - - Siguiente - - - OK - - - Aviso de grupo - - - Avisos del grupo - - - Enviado por - - - Adjunto: - - - Ver los avisos pasados u optar por dejar de recibir aquí estos mensajes. - - - Abrir el adjunto - - - Guardar el adjunto - - - Ofrecimiento de teleporte - - - Llegaron avisos nuevos mientras estabas ausente... - - - Tienes [%d] aviso/s más - - - Brazo der. - - - Cabeza - - - Brazo izq. - - - Pierna izq. - - - Torso - - - Pierna der. - - - Bajo - - - Medio - - - Alto - - - Pulsa ESC para salir de la vista subjetiva - - - ¿No encuentras lo que buscas? Prueba con [secondlife:///app/search/all/[SEARCH_TERM] Buscar]. - - - ¿No encuentras lo que buscas? Prueba con [secondlife:///app/search/places/[SEARCH_TERM] Buscar]. - - - Arrastra aquí un hito para tenerlo en tus favoritos. - - - No tienes en tu inventario una copia de esta textura - - - Aquí aparecerán algunos de los objetos que recibas, como los regalos Premium. Después puedes arrastrarlos a tu inventario. - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/ - - - http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4 - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/imports - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/learn_more - - - Cualquier usuario puede vender objetos en el mercado. - - - - Para hacerte comerciante debes [[MARKETPLACE_CREATE_STORE_URL] crear una tienda del Mercado]. - - - El buzón de salida está vacío. - - - - Arrastra carpetas a esta sección y pulsa en "Enviar al Mercado" para incluirlas en la lista de venta del [[MARKETPLACE_DASHBOARD_URL] Mercado]. - - - Sin errores - - - Error: Para poder enviar objetos al mercado, debes registrarte como comerciante (es gratis). - - - Error: Esta carpeta está vacía. - - - Error: Este objeto no se pudo subir porque tu cuenta de comerciante tiene demasiados objetos que no están asociados a productos. Para corregirlo, inicia sesión en la página web del mercado y asocia más objetos. - - - Error: Este elemento contiene demasiados objetos. Para corregir el error, guarda objetos en cajas de forma que el total de objetos sea menor que 200. - - - Error: Este objeto contiene demasiados niveles de carpetas anidadas. Reorganízalo de forma que tenga como máximo 3 niveles de carpetas anidadas. - - - Error: Este objeto no se puede vender en el mercado. - - - Error: Este objeto tiene un problema. Vuelve a intentarlo más tarde. - - - Abrir hitos - - - - - - - - - Cargando el contenido... - - - No hay contenido - - - - - - - - - - - - - - - - - - - - - - - - - - - - Mi Inventario - - - Biblioteca - - - Texturas - - - Sonidos - - - Tarjetas de visita - - - Hitos - - - Scripts - - - Ropa - - - Objetos - - - Notas - - - Carpeta nueva - - - Inventario - - - Imágenes sin comprimir - - - Partes del cuerpo - - - Papelera - - - Álbum de fotos - - - Objetos Perdidos - - - Sonidos sin comprimir - - - Animaciones - - - Gestos - - - Mis Favoritos - - - Mis Favoritos - - - Vestuario actual - - - Vestuario inicial - - - Mis vestuarios - - - Accesorios - - - Redes - - - Amigos - - - Todas - - - No tienes puestos anexos - - - Anexos (quedan [COUNT] ranuras) - - - Comprar - - - Comprar por L$ - - - Piedra - - - Metal - - - Cristal - - - Madera - - - Carne - - - Plástico - - - Goma - - - Claridad - - - Mayúsculas - - - Ctrl - - - Tórax - - - Cráneo - - - Hombro izquierdo - - - Hombro derecho - - - Mano izq. - - - Mano der. - - - Pie izq. - - - Pie der. - - - Columna - - - Pelvis - - - Boca - - - Barbilla - - - Oreja izq. - - - Oreja der. - - - Ojo izq. - - - Ojo der. - - - Nariz - - - Brazo der. - - - Antebrazo der. - - - Brazo izq. - - - Antebrazo izq. - - - Cadera der. - - - Muslo der. - - - Pantorrilla der. - - - Cadera izq. - - - Muslo izq. - - - Pantorrilla izq. - - - Abdomen - - - Pecho izquierdo - - - Pecho derecho - - - Cuello - - - Centro del avatar - - - Punto de colocación no válido - - - [AGEYEARS] [AGEMONTHS] de edad - - - [AGEYEARS] de edad - - - [AGEMONTHS] de edad - - - [AGEWEEKS] de edad - - - [AGEDAYS] de edad - - - Registrado hoy - - - [COUNT] año - - - [COUNT] años - - - [COUNT] años - - - [COUNT] mes - - - [COUNT] meses - - - [COUNT] meses - - - [COUNT] semana - - - [COUNT] semanas - - - [COUNT] semanas - - - [COUNT] día - - - [COUNT] días - - - [COUNT] días - - - [COUNT] miembro - - - [COUNT] miembros - - - [COUNT] miembros - - - Residente - - - Prueba - - - Miembro fundador - - - Empleado de Linden Lab - - - Ha usado información sobre la forma de pago - - - Hay información archivada sobre la forma de pago - - - No hay información archivada sobre la forma de pago - - - Edad verificada - - - Edad no verificada - - - Centro 2 - - - Arriba der. - - - Arriba - - - Arriba izq. - - - Centro - - - Abajo izq. - - - Abajo - - - Abajo der. - - - Descargado, compilándolo - - - No se encuentra el script en el servidor. - - - Problema al descargar - - - Permisos insuficientes para descargar un script. - - - Permisos insuficientes para - - - Fallo desconocido en la descarga - - - Recompilando - - - recompilar - - - Progreso del reinicio - - - restaurar - - - Configurar según se ejecuta - - - Configurando según se ejecuta - - - Configurar sin ejecutar - - - Configurando sin ejecutarlo - - - ¡Compilación correcta! - - - Compilación correcta, guardando... - - - Guardado. - - - Script (objeto fuera de rango) - - - El objeto [OBJECT] es propiedad de [OWNER] - - - ninguno - - - - (Desconocido) - - - - - [mthnum,datetime,utc]/[day,datetime,utc]/[year,datetime,utc] - - - - - Saldo - - - Créditos - - - Débitos - - - Total - - - No se encontraron datos del grupo - - - parent estate - - - continente - - - teen - - - cualquiera - - - error - - - todos los estados propiedad de [OWNER] - - - todos los estados que posees - - - todos los estados que administras para [OWNER] - - - Resientes autorizados: ([ALLOWEDAGENTS], de un máx. de [MAXACCESS]) - - - Grupos autorizados: ([ALLOWEDGROUPS], de un máx. de [MAXACCESS]) - - - Memoria de los scripts de la parcela - - - Parcelas listadas: [PARCELS] - - - Memoria usada: [COUNT] kb de un máx de [MAX] kb; [AVAILABLE] kb disponibles - - - Memoria usada: [COUNT] kb - - - URLs de los scripts de la parcela - - - URLs usadas: [COUNT] de un máx. de [MAX]; [AVAILABLE] disponibles - - - URLs usadas: [COUNT] - - - Error al obtener la información - - - No hay una parcela seleccionada - - - Error: la información del script sólo está disponible en tu región actual - - - Obteniendo la información... - - - No tienes permiso para examinar esta parcela - - - Sentado en - - - Tórax - - - Cabeza - - - Hombro izquierdo - - - Hombro derecho - - - Mano izq. - - - Mano der. - - - Pie izq. - - - Pie der. - - - Anterior - - - Pelvis - - - Boca - - - Barbilla - - - Oreja izq. - - - Oreja der. - - - Ojo izq. - - - Ojo der. - - - Nariz - - - Brazo der. - - - Antebrazo der. - - - Brazo izq. - - - Antebrazo izq. - - - Cadera der. - - - Muslo der. - - - Pantorrilla der. - - - Cadera izq. - - - Muslo izq. - - - Pantorrilla izq. - - - Vientre - - - Pecho derecho - - - Pecho izquierdo - - - HUD: Centro 2 - - - HUD: arriba der. - - - HUD: arriba centro - - - HUD: arriba izq. - - - HUD: Centro 1 - - - HUD: abajo izq. - - - HUD: abajo - - - HUD: abajo der. - - - Línea [LINE], Columna [COLUMN] - - - [COUNT] resultados - - - Contenido del objeto - - - Script nuevo - - - El Residente al que has enviado un mensaje ha solicitado que no se le moleste porque está en modo ocupado. Podrá ver tu mensaje más adelante, ya que éste aparecerá en su panel de MI. - - - (Por el nombre) - - - (Residente) - - - (Objeto) - - - (Grupo) - - - (Externo) - - - No se ha aportado un contrato para este estado. - - - No se ha aportado un contrato para este estado. El terreno de este estado lo vende el propietario del estado, no Linden Lab. Por favor, contacta con ese propietario para informarte sobre la venta. - - - - - - Propiedad del grupo - - - Público - - - Configuración local - - - Configuración de la región - - - Clics: [TELEPORT] teleportes, [MAP] mapa, [PROFILE] perfil - - - (se actualizará tras la publicación) - - - No has creado destacados ni clasificados. Pulsa el botón Más para crear uno. - - - El usuario no tiene clasificados ni destacados - - - Cargando... - - - Vista previa - - - Propiedades - - - Un objeto de nombre - - - propiedad del grupo - - - propiedad de un grupo desconocido - - - propiedad de - - - propiedad de un usuario desconocido - - - te ha dado - - - Rechazas [DESC] de <nolink>[NAME]</nolink>. - - - Total - - - comprado - - - pagado a ti - - - pagado en - - - pase comprado a - - - cuotas pagadas para el evento - - - precio pagado por el evento - - - Saldo - - - Créditos - - - Débitos - - - [weekday,datetime,utc] [mth,datetime,utc] [day,datetime,utc], [year,datetime,utc] - - - Contenidos - - - Artículos adquiridos - - - Cancelar - - - Subir [NAME] cuesta [AMOUNT] L$ - - - Comprar esto cuesta [AMOUNT] L$ - - - Extensión de archivo desconocida [.%s] -Se esperaba .wav, .tga, .bmp, .jpg, .jpeg, o .bvh - - - Ignorar - - - Guardarme este hito... - - - Editar este hito... - - - ⌃ - - - ⌘ - - - ⌥ - - - ⇧ - - - Ctrl+ - - - Alt+ - - - Mayús+ - - - Archivo guardado - - - Recibiendo - - - AM - - - PM - - - PST - - - PDT - - - Adelante - - - Izquierda - - - Derecha - - - Atrás - - - Norte - - - Sur - - - Oeste - - - Este - - - Arriba - - - Abajo - - - Cualquier categoría - - - Compras - - - Terreno en alquiler - - - Propiedad en alquiler - - - Atracción especial - - - Nuevos productos - - - Empleo - - - Se busca - - - Servicios - - - Personal - - - Ninguno - - - Localización Linden - - - Adulto - - - Arte y Cultura - - - Negocios - - - Educativo - - - Juegos de azar - - - Entretenimiento - - - Para recién llegados - - - Parques y Naturaleza - - - Residencial - - - Artes escénicas - - - Otra - - - Terreno en alquiler - - - Cualquiera - - - Tú - - - Múltiples medias - - - Play/Pausa los media - - - Ha habido un error analizando la línea de comando. -Por favor, consulta: http://wiki.secondlife.com/wiki/Client_parameters -Error: - - - [APP_NAME] Uso de línea de comando: - - - [APP_NAME] no puede acceder a un archivo que necesita. - -Puede ser porque estés ejecutando varias copias, o porque tu sistema crea -equivocadamente- que el archivo está abierto. -Si este mensaje persiste, reinicia tu ordenador y vuelve a intentarlo. -Si aun así sigue apareciendo el mensaje, debes desinstalar completamente [APP_NAME] y reinstalarlo. - - - Error fatal - - - [APP_NAME] requiere un procesador con AltiVec (G4 o posterior). - - - [APP_NAME] ya se está ejecutando. -Revisa tu barra de tareas para encontrar una copia minimizada del programa. -Si este mensaje persiste, reinicia tu ordenador. - - - En su anterior ejecución, [APP_NAME] se congeló o se cayó. -¿Quieres enviar un informe de caída? - - - Alerta - - - [APP_NAME] no encuentra DirectX 9.0b o superior. -[APP_NAME] usa DirectX para detectar el hardware o los drivers no actualizados que pueden provocar problemas de estabilidad, ejecución pobre y caídas. Aunque puedes ejecutar [APP_NAME] sin él, recomendamos encarecidamente hacerlo con DirectX 9.0b. - -¿Quieres continuar? - - - ¡Atención! - - - Las actualizaciones automáticas no están todavía implementadas para Linux. -Por favor, descarga la última versión desde www.secondlife.com. - - - Fallo en RegisterClass - - - Error - - - No puede ejecutarse a pantalla completa de [WIDTH] x [HEIGHT]. -Ejecutándose en una ventana. - - - Error Shutdown destruyendo la ventana (DestroyWindow() failed) - - - Error Shutdown - - - No se puede construir el 'GL device context' - - - No se puede encontrar un formato adecuado de píxel - - - No se puede conseguir la descripción del formato de píxel - - - Para ejecutarse, [APP_NAME] necesita True Color (32-bit). -Por favor, en las configuraciones de tu ordenador ajusta el modo de color a 32-bit. - - - [APP_NAME] no puede ejecutarse porque no puede obtener un canal alpha de 8 bit. Generalmente, se debe a alguna cuestión de los drivers de la tarjeta de vídeo. -Por favor, comprueba que tienes instalados los últimos drivers para tu tarjeta de vídeo. -Comprueba también que tu monitor esta configurado para True Color (32-bit) en Panel de Control > Apariencia y temas > Pantalla. -Si sigues recibiendo este mensaje, contacta con [SUPPORT_SITE]. - - - No se puede configurar el formato de píxel - - - No se puede crear el 'GL rendering context' - - - No se puede activar el 'GL rendering context' - - - [APP_NAME] no puede ejecutarse porque los drivers de tu tarjeta de vídeo o no están bien instalados, o no están actualizados, o son para hardware no admitido. Por favor, comprueba que tienes los drivers más actuales para tu tarjeta de vídeo, y, aunque los tengas, intenta reinstalarlos. - -Si sigues recibiendo este mensaje, contacta con [SUPPORT_SITE]. - - - Barba del día - - - Blanco del todo - - - Ojos de cómic - - - Arqueadas - - - Brazos: longitud - - - Cortos - - - Lóbulos - - - Nuca: largo - - - Marcadas - - - Bangs - - - Ojos pequeños - - - Barriga: tamaño - - - Grande - - - Culo grande - - - Pelo: moño - - - Pelo: tupé - - - Pelo: melena alta - - - Cabeza grande - - - Grandes pectorales - - - Crestas grandes - - - Negro - - - Rubio - - - Pelo rubio - - - Colorete - - - Color del colorete - - - Opacidad del colorete - - - Definición del cuerpo - - - Cuerpo: gordura - - - Pecas del cuerpo - - - Cuerpo grueso - - - Cuerpo: grosor - - - Cuerpo delgado - - - Abiertas - - - Busto: firmeza - - - Busto: canalillo - - - Busto: tamaño - - - Puente: ancho - - - Aumentar - - - Arco ciliar - - - Bug Eyes - - - Ojos saltones - - - Bulbosa - - - Nariz de porra - - - Masa del busto - - - Suavizado del busto - - - Gravedad del busto - - - Aerodinámica del busto - - - Efecto máx. - - - Elasticidad - - - Ganancia - - - Amortiguación - - - Efecto máx. - - - Elasticidad - - - Ganancia - - - Amortiguación - - - Efecto máx. - - - Elasticidad - - - Ganancia - - - Amortiguación - - - Masa de la barriga - - - Suavizado de la barriga - - - Gravedad de la barriga - - - Aerodinámica de la barriga - - - Efecto máx. - - - Elasticidad - - - Ganancia - - - Amortiguación - - - Masa del culo - - - Suavizado del culo - - - Gravedad del culo - - - Aerodinámica del culo - - - Efecto máx. - - - Elasticidad - - - Ganancia - - - Amortiguación - - - Efecto máx. - - - Elasticidad - - - Ganancia - - - Amortiguación - - - Cejijuntas - - - Pelo tupido - - - Culo: tamaño - - - Gravedad del culo - - - Polisón - - - Sin polisón - - - Con polisón - - - Cortito - - - Pómulos - - - Tórax: tamaño - - - Barbilla: ángulo - - - Barbilla: contorno - - - Barba en collar - - - Barbilla: largo - - - Hacia la barbilla - - - Barbilla retraída - - - Barbilla prominente - - - Papada - - - Transparente - - - Remarcar - - - Ojos juntos - - - Cerrar - - - Trasera cerrada - - - Frontal cerrado - - - Cerrada - - - Cerrada - - - Poco abultada - - - Espalda - - - Escote - - - Hacia abajo - - - Hacia arriba - - - Caídos - - - Nariz torcida - - - Acampanado - - - Oscuridad - - - Verde oscuro - - - Más oscuros - - - Remarcar - - - Tacones por defecto - - - Densas - - - Mucha papada - - - Poco - - - Muy abultada - - - Orejas: ángulo - - - Orejas: tamaño - - - Orejas: forma - - - Cabeza: ahuevada - - - Ojos: bolsas - - - Ojos: color - - - Ojos: profundidad - - - Ojos: brillo - - - Ojos: apertura - - - Ojos: simetría - - - Ojos: tamaño - - - Ojos: separación - - - Cejas: arco - - - Cejas: densidad - - - Cejas: altura - - - Cejas: en V - - - Cejas: tamaño - - - Pestañas: longitud - - - Contorno de ojos - - - Contorno de ojos: color - - - Eyes Bugged - - - Cara: simetría - - - Rasgos marcados - - - Ojos separados - - - Prominentes - - - Mujer - - - Sin dedos - - - Con dedos - - - Campana - - - Redondeadas - - - Culo plano - - - Cabeza plana - - - Empeine bajo - - - Pie: tamaño - - - Frente: ángulo - - - Hacia la frente - - - Pecas - - - Flequillo - - - Sin cortar - - - Contorno completo - - - Sin cortar - - - Pelo: volumen a los lados - - - Volumen total - - - Con brillo - - - Guantes: dedos - - - Guantes: largo - - - Pelo - - - Pelo: nuca - - - Pelo: delante - - - Pelo: lados - - - Peinado: dirección - - - Pelo: espesor - - - Pelo: espesor - - - Pelo: inclinación - - - A la izq. - - - A la der. - - - Pelo: volumen - - - Manos: tamaño - - - Muy largo - - - Cabeza: longitud - - - Cabeza: forma - - - Cabeza: tamaño - - - Cabeza: estiramiento - - - Tacón: altura - - - Tacón: forma - - - Altura - - - Subir - - - Tacones altos - - - Mandíbula alta - - - Suela gorda - - - Pegada - - - Arrriba - - - Cadera: altura - - - Cadera: ancho - - - Pegadas - - - Línea de ojos: color - - - Línea de ojos: opacidad - - - Ojos: lagrimal - - - Inner Eye Shadow - - - Línea de ojos - - - Chaqueta: largo - - - Chaqueta: arrugas - - - Mandíbula: ángulo - - - Maxilar inferior - - - Mandíbula: forma - - - Más junto - - - Mofletes - - - Rodillas: ángulo - - - Zambas - - - Aumentar - - - Manos grandes - - - Raya: izq. - - - Piernas: longitud - - - Piernas: musculatura - - - Menos - - - Menos gordura - - - Menos tupida - - - Menos pecas - - - Menos grosor - - - Más levantado - - - Menos michelines - - - Pocos músculos - - - Poca musculatura - - - Menos sonrosada - - - Menos redondeada - - - Menos cartucheras - - - Menos cuadrada - - - Menos volumen - - - Pequeña - - - Más luminosos - - - Labio: hoyuelo - - - Hoyuelo marcado - - - Labios: grosor - - - Labios sonrosados - - - Labios: ratio - - - Labios: prominencia - - - Labios: ancho - - - Brillo de labios - - - Barra de labios - - - Barra de labios: color - - - Más - - - Cabeza alargada - - - Cadera larga - - - Piernas largas - - - Cuello largo - - - Coletas largas - - - Cola de caballo larga - - - Torso largo - - - Brazos largos - - - Pantalón suelto - - - Camiseta suelta - - - Puños anchos - - - Michelines - - - Bajar - - - Tacones bajos - - - Mandíbula baja - - - Suela fina - - - Suelta - - - Abajo - - - Puente: abajo - - - Mejillas: abajo - - - Varón - - - Raya: en medio - - - Más - - - Más colorete - - - Más gordura - - - Más tupida - - - Más - - - Más pecas - - - Más grosor - - - Menos levantado - - - Más barra de labios - - - Más michelines - - - Más el inferior - - - Más músculos - - - Más musculatura - - - Más sonrosada - - - Más redondeada - - - Más cartucheras - - - Más inclinada - - - Más cuadrada - - - Más el superior - - - Más recta - - - Más volumen - - - Grande - - - Bigote - - - Comisuras - - - Boca: posición - - - Rapado - - - Muscular - - - Patillas largas - - - Uñas pintadas - - - Uñas pintadas: color - - - Disminuir - - - Rapada - - - Entradas - - - Labios estrechos - - - Natural - - - Cuello: longitud - - - Cuello: grosor - - - Sin colorete - - - Sin contorno - - - Menos - - - Sin brillo - - - Sin barra de labios - - - Sin raya - - - Sin pintar - - - Nada - - - Sin crestas - - - Sin blanco - - - Sin arrugas - - - Normal Lower - - - Normal Upper - - - Nariz a la izq. - - - Nariz a la der. - - - Nariz: tamaño - - - Nariz: grosor - - - Nariz: respingona - - - Nariz: punta - - - Nariz: ancho - - - Ventana: altura - - - Ventana: ancho - - - Opaco - - - Abrir - - - Apertura trasera - - - Apertura frontal - - - Abierta - - - Abierta - - - Anaranjado - - - De soplillo - - - Sombra de ojos: color - - - Sombra de ojos: opacidad - - - Ojos: comisura - - - Outer Eye Shadow - - - Sombra de ojos - - - Retraído - - - Pubis - - - Pintadas - - - Pálida - - - Pantalón: cruz - - - Ceñido - - - Pernera: largo - - - Caja - - - Pantalón: arrugas - - - Raya - - - Flequillo partido - - - Pectorales - - - Tono - - - Coletas - - - Rosa - - - Más sonrosados - - - Suela: altura - - - Suela: ancho - - - En punta - - - De aguja - - - Cola de caballo - - - Con vuelo - - - Izquierdo más grande - - - Derecho más grande - - - Hinchadas - - - Ojeras - - - Irisación - - - Pelirrojo - - - Regular - - - Raya: der. - - - Tez sonrosada - - - Redondear - - - Rubicundez - - - Rojiza - - - Pelo encrespado - - - Cartucheras - - - Piernas flacas - - - Más ancho - - - Sin marcar - - - Nuca: corte - - - Shear Face - - - Shear Front - - - Arriba - izq. - - - Arriba - der. - - - Rapada - - - Rapada - - - A la izq. - - - Boca: ladeada - - - A la der. - - - Alto de cintura - - - Ceñido - - - Camisa: arrugas - - - Caña: altura - - - Menos - - - Brazos cortos - - - Piernas cortas - - - Cuello corto - - - Coletas cortas - - - Cola de caballo corta - - - Patillas cortas - - - Torso corto - - - Cadera corta - - - Hombros - - - Lados: franja - - - Patillas - - - Pelo: lados - - - Bajar lados del pelo - - - Subir lados del pelo - - - Cuello estrecho - - - Falda: vuelo - - - Falda: largo - - - Slanted Forehead - - - Largo de manga - - - Ancho de puños - - - Raja trasera - - - Raja frontal - - - Raja a la izq. - - - Raja a la der. - - - Disminuir - - - Manos pequeñas - - - Cabeza pequeña - - - Leves - - - Pelo liso - - - Calcetines: largo - - - Perilla - - - Depiladas - - - Crestas - - - Cuadrada - - - Punta cuadrada - - - Cabeza aplastada - - - Cabeza estirada - - - Chupadas - - - Estrecho de pecho - - - Ojos hundidos - - - Sweep Back - - - Sweep Forward - - - Más - - - Cubierta trasera - - - Cubierta frontal - - - Tacones grandes - - - Cuello ancho - - - Empeine alto - - - Delgadas - - - Cejas finas - - - Hacia dentro - - - Nariz fina - - - Poca papada - - - Sin campana - - - Pantalón ceñido - - - Camisa ceñida - - - Falda ceñida - - - Puños ceñidos - - - Punta: forma - - - Empeine - - - Torso: longitud - - - Torso: musculatura - - - Torso flacucho - - - Largos - - - Abiertos - - - Prognatismo - - - No natural - - - Puente: arriba - - - Mejillas: arriba - - - Barbilla: prominencia - - - Párpados - - - Mucho - - - Del todo - - - Cintura - - - Mofletes - - - Pelo blanco - - - Aumentar - - - Completa - - - Completa - - - Labios anchos - - - Total - - - Arrugas - - - Añadir a mis hitos - - - Editar mis hitos - - - Ver más información de esta localización - - - Historial de mis localizaciones - - - Comprar este terreno - - - Región Adulta - - - Región Moderada - - - Región General - - - Los avatares están visibles y está permitido el chat fuera de esta parcela - - - Los objetos que se mueven pueden presentar un comportamiento incorrecto en la región hasta que ésta se recargue. - - - Esta región no tiene activado el pathfinding dinámico. - - - Actualizar [APP_NAME] - - - Actualizando [APP_NAME]... - - - Instalando [APP_NAME]... - - - Tu visor [APP_NAME] se está actualizando a la última versión. Llevará algún tiempo, paciencia. - - - Descargando la actualización... - - - Descargando la actualización - - - Fallo en la descarga de la actualización - - - Ha habido un error actualizando [APP_NAME]. Por favor, descarga la última versión desde www.secondlife.com. - - - Fallo al instalar la actualización - - - Fallo al iniciar el visor - - - [APP_NAME]: Los ítems se reciben muy rápido de [FROM_NAME]; desactivada la vista previa automática durante [TIME] sgs. - - - [APP_NAME]: Los ítems se reciben muy rápido; desactivada la vista previa automática durante [TIME] sgs. - - - -- Activado el registro de los mensajes instantáneos -- - - - [NAME] está escribiendo... - - - (sin nombre) - - - (Moderado: por defecto, desactivada la voz) - - - Para esta llamada no está disponible el chat de texto. - - - Un moderador del grupo ha desactivado tu chat de texto. - - - Pulsa aquí para enviar un mensaje instantáneo. - - - A - - - (Moderador) - - - (Guardado [LONG_TIMESTAMP]) - - - Han respondido a tu llamada - - - Has iniciado una llamada de voz - - - Has entrado en la llamada de voz - - - [NAME] inició una llamada de voz - - - Haciendo la llamada de voz... - - - Conectado, pulsa Colgar para salir - - - Se colgó la llamada de voz - - - Conferencia con [AGENT_NAME] - - - Ofrecido el item del inventario - - - Arrastra los ítems desde el invenbtario hasta aquí - - - (La sesión de MI no existe) - - - Usted es el único usuario en esta sesión. - - - [NAME] está desconectado. - - - Pulse el botón [BUTTON NAME] para aceptar/conectar este chat de voz. - - - Has ignorado a este residente. Enviándole un mensaje, automáticamente dejarás de ignorarle. - - - Error en lo solicitado, por favor, inténtalo más tarde. - - - Error al hacer lo solicitado; por favor, inténtelo más tarde. - - - Usted no tiene permisos suficientes. - - - La sesión ya acabó - - - Usted no tiene esa capacidad. - - - Usted no tiene esa capacidad. - - - Usted no es un moderador de la sesión. - - - Un moderador del grupo ha desactivado tu chat de texto. - - - Un moderador del grupo le ha desactivado el chat de texto. - - - No se ha podido añadir usuarios a la sesión de chat con [RECIPIENT]. - - - No se ha podido enviar tu mensaje a la sesión de chat con [RECIPIENT]. - - - No se ha podido enviar su mensaje a la sesión de chat con [RECIPIENT]. - - - Error moderando. - - - Se te ha sacado del grupo. - - - Ha sido eliminado del grupo. - - - Usted ya no tendrá más la capacidad de estar en la sesión de chat. - - - [SOURCES] ha dicho algo nuevo - - - [SOURCES] ha dicho algo nuevo - - - Se ha agotado el tiempo del inicio de sesión - - - Posición inicial establecida. - - - http://secondlife.com/landing/voicemorphing - - - [NAME] te ha pagado [AMOUNT] L$ [REASON]. - - - [NAME] te ha pagado [AMOUNT] L$. - - - Has pagado [AMOUNT] L$ a [NAME] por [REASON]. - - - Has pagado[AMOUNT] L$ - - - Has pagado [AMOUNT] L$ a [NAME]. - - - Has pagado [AMOUNT] L$ por [REASON]. - - - No has pagado a [NAME] [AMOUNT] L$ [REASON]. - - - No has pagado [AMOUNT] L$. - - - No has pagado a [NAME] [AMOUNT] L$. - - - No has pagado [AMOUNT] L$ [REASON]. - - - para [ITEM] - - - para una parcela de terreno - - - para un pase de acceso a terrenos - - - for deeding land - - - para crear un grupo - - - para entrar a un grupo - - - to upload - - - para publicar un anuncio clasificado - - - Dando [AMOUNT] L$ - - - Subir esto cuesta [AMOUNT] L$ - - - Esto cuesta [AMOUNT] L$ - - - Compra del terreno seleccionado por [AMOUNT] L$ - - - Este objeto cuesta [AMOUNT] L$ - - - Todos - - - Oficiales - - - Propietarios - - - Conectado/a - - - Subiendo... - -Denuncia de infracción - - - Anatomía nueva - - - Piel nueva - - - Pelo nuevo - - - Ojos nuevos - - - Camisa nueva - - - Pantalón nuevo - - - Zapatos nuevos - - - Calcetines nuevos - - - Chaqueta nueva - - - Guantes nuevos - - - Camiseta nueva - - - Ropa interior nueva - - - Falda nueva - - - Nueva Alfa - - - Tatuaje nuevo - - - Nueva física - - - No se puede poner - - - Gesto nuevo - - - Script nuevo - - - Nota nueva - - - Carpeta nueva - - - Contenidos - - - Gestos - - - Gestos de hombre - - - Gestos de mujer - - - Otros gestos - - - Gestos al hablar - - - Gestos corrientes - - - Varón - Disculpa - - - Varón – Déjame en paz - - - Varón - Lanzar un beso - - - Varón - Abucheo - - - Varón - Aburrido - - - Varón – ¡Eh! - - - Varón - Risa - - - Varón - Rechazo - - - Varón - Encogimiento de hombros - - - Varón - Sacando la lengua - - - Varón - Admiración - - - Mujer - Risa suave - - - Mujer - Llorar - - - Mujer - Ruborizada - - - Mujer - Disculpa - - - Mujer – Déjame en paz - - - Mujer - Lanzar un beso - - - Mujer - Abucheo - - - Mujer - Aburrida - - - Mujer - ¡Eh! - - - Mujer - ¡Eh, encanto! - - - Mujer - Risa - - - Mujer - Buen aspecto - - - Mujer - Por aquí - - - Mujer - Por favor - - - Mujer - Rechazo - - - Mujer - Encogimiento de hombros - - - Mujer - Sacando la lengua - - - Mujer - Admiración - - - /reverencia - - - /aplaudir - - - /contar - - - /apagar - - - /bmc - - - /músculo - - - /no - - - /¡no! - - - /papel - - - /señalarme - - - /señalarte - - - /piedra - - - /tijera - - - /fumar - - - /estirar - - - /silbar - - - /sí - - - /¡sí! - - - ausente - - - baile1 - - - baile2 - - - baile3 - - - baile4 - - - baile5 - - - baile6 - - - baile7 - - - baile8 - - - [day,datetime,slt]/[mthnum,datetime,slt]/[year,datetime,slt] - - - ninguno/ninguno - - - No se puede subir imágenes mayores de [WIDTH]*[HEIGHT] - - - - Parece que hay algún problema que ha escapado a nuestros controles. - - Visita status.secondlifegrid.net para ver si hay alguna incidencia conocida que esté afectando al servicio. - Si sigues teniendo problemas, comprueba la configuración de la red y del servidor de seguridad. - - - Domingo:Lunes:Martes:Miércoles:Jueves:Viernes:Sábado - - - Dom:Lun:Mar:Mié:Jue:Vie:Sáb - - - Enero:Febrero:Marzo:Abril:Mayo:Junio:Julio:Agosto:Septiembre:Octubre:Noviembre:Diciembre - - - Ene:Feb:Mar:Abr:May:Jun:Jul:Ago:Sep:Oct:Nov:Dic - - - [MDAY] - - - AM - - - PM - - - [AMOUNT] US$ - - - Membresía - - - Roles - - - Indentidad de grupo - - - Gestión de la parcela - - - Identidad de la parcela - - - Configuración de la parcela - - - Poder de la parcela - - - Acceso a la parcela - - - Contenido de la parcela - - - Manejo de objetos - - - Contabilidad - - - Avisos - - - Chat - - - ¿Deseas eliminar los elementos seleccionados? - - - ¿Deseas eliminar el elemento seleccionado? - - - No hay elementos en este vestuario - - - Selecciona un editor mediante la configuración de ExternalEditor. - - - No se encuentra el editor externo especificado. -Inténtalo incluyendo la ruta de acceso al editor entre comillas -(por ejemplo, "/ruta a mi/editor" "%s"). - - - Error al analizar el comando de editor externo. - - - Error al ejecutar el editor externo. - - - Error al traducir: [REASON] - - - Error al analizar la respuesta de la traducción. - - - Esc - - - Space - - - Enter - - - Tab - - - Ins - - - Del - - - Backsp - - - Shift - - - Ctrl - - - Alt - - - CapsLock - - - Base - - - End - - - PgUp - - - PgDn - - - F1 - - - F2 - - - F3 - - - F4 - - - F5 - - - F6 - - - F7 - - - F8 - - - F9 - - - F10 - - - F11 - - - F12 - - - Añadir - - - Restar - - - Multiplicar - - - Dividir - - - PAD_DIVIDE - - - PAD_LEFT - - - PAD_RIGHT - - - PAD_DOWN - - - PAD_UP - - - PAD_HOME - - - PAD_END - - - PAD_PGUP - - - PAD_PGDN - - - PAD_CENTER - - - PAD_INS - - - PAD_DEL - - - PAD_Enter - - - PAD_BUTTON0 - - - PAD_BUTTON1 - - - PAD_BUTTON2 - - - PAD_BUTTON3 - - - PAD_BUTTON4 - - - PAD_BUTTON5 - - - PAD_BUTTON6 - - - PAD_BUTTON7 - - - PAD_BUTTON8 - - - PAD_BUTTON9 - - - PAD_BUTTON10 - - - PAD_BUTTON11 - - - PAD_BUTTON12 - - - PAD_BUTTON13 - - - PAD_BUTTON14 - - - PAD_BUTTON15 - - - - - - - = - - - ` - - - ; - - - [ - - - ] - - - \ - - - 0 - - - 1 - - - 2 - - - 3 - - - 4 - - - 5 - - - 6 - - - 7 - - - 8 - - - 9 - - - A - - - B - - - C - - - D - - - E - - - F - - - G - - - H - - - I - - - J - - - K - - - L - - - M - - - N - - - O - - - P - - - Q - - - R - - - S - - - T - - - U - - - V - - - W - - - X - - - Y - - - Z - - - Viendo balizas de partículas (azules) - - - Viendo balizas de objetos materiales (verdes) - - - Viendo balizas de objetos con script (rojas) - - - Viendo el objeto con script con balizas de función táctil (rojas) - - - Viendo balizas de sonido (amarillas) - - - Viendo balizas de medios (blancas) - - - Ocultando las partículas - - - Acerca del terreno - - - Apariencia - - - Avatar - - - Construir - - - Chat - - - Brújula - - - Destinos - - - Gestos - - - Cómo - - - Inventario - - - Mapa - - - Mercado - - - Minimapa - - - Caminar / Correr / Volar - - - Buzón de salida de comerciante - - - Gente - - - Destacados - - - Lugares - - - Preferencias - - - Perfil - - - Buscar - - - Foto - - - Hablar - - - Controles de la cámara - - - Configuración de voz - - - Información sobre el terreno que vas a visitar - - - Cambiar tu avatar - - - Elegir un avatar completo - - - Construir objetos y modificar la forma del terreno - - - Habla por chat de texto con las personas próximas - - - Brújula - - - Destinos de interés - - - Gestos para tu avatar - - - Cómo hacer las tareas habituales - - - Ver y usar tus pertenencias - - - Mapa del mundo - - - Ir de compras - - - Mostrar la gente que está cerca - - - Desplazando el avatar - - - Transfiere objetos a tu mercado para venderlos - - - Amigos, grupos y personas próximas - - - Lugares que se mostrarán como favoritos en tu perfil - - - Lugares que has guardado - - - Preferencias - - - Consulta o edita tu perfil - - - Buscar lugares, eventos y personas - - - Tomar una fotografía - - - Utiliza el micrófono para hablar con las personas próximas - - - Cambiando el ángulo de la cámara - - - Controles de volumen para las llamadas y la gente que se encuentre cerca de ti en el mundo virtual - - - actualmente en tu barra de herramientas inferior - - - actualmente en tu barra de herramientas izquierda - - - actualmente en tu barra de herramientas derecha - - - % retención - - - Detalle - - - Mejor detalle - - - Superficie - - - Sólido - - - Envoltura - - - Vista previa - - - Normal - - - http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer - - - Ninguno - - - Afecta al navmesh - - - Personaje - - - (Múltiple) - - - Muy bajo - - - Bajo - - - Medio - - - Alto - - - Muy alto - - - El Residente no puede visitar esta región. - - - [Usuario] - - diff --git a/indra/newview/skins/steam/xui/fr/strings.xml b/indra/newview/skins/steam/xui/fr/strings.xml deleted file mode 100644 index dcb25357bc..0000000000 --- a/indra/newview/skins/steam/xui/fr/strings.xml +++ /dev/null @@ -1,5054 +0,0 @@ - - - - - Second Life - - - Second Life - - - SECOND LIFE - - - Grille de Second Life - - - Portail Assistance Second Life - - - Détection du matériel... - - - Chargement de [APP_NAME]... - - - Vidage du cache... - - - Initialisation du cache des textures... - - - Initialisation VFS... - - - Échec d'initialisation des graphiques. Veuillez mettre votre pilote graphique à jour. - - - Restauration... - - - Changement de la résolution... - - - Fullbright (Legacy) - - - La connexion à [APP_NAME] apparaît peut-être comme étant gelée. Veuillez patienter. - - - Connexion... - - - Authentification en cours - - - Maintenance du compte en cours… - - - La tentative de connexion précédente a échoué. Connexion, esssai [NUMBER] - - - Monde en cours de chargement… - - - Navigateur Web incorporé en cours d'initialisation… - - - Multimédia en cours d'initialisation… - - - Chargement des polices en cours... - - - Fichiers du cache en cours de vérification (peut prendre 60-90 s)... - - - Réponse en cours de traitement… - - - Monde en cours d'initialisation… - - - Décodage des images en cours... - - - Quicktime en cours d'initialisation - - - Quicktime introuvable, impossible de procéder à l'initialisation. - - - Initialisation de Quicktime réussie. - - - Capacités de la région demandées... - - - Capacités de la région demandées... Tentative n° [NUMBER]. - - - Liaison avec la région en cours de création... - - - Connexion avec la région en cours... - - - Habits en cours de téléchargement... - - - Certificat non valide ou corrompu renvoyé par le serveur. Contactez l'administrateur de la grille. - - - Nom d'hôte non valide utilisé pour accéder au serveur. Vérifiez votre nom d'hôte de grille ou SLURL. - - - Il semble que le certificat renvoyé par la grille ait expiré. Vérifiez votre horloge système ou contactez l'administrateur de la grille. - - - Impossible d'utiliser le certificat renvoyé par le serveur pour SSL. Contactez l'administrateur de la grille. - - - Certificats trop nombreux dans la chaîne des certificats du serveur. Contactez l'administrateur de la grille. - - - Impossible de vérifier la signature de certificat renvoyée par le serveur de la grille. Contactez l'administrateur de la grille. - - - Erreur réseau : impossible d'établir la connexion. Veuillez vérifier votre connexion réseau. - - - Échec de la connexion. - - - Quitter - - - http://join.secondlife.com/index.php?sourceid=1206_steam&lang=fr-FR - - - Le client que vous utilisez ne permet plus d'accéder à Second Life. Téléchargez un nouveau client à la page suivante : -http://secondlife.com/download - -Pour plus d'informations, consultez la page FAQ ci-dessous : -http://secondlife.com/viewer-access-faq - - - Mise à jour facultative du client disponible : [VERSION] - - - Mise à jour du client requise : [VERSION] - - - L'agent est déjà connecté. - - - Désolé ! La connexion a échoué. -Veuillez vérifier que les éléments ci-dessous ont été correctement saisis : - * Nom d'utilisateur (par exemple, bobsmith12 ou steller.sunshine) - * Mot de passe -Assurez-vous également que la touche Verr. maj n'est pas activée. - - - Votre mot de passe a été modifié pour des raisons de sécurité. -Veuillez accéder à votre compte à la page http://secondlife.com/password -et répondre à la question de sécurité afin de réinitialiser votre mot de passe. -Nous vous prions de nous excuser pour la gêne occasionnée. - - - Vous allez devoir réinitialiser votre mot de passe suite à quelques changements effectués sur notre système. -Pour cela, accédez à votre compte à la page http://secondlife.com/password -et répondez à la question de sécurité. Votre mot de passe sera réinitialisé. -Nous vous prions de nous excuser pour la gêne occasionnée. - - - Second Life est temporairement fermé pour des raisons de maintenance. -Seuls les employés peuvent actuellement y accéder. -Consultez la page www.secondlife.com/status pour plus d'informations. - - - Les connexions à Second Life sont temporairement limitées afin de s'assurer que l'expérience des utilisateurs présents dans le monde virtuel soit optimale. - -Les personnes disposant de comptes gratuits ne pourront pas accéder à Second Life pendant ce temps afin de permettre à celles qui ont payé pour pouvoir utiliser Second Life de le faire. - - - Impossible d'accéder à Second Life depuis cet ordinateur. -Si vous pensez qu'il s'agit d'une erreur, contactez -l'Assistance à l'adresse suivante : support@secondlife.com. - - - Votre compte est inaccessible jusqu'à -[TIME], heure du Pacifique. - - - Nous n'avons pas réussi à traiter votre demande. -Pour obtenir de l'aide, veuillez contacter l'Assistance Second Life à la page suivante : http://secondlife.com/support. -Si vous ne parvenez pas à changer de mot de passe, veuillez appeler le (866) 476-9763. - - - Incohérence des données lors de la connexion. -Veuillez contacter support@secondlife.com. - - - Des opérations de maintenance mineures sont actuellement effectuées sur votre compte. -Votre compte est inaccessible jusqu'à -[TIME], heure du Pacifique. -Si vous pensez qu'il s'agit d'une erreur, contactez l'Assistance à l'adresse suivante : support@secondlife.com - - - Le simulateur a renvoyé une erreur en réponse à la demande de déconnexion. - - - Le système est en train de vous déconnecter. -Votre compte sera indisponible jusqu'à -[TIME], heure du Pacifique. - - - Impossible de créer de session valide. - - - Impossible de se connecter à un simulateur. - - - Votre compte permet uniquement d'accéder à Second Life -entre [START] et [END], heure du Pacifique. -Veuillez réessayer au cours de la période indiquée. -Si vous pensez qu'il s'agit d'une erreur, contactez l'Assistance à l'adresse suivante : support@secondlife.com - - - Paramètres incorrects. -Si vous pensez qu'il s'agit d'une erreur, contactez l'Assistance à l'adresse suivante : support@secondlife.com - - - Le paramètre Prénom doit être alphanumérique. -Si vous pensez qu'il s'agit d'une erreur, contactez l'Assistance à l'adresse suivante : support@secondlife.com - - - Le paramètre Nom doit être alphanumérique. -Si vous pensez qu'il s'agit d'une erreur, contactez l'Assistance à l'adresse suivante : support@secondlife.com - - - La région est en train d'être mise hors ligne. -Veuillez réessayer de vous connecter dans une minute. - - - Agent absent de la région. -Veuillez réessayer de vous connecter dans une minute. - - - Une autre session était en cours d'ouverture au sein de la région. -Veuillez réessayer de vous connecter dans une minute. - - - La session précédente était en cours de fermeture au sein de la région. -Veuillez réessayer de vous connecter dans une minute. - - - Fermeture de la session précédente toujours en cours pour la région. -Veuillez réessayer de vous connecter dans une minute. - - - Dernière session fermée au sein de la région. -Veuillez réessayer de vous connecter dans une minute. - - - Processus de déconnexion commencé pour la région. -Veuillez réessayer de vous connecter dans une minute. - - - Le système a commencé à fermer votre dernière session. -Veuillez réessayer de vous connecter dans une minute. - - - Il y a peut-être des problèmes techniques dans cette région. Veuillez vérifier votre connexion Internet. - - - Enregistrement des paramètres... - - - Déconnexion... - - - Arrêt en cours... - - - Vous avez été déconnecté de la région où vous étiez. - - - Vous avez été transféré vers une région non valide. - - - Test de déconnexion du client - - - Personne - - - (pas de nom) - - - Propriétaire : - - - Public - - - (Groupe) - - - À vendre : [AMOUNT] L$ - - - Contruction de groupe - - - Pas de construction - - - Contruction de groupe - - - Non sécurisé - - - Interdiction de voler - - - Scripts de groupe - - - Pas de scripts - - - Terrain : - - - Impossible de faire glisser plus d'un objet ici - - - - Impossible de rezzer des articles dans la boîte d'envoi vendeur - - - Impossible de vendre ou de transférer un ou plusieurs de ces objets. - - - La boîte d'envoi vendeur n'accepte que les articles directement issus de votre inventaire. - - - Impossible de placer des articles que vous portez dans votre boîte d'envoi vendeur - - - Impossible de placer des cartes de visite dans votre boîte d'envoi vendeur - - - Il existe plus de 3 niveaux de dossiers imbriqués. - - - Le dossier de niveau supérieur contient plus de 20 sous-dossiers. - - - Le dossier de niveau supérieur contient plus de 200 articles. - - - Impossible de déplacer un dossier vers son enfant - - - Impossible de déplacer un dossier vers lui-même - - - Cliquez pour afficher cette page web - - - Cliquez pour en savoir plus sur cet endroit - - - Cliquez pour afficher le profil de ce résident - - - En savoir plus sur ce résident - - - Cliquer pour ignorer ce résident - - - Cliquer pour ne plus ignorer ce résident - - - Cliquer pour envoyer un IM à ce résident - - - Cliquer pour payer ce résident - - - Cliquer pour proposer une téléportation à ce résident - - - Cliquer pour demander à ce résident d'être votre ami - - - Cliquez pour afficher la description de ce groupe - - - Cliquez pour afficher la description de cet événement - - - Cliquez pour afficher cette petite annonce - - - Cliquez pour afficher la description de cette parcelle - - - Cliquez pour vous téléporter à cet endroit - - - Cliquez pour afficher la description de cet objet - - - Cliquez pour voir cet emplacement sur la carte - - - Cliquez pour exécuter la commande secondlife:// - - - - Me téléporter vers - - - Afficher la carte pour - - - Ignorer - - - Ne plus ignorer - - - IM - - - Payer - - - Proposer une téléportation à - - - Demande d'amitié - - - Fermer (⌘W) - - - Fermer (Ctrl+W) - - - Fermer - - - Restaurer - - - Minimiser - - - Réduire - - - Attacher - - - Afficher l'aide - - - Recherche... - - - Aucun résultat. - - - En cours d'extraction... - - - Notes de version - - - http://wiki.secondlife.com/wiki/Release_Notes/ - - - Chargement... - - - (personne) - - - (en attente) - - - (multiple) - - - (aucun) - - - Appelant Avaline [ORDER] - - - Aucune erreur - - - Requête de l'actif : échec - - - Requête de l'actif : fichier inexistant - - - Requête de l'actif : actif introuvable dans la base de données - - - Fin du ficher - - - Impossible d'ouvrir le fichier - - - Fichier introuvable - - - Délai d'attente du transfert du fichier dépassé - - - Disparition du circuit - - - Il y a une différence de prix entre le client et le serveur - - - Statut inconnu - - - texture - - - son - - - carte de visite - - - repère - - - script (ancienne version) - - - habits - - - objet - - - note - - - dossier - - - racine - - - script LSL2 - - - bytecode LSL - - - texture tga - - - partie du corps - - - photo - - - Objets trouvés - - - image targa - - - Corbeille - - - image jpeg - - - animation - - - geste - - - simstate - - - favori - - - lien - - - lien du dossier - - - maillage - - - (Apparence en cours de modification) - - - Absent - - - Occupé - - - Bloqué(e) - - - Effrayé - - - En colère - - - Absent - - - Salto arrière - - - Rire en se tenant le ventre - - - Grand sourire - - - Envoyer un baiser - - - Bailler d'ennui - - - S'incliner - - - Applaudir - - - Révérence de cour - - - Pleurer - - - Danse 1 - - - Danse 2 - - - Danse 3 - - - Danse 4 - - - Danse 5 - - - Danse 6 - - - Danse 7 - - - Danse 8 - - - Mépris - - - Boire - - - Gêne - - - Désapprobation - - - Victoire - - - Yoga - - - Froncer les sourcils - - - Impatient - - - Sauter de joie - - - Va te faire voir ! - - - Envoyer un baiser - - - Rire - - - Montrer ses muscles - - - Non (mécontent) - - - Non - - - Na na na na nère - - - Gauche-droite - - - Bouche ouverte - - - Paix - - - Montrer quelqu'un du doigt - - - Se montrer du doigt - - - Gauche - - - Droite - - - Compter (pierre-papier-ciseaux) - - - Papier (pierre-papier-ciseaux) - - - Pierre (pierre-papier-ciseaux) - - - Ciseaux (pierre-papier-ciseaux) - - - Dégoût - - - Coup de pied circulaire - - - Triste - - - Salut - - - Crier - - - Hausser les épaules - - - Sourire - - - Fumer, immobile - - - Fumer, prendre une bouffée - - - Fumer, jeter son mégot - - - Surprise - - - Coup d'épée - - - Caprice - - - Tirer la langue - - - Faire signe - - - Chuchoter - - - Siffler - - - Clin d'œil - - - Clin d'œil (Hollywood) - - - Soucis - - - Oui (Joie) - - - Oui - - - Multiples - - - Chargement... - - - Hors ligne - - - [AREA] m² [PRICE] L$ - - - Aucun résultat. - - - OK - - - Fichier incomplet - - - Impossible de trouver ROOT ou JOINT. - - - chuchote : - - - crie : - - - Connexion au chat vocal du Monde en cours… - - - Connecté(e) - - - Voix non disponible à l'endroit où vous êtes - - - Déconnecté du chat vocal - - - Vous allez maintenant être reconnecté(e) au chat vocal près de vous. - - - '[OBJECTNAME]', un objet appartenant à [OWNERNAME], situé dans [REGIONNAME] à [REGIONPOS], a reçu le droit de : [PERMISSIONS]. - - - '[OBJECTNAME]', un objet appartenant à [OWNERNAME], situé dans [REGIONNAME] à [REGIONPOS], n'a pas reçu le droit de : [PERMISSIONS]. - - - Si vous autorisez un accès à votre compte, vous autorisez également l'objet à : - - - Débiter vos Linden dollars (L$) - - - Utiliser vos touches de commandes - - - Reconfigurer vos touches de commandes - - - Animer votre avatar - - - Attacher à votre avatar - - - Passer l'objet dans le domaine public (sans propriétaire) - - - Lier et délier d'autres objets - - - Créer et supprimer des liens avec d'autres objets - - - Modifier ses droits - - - Suivre votre caméra - - - Contrôler votre caméra - - - Vous téléporter - - - Pas connecté(e) - - - Général - - - Modéré - - - Adulte - - - Hors ligne - - - Inconnu - - - (inconnu) - - - Domaine / Région entière - - - Domaine / Homestead - - - Continent / Homestead - - - Continent / Région entière - - - Tous fichiers - - - Sons - - - Animations - - - Images - - - Enregistrer - - - Charger - - - Images Targa - - - Images Bitmap - - - Fichier de film AVI - - - Fichier d'animation XAF - - - Fichier XML - - - Fichier RAW - - - Images compressées - - - Charger des fichiers - - - Choisir le répertoire - - - Scripts - - - Dictionnaires - - - Présent - - - Absent - - - Pas occupé - - - Occupé - - - Silhouette - - - Peau - - - Cheveux - - - Yeux - - - Chemise - - - Pantalon - - - Chaussures - - - Chaussettes - - - Veste - - - Gants - - - Débardeur - - - Caleçon - - - Jupe - - - Alpha - - - Tatouage - - - Propriétés physiques - - - non valide - - - aucun - - - Chemise non portée - - - Pantalon non porté - - - Chaussures non portées - - - Chaussettes non portées - - - Veste non portée - - - Gants non portés - - - Débardeur non porté - - - Caleçon non porté - - - Jupe non portée - - - Alpha non porté - - - Tatouage non porté - - - Propriétés physiques non portées - - - non valide - - - Créer une nouvelle silhouette - - - Créer une nouvelle peau - - - Créer de nouveaux cheveux - - - Créer de nouveaux yeux - - - Créer une nouvelle chemise - - - Créer un nouveau pantalon - - - Créer de nouvelles chaussures - - - Créer de nouvelles chaussettes - - - Créer une nouvelle veste - - - Créer de nouveaux gants - - - Créer un nouveau débardeur - - - Créer un nouveau caleçon - - - Créer une nouvelle jupe - - - Créer un nouvel alpha - - - Créer un nouveau tatouage - - - Créer de nouvelles propriétés physiques - - - non valide - - - Nouv. [WEARABLE_ITEM] - - - Suivant - - - OK - - - Note au groupe - - - Notices au groupe - - - Envoyée par - - - Pièce(s) jointe(s) : - - - Consultez les notices précédentes ou choisissez de ne plus recevoir ces messages ici. - - - Ouvrir pièce jointe - - - Enregistrer la pièce jointe - - - Offre de téléportation - - - De nouvelles notifications sont arrivées en votre absence. - - - Vous avez %d notification(s) supplémentaire(s) - - - Bras droit - - - Tête - - - Bras gauche - - - Jambe gauche - - - Torse - - - Jambe droite - - - Faible - - - Moyen - - - Élevé - - - Appuyez sur ESC pour quitter la vue subjective - - - Vous n'avez pas trouvé ce que vous cherchiez ? Essayez [secondlife:///app/search/all/[SEARCH_TERM] Rechercher]. - - - Vous n'avez pas trouvé ce que vous cherchiez ? Essayez [secondlife:///app/search/places/[SEARCH_TERM] Rechercher]. - - - Faites glisser un repère ici pour l'ajouter à vos Favoris. - - - Vous n'avez pas de copie de cette texture dans votre inventaire - - - Les achats que vous avez effectués sur la Place du marché s'affichent ici. Vous pouvez alors les faire glisser vers votre inventaire afin de les utiliser. - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/ - - - http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4 - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/imports - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/learn_more - - - Tout le monde peut vendre des articles sur la Place du marché. - - - - Pour devenir vendeur, vous devez [[MARKETPLACE_CREATE_STORE_URL] créer une boutique sur la Place du marché]. - - - Votre boîte d'envoi est vide. - - - - Pour mettre des dossiers en vente sur la [[MARKETPLACE_DASHBOARD_URL] Place du marché], faites-les glisser vers cette zone et cliquez sur "Envoyer vers la Place du marché". - - - Aucune erreur - - - Erreur : avant d'envoyer des articles vers la Place du marché, vous devez vous configurer comme vendeur (gratuit). - - - Erreur : ce dossier est vide. - - - Erreur : le chargement de cet article a échoué en raison d'un nombre trop important d'articles non associés à des produits au niveau de votre compte de vendeur. Pour résoudre ce problème, connectez-vous au site Web de la Place du marché et réduisez le nombre d'articles non associés. - - - Erreur : cet article contient trop d'objets. Pour résoudre le problème, regroupez des objets dans des paquets afin de réduire le nombre total à moins de 200. - - - Erreur : trop de niveaux de dossiers imbriqués concernant cet article. Réorganisez le tout afin qu'un maximum de 3 niveaux soit utilisé. - - - Erreur : cet article ne peut pas être vendu sur la Place du marché. - - - Erreur : un problème est survenu concernant cet article. Veuillez réessayer ultérieurement. - - - Ouvrir les repères - - - - - - - - - chargement des contenus en cours... - - - Aucun contenu - - - - - Oui - - - Non - - - - - - - - - - - - - - - - - - - - - - - - - - Mon inventaire - - - Bibliothèque - - - Textures - - - Sons - - - Cartes de visite - - - Repères - - - Scripts - - - Habits - - - Objets - - - Notes - - - Nouveau dossier - - - Inventaire - - - Images non compressées - - - Parties du corps - - - Corbeille - - - Albums photo - - - Objets trouvés - - - Sons non compressés - - - Animations - - - Gestes - - - Mes Favoris - - - Mes Favoris - - - Tenue actuelle - - - Tenues initiales - - - Mes tenues - - - Accessoires - - - Maillages - - - Amis - - - Tout - - - Aucun élément attaché porté - - - Éléments attachés ([COUNT] emplacements restants) - - - Acheter - - - Acheter des L$ - - - Pierre - - - Métal - - - Verre - - - Bois - - - Chair - - - Plastique - - - Caoutchouc - - - Léger - - - Maj- - - - Ctrl - - - Poitrine - - - Crâne - - - Épaule gauche - - - Épaule droite - - - Main gauche - - - Main droite - - - Pied gauche - - - Pied droit - - - Colonne - - - Bassin - - - Bouche - - - Menton - - - Oreille gauche - - - Oreille droite - - - Globe oculaire gauche - - - Globe oculaire droit - - - Nez - - - Bras D - - - Avant-bras D - - - Bras G - - - Avant-bras G - - - Hanche droite - - - Cuisse D - - - Jambe D - - - Hanche gauche - - - Cuisse G - - - Jambe G - - - Estomac - - - Pectoral gauche - - - Pectoral droit - - - Cou - - - Centre de l'avatar - - - Point d'attache non valide - - - [AGEYEARS] [AGEMONTHS] - - - [AGEYEARS] - - - [AGEMONTHS] - - - [AGEWEEKS] - - - [AGEDAYS] - - - Inscrit aujourd'hui - - - [COUNT] an - - - [COUNT] ans - - - [COUNT] ans - - - [COUNT] mois - - - [COUNT] mois - - - [COUNT] mois - - - [COUNT] semaine - - - [COUNT] semaines - - - [COUNT] semaines - - - [COUNT] jour - - - [COUNT] jours - - - [COUNT] jours - - - [COUNT] membre - - - [COUNT] membres - - - [COUNT] membres - - - Résident - - - Essai - - - Membre originaire - - - Employé(e) de Linden Lab - - - Infos de paiement utilisées - - - Infos de paiement enregistrées - - - Aucune info de paiement enregistrée - - - Personne dont l'âge a été vérifié - - - Personne dont l'âge n'a pas été vérifié - - - Centre 2 - - - En haut à droite - - - En haut - - - En haut à gauche - - - Centre - - - En bas à gauche - - - Bas - - - En bas à droite - - - Téléchargé, compilation en cours - - - Script introuvable sur le serveur. - - - Problème lors du téléchargement - - - Droits insuffisants pour télécharger un script. - - - Droits insuffisants pour - - - Échec du téléchargement, erreur inconnue - - - Recompilation - progrès - - - recompiler - - - Réinitialiser les progrès - - - réinitialiser - - - Lancer - - - lancer - - - Arrêter - - - arrêter - - - Compilation réussie ! - - - Compilation réussie, enregistrement en cours... - - - Enregistrement terminé. - - - Script (objet hors de portée) - - - Objet [OBJECT] appartenant à [OWNER] - - - aucun - - - - (Inconnu) - - - - - [day,datetime,utc]/[mthnum,datetime,utc]/[year,datetime,utc] - - - - - Solde - - - Crédits - - - Débits - - - Total - - - Aucune donnée trouvée pour le groupe - - - domaine parent - - - continent - - - teen - - - n'importe qui - - - erreur - - - tous les domaines appartenant à [OWNER] - - - tous les domaines vous appartenant - - - tous les domaines que vous gérez pour [OWNER] - - - Résidents autorisés : ([ALLOWEDAGENTS], max. [MAXACCESS]) - - - Groupes autorisés : ([ALLOWEDGROUPS], max. [MAXACCESS]) - - - Mémoire des scripts de parcelles - - - Parcelles répertoriées : [PARCELS] - - - Mémoire utilisée : [COUNT] Ko sur [MAX] ; [AVAILABLE] Ko disponibles - - - Mémoire utilisée : [COUNT] Ko - - - URL des scripts de parcelles - - - URL utilisées : [COUNT] sur [MAX] ; [AVAILABLE] disponible(s) - - - URL utilisées : [COUNT] - - - Une erreur est survenue pendant la requête d'informations. - - - Aucune parcelle sélectionnée - - - Erreur : les informations de script ne sont disponibles que dans votre région actuelle. - - - Extraction des informations en cours... - - - Vous n'avez pas le droit d'examiner cette parcelle. - - - Assis(e) dessus - - - Poitrine - - - Tête - - - Épaule gauche - - - Épaule droite - - - Main gauche - - - Main droite - - - Pied gauche - - - Pied droit - - - Précédent - - - Bassin - - - Bouche - - - Menton - - - Oreille gauche - - - Oreille droite - - - Œil gauche - - - Œil droit - - - Nez - - - Bras droit - - - Avant-bras droit - - - Bras gauche - - - Avant-bras gauche - - - Hanche droite - - - Cuisse droite - - - Jambe droite - - - Hanche gauche - - - Cuisse gauche - - - Jambe gauche - - - Ventre - - - Pectoral droit - - - Pectoral gauche - - - HUD centre 2 - - - HUD en haut à droite - - - HUD en haut au centre - - - HUD en haut à gauche - - - HUD centre 1 - - - HUD en bas à gauche - - - HUD en bas - - - HUD en bas à droite - - - Ligne [LINE], colonne [COLUMN] - - - [COUNT] trouvé(s) - - - [hour12,datetime,slt]:[min,datetime,slt] [ampm,datetime,slt] - - - [mthnum,datetime,slt]/[day,datetime,slt] - - - Contenu de l'objet - - - Nouveau script - - - Le résident auquel vous avez envoyé un message est en mode Occupé, ce qui signifie qu'il a demandé à ne pas être dérangé. Votre message restera affiché dans son panneau IM afin qu'il puisse le lire ultérieurement. - - - (par nom) - - - (résident) - - - (objet) - - - (groupe) - - - (externe) - - - Il n'y a aucun règlement pour ce domaine. - - - Il n'y a aucun règlement pour ce domaine. Le terrain sur ce domaine est vendu par le propriétaire, non par Linden Lab. Pour en savoir plus, veuillez contacter le propriétaire. - - - - - - Propriété du groupe - - - Public - - - Réglages locaux - - - Réglages de la région - - - Clics : [TELEPORT] téléportation, [MAP] carte, [PROFILE] profil - - - (mise à jour après la publication) - - - Vous n'avez pas créé de favoris ni de petites annonces Cliquez sur le bouton Plus pour créer un favori ou une petite annonce. - - - L'utilisateur n'a ni favoris ni petites annonces. - - - Chargement... - - - Prévisualiser - - - Propriétés - - - Un objet appelé - - - possédé par le groupe - - - possédé par un groupe inconnu - - - possédé par - - - possédé par un résident inconnu - - - vous a donné - - - Vous refusez l'offre [DESC] de <nolink>[NAME]</nolink>. - - - Total - - - acheté - - - vous a payé - - - payé - - - a acheté un pass à - - - a payé des frais pour un événement - - - a payé un prix pour un événement - - - Solde - - - Crédits - - - Débits - - - [weekday,datetime,utc] [day,datetime,utc] [mth,datetime,utc] [year,datetime,utc] - - - Contenus - - - Objets acquis - - - Annuler - - - Le chargement de [NAME] coûte [AMOUNT] L$ - - - Cet achat coûte [AMOUNT] L$ - - - Extension de fichier inconnue .%s -.wav, .tga, .bmp, .jpg, .jpeg, ou .bvh acceptés - - - Ignorer - - - Ignorer - - - Ne plus ignorer - - - Ne plus ignorer - - - Ajouter à mes repères... - - - Modifier mon repère... - - - ⌃ - - - ⌘ - - - ⌥ - - - ⇧ - - - Ctrl+ - - - Alt+ - - - Maj+ - - - Fichier enregistré - - - Réception - - - Matin - - - Après-midi - - - PST - - - PDT - - - Avant - - - Gauche - - - Droite - - - Arrière - - - Nord - - - Sud - - - Ouest - - - Est - - - Haut - - - Bas - - - Toutes catégories - - - Shopping - - - Terrains à louer - - - Propriétés à louer - - - Divertissements - - - Nouveaux produits - - - Emplois - - - Offres - - - Services - - - Divers - - - Aucun - - - Appartenant aux Lindens - - - Adulte - - - Arts et culture - - - Business - - - Éducation - - - Jeux - - - Favoris - - - Accueil pour les nouveaux - - - Parcs et nature - - - Résidentiel - - - Phase - - - Autre - - - Location - - - Aucun - - - Vous - - - : - - - , - - - ... - - - *** - - - ( - - - ) - - - . - - - ' - - - --- - - - Médias multiples - - - Lire/pauser le média - - - Une erreur est survenue lors de la lecture de la ligne de commande. -Merci de consulter : http://wiki.secondlife.com/wiki/Client_parameters -Erreur : - - - [APP_NAME] - Utilisation de la ligne de commande : - - - [APP_NAME] ne peut accéder à un fichier requis. - -Cela vient du fait que quelqu'un a ouvert plusieurs copies ou que votre système pense qu'un fichier est ouvert. -Si ce message persiste, veuillez redémarrer votre ordinateur. -Si le problème persiste, vous devrez peut-être complètement désinstaller puis réinstaller [APP_NAME]. - - - Erreur fatale - - - [APP_NAME] nécessite un microprocesseur AltiVec (version G4 ou antérieure). - - - [APP_NAME] est déjà en cours d'exécution. -Vérifiez si une version minimisée du programme apparaît dans votre barre de tâches. -Si ce message persiste, redémarrez votre ordinateur. - - - [APP_NAME] semble avoir crashé lors de l'utilisation précédente. -Voulez-vous envoyer un rapport de crash ? - - - Notification - - - [APP_NAME] ne peut détecter DirectX 9.0b ou une version supérieure. -[APP_NAME] utilise DirectX pour détecter les matériels et/ou les pilotes qui ne sont pas à jour et peuvent causer des problèmes de stabilité, de performance ou des plantages. Bien que vous puissiez utiliser [APP_NAME] sans DirectX, nous vous recommandons de l'utiliser avec DirectX 9.0b. - -Voulez-vous continuer ? - - - Avertissement - - - Les mises à jour automatiques n'existent pas encore pour Linux. -Veuillez télécharger la dernière version sur www.secondlife.com. - - - RegisterClass a échoué - - - Erreur - - - Impossible d'ouvrir le mode plein écran à [WIDTH] x [HEIGHT]. -Utilisation du mode fenêtré. - - - Erreur de fermeture lors de la destruction de la fenêtre (DestroyWindow() a échoué) - - - Erreur de fermeture - - - Impossible de créer le contexte GL - - - Impossible de trouver le format pixel approprié - - - Impossible de trouver la description du format pixel - - - [APP_NAME] nécessite True Color (32 bits) pour s'exécuter. -Accédez aux paramètres d'affichage de votre ordinateur et réglez le mode couleur sur 32 bits. - - - [APP_NAME] ne peut pas s'exécuter, car il n'y pas de canal alpha 8 bits accessible. En général, ceci vient de problèmes avec le pilote de la carte vidéo. -Assurez-vous d'avoir installé le pilote de carte vidéo le plus récent possible. -Assurez-vous aussi que votre écran est réglé sur True Color (32 bits) sous Panneau de configuration > Affichage > Paramètres. -Si ce message persiste, veuillez aller sur la page [SUPPORT_SITE]. - - - Impossible de trouver le format pixel approprié - - - Impossible de créer le contexte de rendu GL - - - Impossible d'activer le contexte de rendu GL - - - [APP_NAME] ne peut pas s'exécuter car les pilotes de votre carte vidéo n'ont pas été installés correctement, ne sont pas à jour, ou sont pour du matériel non pris en charge. Assurez-vous d'avoir des pilotes de cartes vidéos récents, et même si vous avez les plus récents, réinstallez-les. - -Si ce message persiste, veuillez aller sur la page [SUPPORT_SITE]. - - - Peu - - - Tout blancs - - - Grand yeux - - - Arqués - - - Longueur des bras - - - Attachés - - - Lobes - - - Mèches de derrière - - - Plus - - - Frange - - - Yeux perçants - - - Taille du ventre - - - Plus - - - Grosses fesses - - - Volume : Derrière - - - Volume : Devant - - - Volume : Haut - - - Plus - - - Gros pectoraux - - - Spikes - - - Noir - - - Blond - - - Cheveux blonds - - - Blush - - - Couleur du blush - - - Opacité du blush - - - Contour du corps - - - Graisse - - - Grains de beauté - - - Plus - - - Épaisseur du corps - - - Moins - - - Jambes arquées - - - Hauteur des seins - - - Clivage - - - Taille des seins - - - Arête du nez - - - Large - - - Taille du front - - - Yeux globuleux - - - Yeux globuleux - - - En bulbe - - - Nez en bulbe - - - Masse des seins - - - Lissage des seins - - - Gravité des seins - - - Résistance de l'air sur les seins - - - Effet max. - - - Élasticité - - - Amplification - - - Amortissement - - - Effet max. - - - Élasticité - - - Amplification - - - Amortissement - - - Effet max. - - - Élasticité - - - Amplification - - - Amortissement - - - Masse du ventre - - - Lissage du ventre - - - Gravité du ventre - - - Résistance de l'air sur le ventre - - - Effet max. - - - Élasticité - - - Amplification - - - Amortissement - - - Masse des fesses - - - Lissage des fesses - - - Gravité des fesses - - - Résistance de l'air sur les fesses - - - Effet max. - - - Élasticité - - - Amplification - - - Amortissement - - - Effet max. - - - Élasticité - - - Amplification - - - Amortissement - - - Sourcils touffus - - - Beaucoup - - - Taille des fesses - - - Gravité des fesses - - - Jupe gonflante - - - Pas gonflante - - - Plus gonflante - - - Moins - - - Pommettes - - - Taille de la poitrine - - - Angle du menton - - - Fente du menton - - - Favoris - - - Profondeur - - - Menton lourd - - - Menton rentré - - - Menton sorti - - - Menton-cou - - - Clair - - - Fendu - - - Yeux rapprochés - - - Fermé(s) - - - Fermé à l'arrière - - - Fermé devant - - - Fermé à gauche - - - Fermé à droite - - - Mini - - - Col arrière - - - Col devant - - - Coin vers le bas - - - Coin vers le haut - - - Fripée - - - Déviation du nez - - - Jambes - - - Sombre - - - Vert foncé - - - Plus foncé - - - Profonde - - - Talons par défaut - - - Dense - - - Double menton - - - Pointant vers le bas - - - Maxi - - - Angle de l'oreille - - - Taille - - - Extrémités - - - Proéminence - - - Cernes - - - Couleur des yeux - - - Profondeur - - - Clarté - - - Ouverture - - - Œil proéminent - - - Taille de l'œil - - - Espacement - - - Arc - - - Épaisseur sourcils - - - Hauteur - - - Direction - - - Taille - - - Longueur des cils - - - Eyeliner - - - Couleur de l'eyeliner - - - Yeux globuleux - - - Visage - - - Définition - - - Yeux écartés - - - Lèvres épaisses - - - Femme - - - Sans doigts - - - Doigts - - - Jambes larges - - - Moins - - - Fesses plates - - - Tête plate - - - Orteil plat - - - Pointure - - - Angle du front - - - Front lourd - - - Tâches de rousseur - - - Mèches de devant - - - Arrière touffu - - - Eyeliner marqué - - - Devant touffu - - - Côtés touffus - - - Côtés touffus - - - Brillant - - - Gants avec doigts - - - Longueur - - - Cheveux - - - Cheveux : Derrière - - - Cheveux : Devant - - - Cheveux : Côtés - - - Sens de la coiffure - - - Épaisseur cheveux - - - Épaisseur cheveux - - - Inclinaison - - - Vers la gauche - - - Vers la droite - - - Cheveux : Volume - - - Taille de la main - - - Plus - - - Longueur - - - Forme - - - Taille - - - Allongement - - - Talons - - - Forme des talons - - - Taille - - - Haut - - - Talons hauts - - - Haut - - - Haute - - - Haut et serré - - - Plus élevé - - - Longueur hanche - - - Largeur hanche - - - Rentré - - - Couleur ombre interne - - - Opacité ombre interne - - - Coin interne - - - Ombre de l'œil interne - - - Ombre interne - - - Longueur de la veste - - - Plis de la veste - - - Angle mâchoire - - - Saillie mâchoire - - - Mâchoire - - - Rapprochés - - - Bajoues - - - Angle du genou - - - Genoux rapprochés - - - Plus - - - Grandes mains - - - Raie à gauche - - - Longueur - - - Muscles - - - Moins - - - Moins - - - Moins - - - Moins - - - Moins - - - Moins - - - Moins - - - Moins - - - Moins - - - Moins - - - Moins ronde - - - Moins - - - Moins carrée - - - Moins - - - Moins - - - Plus léger - - - Fente labiale - - - Prof. fente labiale - - - Volume des lèvres - - - Rougeur des lèvres - - - Proportion des lèvres - - - Épaisseur - - - Largeur - - - Brillant à lèvres - - - Rouge à lèvres - - - Couleur du rouge à lèvres - - - Plus - - - Tête longue - - - Hanches longues - - - Jambes longues - - - Long cou - - - Longues couettes - - - Longue queue de cheval - - - Torse long - - - Bras longs - - - Pantalons amples - - - Chemise ample - - - Manches amples - - - Poignées d'amour - - - Bas - - - Talons bas - - - Bas - - - Basse - - - Bas et ample - - - Abaisser - - - Arête inférieure - - - Joue inférieure - - - Homme - - - Raie au milieu - - - Plus - - - Plus - - - Plus - - - Plus - - - Plus - - - Plus - - - Plus - - - Plus - - - Plus - - - Plus - - - Inférieure plus grosse - - - Plus - - - Plus - - - Plus - - - Plus - - - Plus - - - Plus - - - Plus - - - Supérieure plus grosse - - - Plus - - - Plus - - - Plus - - - Moustache - - - Coin de la bouche - - - Position - - - Mowhawk - - - Musclé - - - Longs - - - Vernis à ongles - - - Couleur du vernis - - - Moins - - - Arrière étroit - - - Devant étroit - - - Lèvres étroites - - - Naturel - - - Longueur du cou - - - Épaisseur du cou - - - Pas de blush - - - Pas d'eyeliner - - - Pas d'ombre à paupières - - - Pas de brillant à lèvres - - - Pas de rouge à lèvres - - - Pas de raie - - - Pas de vernis - - - Pas de rouge - - - Pas de spikes - - - Pas de blanc - - - Pas de rides - - - Normal plus bas - - - Normal plus haut - - - Nez à gauche - - - Nez à droite - - - Taille du nez - - - Épaisseur du nez - - - Angle bout du nez - - - Forme bout du nez - - - Largeur du nez - - - Division narines - - - Largeur narines - - - Opaque - - - Ouvert - - - Derrière ouvert - - - Devant ouvert - - - Ouvert à gauche - - - Ouvert à droite - - - Orange - - - Sorti - - - Couleur de l'ombre externe - - - Opacité de l'ombre externe - - - Coin externe - - - Ombre de l'œil externe - - - Ombre externe - - - Rentrée - - - Parties - - - Ongles vernis - - - Pâle - - - Entrejambe - - - Taille - - - Longueur - - - Taille - - - Plis - - - Raie - - - Séparation frange - - - Pectoraux - - - Pigmentation - - - Couettes - - - Rose - - - Plus rose - - - Platef. (hauteur) - - - Platef. (largeur) - - - Pointue - - - Talons pointus - - - Queue de cheval - - - Jupe bouffante - - - Œil gauche saillant - - - Œil droit saillant - - - Plus - - - Paup. gonflées - - - Couleur arc en ciel - - - Cheveux roux - - - Standard - - - Raie à droite - - - Teint rosé - - - Rond - - - Rougeur - - - Rouge - - - Texture - - - Culotte de cheval - - - Jambes maigres - - - Séparés - - - Creux - - - Coupe derrière - - - Visage - - - Front - - - Haut gauche décalé - - - Haut droit décalé - - - Dégagé derrière - - - Dégagé devant - - - Vers la gauche - - - Déplacement - - - Vers la droite - - - Chemise - - - Taille - - - Plis - - - Hauteur - - - Moins - - - Bras courts - - - Jambes courtes - - - Petit cou - - - Couettes courtes - - - Queue de cheval courte - - - Court - - - Torse court - - - Hanches courtes - - - Épaules - - - Mèches sur le côté - - - Favoris - - - Cheveux sur le côté - - - Cheveux sur le côté en bas - - - Cheveux sur le côté en haut - - - Cou maigre - - - Taille jupe - - - Longueur jupe - - - Front incliné - - - Longueur manche - - - Ampleur manche - - - Fente : Derrière - - - Fente : Devant - - - Fente : Gauche - - - Fente : Droite - - - Moins - - - Petites mains - - - Moins - - - Moins - - - Cheveux lisses - - - Longueur - - - Barbichette - - - Rares - - - Mèches en pointe - - - Carrée - - - Orteil carré - - - Écraser la tête - - - Allonger la tête - - - Saillante - - - Poitrine enfoncée - - - Yeux enfoncés - - - En arrière - - - Vers l'avant - - - Plus - - - Arrière - - - Avant - - - Talons épais - - - Cou épais - - - Orteil épais - - - Mince - - - Sourcils fins - - - Lèvres fines - - - Nez fin - - - Menton fin - - - Jambes serrées - - - Pantalons serrés - - - Chemise serrée - - - Jupe serrée - - - Manches serrées - - - Forme de l'orteil - - - Épaisseur orteil - - - Longueur du torse - - - Muscles du torse - - - Torse maigre - - - Séparés - - - Lisse - - - Sortie - - - Artificiel - - - Arête supérieure - - - Joue supérieure - - - Menton supérieur - - - Paupière sup. - - - En trompette - - - Très rouge - - - Hauteur taille - - - Ronde - - - Cheveux blancs - - - Plus - - - Derrière large - - - Devant large - - - Lèvres larges - - - Artificiel - - - Rides - - - Ajouter à mes repères - - - Modifier mon repère - - - En savoir plus sur l'emplacement actuel - - - Historique de mes emplacements - - - Acheter ce terrain - - - Chat vocal indisponible ici - - - Vol interdit - - - Pas de bousculades - - - Construction/placement d'objets interdit - - - Scripts interdits - - - Santé - - - Région de type Adulte - - - Région de type Modéré - - - Région de type Général - - - Avatars visibles et chat autorisé en dehors de cette parcelle - - - Les objets mobiles risquent de ne pas se comporter correctement dans cette région tant qu'elle n'est pas refigée. - - - La recherche de chemin dynamique n'est pas activée dans cette région. - - - [APP_NAME] - Mise à jour - - - Mise à jour de [APP_NAME]... - - - Installation de [APP_NAME]... - - - Le client [APP_NAME] est en train d'être mis à jour. Cela peut prendre un certain temps, merci de votre patience. - - - Mise à jour en cours... - - - Mise à jour en cours - - - Le téléchargement de la mise à jour a échoué - - - Une erreur est survenue lors de la mise à jour de [APP_NAME]. Veuillez télécharger la dernière version sur www.secondlife.com. - - - L'installation de la mise à jour a échoué - - - Impossible de lancer le client - - - [APP_NAME] : transfert trop rapide des articles de [FROM_NAME] ; aperçu automatique désactivé pendant [TIME] secondes - - - [APP_NAME] : transfert trop rapide des articles ; aperçu automatique désactivé pendant [TIME] secondes - - - -- Archivage des IM activé -- - - - [NAME] est en train d'écrire... - - - (sans nom) - - - (Modéré : Voix désactivées par défaut) - - - Le chat écrit n'est pas disponible pour cet appel. - - - Votre chat écrit a été désactivé par un modérateur de groupe. - - - Cliquez ici pour envoyer un message instantané. - - - À - - - (Modérateur) - - - (Enregistrement : [LONG_TIMESTAMP]) - - - Pour afficher ce message, vous devez désactiver la case Seuls mes amis et groupes peuvent m'appeler ou m'envoyer un IM, sous Préférences/Confidentialité. - - - Votre appel a fait l'objet d'une réponse - - - Vous appelez. - - - Vous avez rejoint l'appel - - - [NAME] appelle. - - - En train de rejoindre l'appel... - - - Connecté(e), cliquez sur Quitter l'appel pour raccrocher - - - A quitté l'appel - - - Connexion en cours... - - - Conférence ad-hoc - - - Conférence avec [AGENT_NAME] - - - Objet de l'inventaire offert - - - Faire glisser les objets de l'inventaire ici - - - (Session IM inexistante) - - - Vous êtes le seul participant à cette session. - - - [NAME] est hors ligne. - - - Pour accepter ce chat vocal/vous connecter, cliquez sur le bouton [BUTTON NAME]. - - - Vous ignorez ce résident. Si vous lui envoyez un message, il ne sera plus ignoré. - - - Erreur lors de la requête, veuillez réessayer ultérieurement. - - - Erreur lors de la requête, veuillez réessayer ultérieurement. - - - Vous n'avez pas les droits requis. - - - La session a expiré - - - Vous n'avez pas ce pouvoir. - - - Vous n'avez pas ce pouvoir. - - - Vous n'êtes pas modérateur de session. - - - Un modérateur de groupe a désactivé votre chat écrit. - - - Un modérateur de groupe a désactivé votre chat écrit. - - - Impossible d'ajouter des participants à la session de chat avec [RECIPIENT]. - - - Impossible d'envoyer votre message à la session de chat avec [RECIPIENT]. - - - Impossible d'envoyer votre message à la session de chat avec [RECIPIENT]. - - - Erreur lors de la modération. - - - Vous avez été supprimé du groupe. - - - Vous avez été supprimé du groupe. - - - Vous ne pouvez plus participer à la session de chat. - - - [SOURCES] a dit quelque chose de nouveau - - - [SOURCES] ont dit quelque chose de nouveau - - - Expiration du délai d'initialisation de la session - - - Emplacement du domicile défini. - - - http://secondlife.com/landing/voicemorphing - - - [NAME] vous a payé [AMOUNT] L$ [REASON]. - - - [NAME] vous a payé [AMOUNT] L$. - - - Vous avez payé à [AMOUNT] L$ [REASON]. - - - Vous avez payé [AMOUNT] L$. - - - Vous avez payé à [NAME] [AMOUNT] L$. - - - Vous avez payé à [AMOUNT] L$ [REASON]. - - - Votre paiement de [AMOUNT] L$ à [NAME] [REASON] a échoué. - - - Votre paiement de [AMOUNT] L$ a échoué. - - - Votre paiement de [AMOUNT] L$ à [NAME] a échoué. - - - Votre paiement de [AMOUNT] L$ [REASON] a échoué. - - - pour l'article suivant : [ITEM] - - - pour une parcelle de terrain - - - pour un pass d'accès au terrain - - - pour une cession de terrain - - - pour créer un groupe - - - pour rejoindre un groupe - - - pour charger - - - pour publier une petite annonce - - - Donner [AMOUNT] L$ - - - Le chargement coûte [AMOUNT] L$ - - - Cela coûte [AMOUNT] L$ - - - Achat du terrain sélectionné pour [AMOUNT] L$ - - - Cet objet coûte [AMOUNT] L$ - - - Tous - - - Officiers - - - Propriétaires - - - En ligne - - - Chargement... - -du rapport d'infraction - - - Nouvelle silhouette - - - Nouvelle peau - - - Nouveaux cheveux - - - Nouveaux yeux - - - Nouvelle chemise - - - Nouveau pantalon - - - Nouvelles chaussures - - - Nouvelles chaussettes - - - Nouvelle veste - - - Nouveaux gants - - - Nouveau débardeur - - - Nouveau caleçon - - - Nouvelle jupe - - - Nouvel alpha - - - Nouveau tatouage - - - Nouvelles propriétés physiques - - - Objet à porter non valide - - - Nouveau geste - - - Nouveau script - - - Nouvelle note - - - Nouveau dossier - - - Contenus - - - Geste - - - Gestes masculins - - - Gestes féminins - - - Autres gestes - - - Gestes liés à la parole - - - Gestes communs - - - Homme - Demander pardon - - - Homme - Get lost - - - Homme - Envoyer un baiser - - - Homme - Hou ! - - - Homme - Ennui - - - Homme - Hé ! - - - Homme - Rire - - - Homme - Dégoût - - - Homme - Hausser les épaules - - - Homme - Tirer la langue - - - Homme - Ouah ! - - - Femme - Glousser - - - Femme - Pleurer - - - Femme - Gêne - - - Femme - Demander pardon - - - Femme - Get lost - - - Femme - Envoyer un baiser - - - Femme - Hou ! - - - Femme - Ennui - - - Femme - Hé ! - - - Femme - Hey baby - - - Femme - Rire - - - Femme - Looking good - - - Femme - Over here - - - Femme - Please - - - Femme - Dégoût - - - Femme - Hausser les épaules - - - Femme - Tirer la langue - - - Femme - Ouah ! - - - /s'incliner - - - /applaudir - - - /compter - - - /éteindre - - - /vatefairevoir - - - /montrersesmuscles - - - /non - - - /non ! - - - /papier - - - /memontrerdudoigt - - - /montrerl'autredudoigt - - - /pierre - - - /ciseaux - - - /fumer - - - /bailler - - - /siffler - - - /oui - - - /oui ! - - - absent - - - danse1 - - - danse2 - - - danse3 - - - danse4 - - - danse5 - - - danse6 - - - danse7 - - - danse8 - - - [day,datetime,slt]/[mthnum,datetime,slt]/[year,datetime,slt] - - - aucun/aucun - - - Impossible de charger des images de taille supérieure à [WIDTH]*[HEIGHT] - - - - Malgré nos efforts, une erreur inattendue s'est produite. - - Veuillez vous reporter à status.secondlifegrid.net afin de déterminer si un problème connu existe avec ce service. - Si le problème persiste, vérifiez la configuration de votre réseau et de votre pare-feu. - - - Sunday:Monday:Tuesday:Wednesday:Thursday:Friday:Saturday - - - Sun:Mon:Tue:Wed:Thu:Fri:Sat - - - January:February:March:April:May:June:July:August:September:October:November:December - - - Jan:Feb:Mar:Apr:May:Jun:Jul:Aug:Sep:Oct:Nov:Dec - - - [MDAY] - - - AM - - - PM - - - [AMOUNT] US$ - - - Inscription - - - Rôles - - - Identité du groupe - - - Gestion des parcelles - - - Identité des parcelles - - - Paramètres des parcelles - - - Pouvoirs sur les parcelles - - - Accès aux parcelles - - - Contenu des parcelles - - - Gestion des objets - - - Comptabilité - - - Notices - - - Chat - - - Supprimer les articles sélectionnés ? - - - Supprimer l'article sélectionné ? - - - Cette tenue ne contient aucun article. - - - Sélectionnez un éditeur à l'aide du paramètre ExternalEditor. - - - Éditeur externe spécifié introuvable. -Essayez avec le chemin d'accès à l'éditeur entre guillemets doubles -(par ex. : "/chemin_accès/editor" "%s"). - - - Erreur lors de l'analyse de la commande d'éditeur externe. - - - Échec d'exécution de l'éditeur externe. - - - Échec de traduction : [REASON] - - - Erreur lors de l'analyse de la réponse relative à la traduction. - - - Échap - - - Space - - - Enter - - - Tab - - - Ins - - - Del - - - Backsp - - - Maj - - - Ctrl - - - Alt - - - CapsLock - - - Début - - - End - - - PgUp - - - PgDn - - - F1 - - - F2 - - - F3 - - - F4 - - - F5 - - - F6 - - - F7 - - - F8 - - - F9 - - - F10 - - - F11 - - - F12 - - - Ajouter - - - Soustraire - - - Multiplier - - - Diviser - - - PAD_DIVIDE - - - PAD_LEFT - - - PAD_RIGHT - - - PAD_DOWN - - - PAD_UP - - - PAD_HOME - - - PAD_END - - - PAD_PGUP - - - PAD_PGDN - - - PAD_CENTER - - - PAD_INS - - - PAD_DEL - - - PAD_Enter - - - PAD_BUTTON0 - - - PAD_BUTTON1 - - - PAD_BUTTON2 - - - PAD_BUTTON3 - - - PAD_BUTTON4 - - - PAD_BUTTON5 - - - PAD_BUTTON6 - - - PAD_BUTTON7 - - - PAD_BUTTON8 - - - PAD_BUTTON9 - - - PAD_BUTTON10 - - - PAD_BUTTON11 - - - PAD_BUTTON12 - - - PAD_BUTTON13 - - - PAD_BUTTON14 - - - PAD_BUTTON15 - - - - - - - = - - - ` - - - ; - - - [ - - - ] - - - \ - - - 0 - - - 1 - - - 2 - - - 3 - - - 4 - - - 5 - - - 6 - - - 7 - - - 8 - - - 9 - - - A - - - B - - - C - - - D - - - E - - - F - - - G - - - H - - - I - - - J - - - K - - - L - - - M - - - N - - - O - - - P - - - Q - - - R - - - S - - - T - - - U - - - V - - - W - - - X - - - Y - - - Z - - - Affichage des balises de particule (bleu) - - - Affichage des balises d'objet physique (vert) - - - Affichage des balises d'objet scripté (rouge) - - - Affichage des balises d'objet scripté avec fonction de toucher (rouge) - - - Affichage des balises de son (jaune) - - - Affichage des balises de média (blanc) - - - Masquage des particules - - - À propos du terrain - - - Apparence - - - Avatar - - - Construire - - - Chat - - - Boussole - - - Destinations - - - Gestes - - - Aide rapide - - - Inventaire - - - Carte - - - Place du marché - - - Mini-carte - - - Marcher / Courir / Voler - - - Boîte d'envoi vendeur - - - Personnes - - - Favoris - - - Lieux - - - Préférences - - - Profil - - - Recherche - - - Photo - - - Parler - - - Paramètres de la caméra - - - Paramètres vocaux - - - Information sur le terrain que vous visitez - - - Modifier votre avatar - - - Choisir un avatar complet - - - Construction d'objets et remodelage du terrain - - - Parler aux personnes près de vous par chat écrit - - - Boussole - - - Destinations intéressantes - - - Gestes de votre avatar - - - Comment effectuer les opérations courantes - - - Afficher et utiliser vos possessions - - - Carte du monde - - - Faire du shopping - - - Afficher les personnes près de vous - - - Faire bouger votre avatar - - - Transférer des articles vers votre place de marché afin de les vendre. - - - Amis, groupes et personnes près de vous - - - Lieux à afficher comme favoris dans votre profil - - - Lieux enregistrés - - - Préférences - - - Modifier ou afficher votre profil - - - Trouver des lieux, personnes, événements - - - Prendre une photo - - - Parler aux personnes près de vous en utilisant votre micro - - - Changer l'angle de la caméra - - - Commandes de réglage du volume des appels et des personnes près de vous dans Second Life. - - - actuellement dans la barre d'outils du bas - - - actuellement dans la barre d'outils de gauche - - - actuellement dans la barre d'outils de droite - - - Garder% - - - Détail - - - Meilleur détail - - - Surface - - - Solide - - - Wrap - - - Aperçu - - - Normal - - - http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer - - - Aucun - - - Maillage de navigation affecté - - - Personnage - - - (Multiple) - - - Très faible - - - Faible - - - Moyenne - - - Élevée - - - Très élevée - - - Le résident ne peut pas visiter cette région. - - - [User] - - diff --git a/indra/newview/skins/steam/xui/it/strings.xml b/indra/newview/skins/steam/xui/it/strings.xml deleted file mode 100644 index 180eecad7b..0000000000 --- a/indra/newview/skins/steam/xui/it/strings.xml +++ /dev/null @@ -1,4964 +0,0 @@ - - - - - Second Life - - - Second Life - - - SECOND LIFE - - - Portale di supporto di Second Life - - - Ricerca hardware... - - - Caricamento di [APP_NAME]... - - - Pulizia della cache... - - - Inizializzazione della cache texture... - - - Inizializzazione VFS... - - - Inizializzazione grafica non riuscita. Aggiorna il driver della scheda grafica! - - - Ripristino in corso... - - - Modifica della risoluzione... - - - Luminosità massima (vers. precedente) - - - In connessione. [APP_NAME] può sembrare rallentata. Attendi. - - - Accesso in corso... - - - In autenticazione - - - Aggiornamento account in corso... - - - Un precedente tentativo di login è fallito. Tentativo di connessione [NUMBER] - - - Sto caricando [SECOND_LIFE]... - - - Inizializzazione del browser web incorporato... - - - Inizializzazione dati multimediali... - - - Caricamento caratteri... - - - Verifica file della cache (tempo previsto 60-90 secondi)... - - - Elaborazione risposta... - - - Inizializzazione mondo... - - - Decodifica immagini... - - - Inizializzazione QuickTime... - - - QuickTime non trovato - impossibile inizializzare. - - - QuickTime configurato con successo. - - - Richiesta capacità regione... - - - Richiesta capacità regione, tentativo [NUMBER]... - - - In attesa della risposta della regione... - - - Connessione alla regione... - - - Sto caricando i vestiti... - - - Il server ha inviato un certificato non valido o errato. Rivolgiti all'amministratore della griglia. - - - Per accedere al server è stato utilizzato un nome host non valido; controlla lo SLURL o il nome host della griglia. - - - Il certificato inviato dalla griglia sembra essere scaduto. Controlla l'orologio del sistema o rivolgiti all'amministratore della griglia. - - - Impossibile utilizzare per SSl il certificato inviato dal server. Rivolgiti all'amministratore della griglia. - - - Nella catena dei certificati del server erano presenti troppi certificati. Rivolgiti all'amministratore della griglia. - - - Impossibile verificare la firma del certificato inviato dal server della griglia. Rivolgiti all'amministratore della griglia. - - - Errore di rete: Non è stato possibile stabilire un collegamento, controlla la tua connessione. - - - Accesso non riuscito. - - - Esci - - - http://join.secondlife.com/index.php?sourceid=1206_steam&lang=it-IT - - - Il viewer utilizzato non è più in grado di accedere a Second Life. Visita la parina seguente per scaricare un nuovo viewer: -http://secondlife.com/download. - -Per maggiori informazioni, consulta le domande frequenti alla pagina seguente: -http://secondlife.com/viewer-access-faq - - - Disponibile aggiornamento facoltativo viewer: [VERSION] - - - Aggernamento viewer richiesto: [VERSION] - - - Questo agente ha già eseguito il login. - - - Siamo spiacenti. Il tentativo di accesso non è riuscito. -Verifica di avere inserito correttamente - * Nome utente (come robby12 o Stella Soleggiato) - * Password -Verifica anche che il blocco delle maiuscole non sia attivato. - - - Come misura precauzionale, la tua password è stata cambiata. -Visita la pagina del tuo account a http://secondlife.com/password -e rispondi alla domanda di sicurezza per reimpostare la password. -Ci scusiamo per l'inconveniente. - - - Abbiamo effettuato delle modifiche al sistema che richiedono di reimpostare la password. -Visita la pagina del tuo account a http://secondlife.com/password -e rispondi alla domanda di sicurezza per reimpostare la password. -Ci scusiamo per l'inconveniente. - - - Second Life è chiuso temporaneamente per manutenzione. -Al momento, solo i dipendenti possono eseguire l'accesso. -Visita www.secondlife.com/status per aggiornamenti. - - - L'accesso a Second Life è temporaneamente limitato per garantire che chi è nel mondo virtuale abbia la migliore esperienza possibile. - -Le persone con account gratuiti non potranno accedere a Second Life durante questo periodo, per lasciare spazio alle persone che hanno pagato per Second Life. - - - Non si può accedere a Second Life da questo computer. -Se ritieni che si tratta di un errore, contatta -support@secondlife.com. - - - Il tuo account non è accessibile fino alle -[TIME] fuso orario del Pacifico. - - - Non siamo attualmente in grado di completare la tua richiesta. -Contatta l'assistenza Second Life alla pagina http://secondlife.com/support. -Se non sei in grado di cambiare la password, chiama (866) 476-9763. - - - Dati incompatibili rilevati durante l'accesso. -Contattare support@secondlife.com. - - - Il tuo account è in fase di leggera manutenzione. -Il tuo account non è accessibile fino alle -[TIME] fuso orario del Pacifico. -Se ritieni che si tratta di un errore, contatta support@secondlife.com. - - - Errore del simulatore in seguito alla richiesta di logout. - - - Il sistema sta eseguendo il logout in questo momento. -Il tuo account non sarà disponibile fino alle -[TIME] fuso orario del Pacifico. - - - Non è possibile creare una sessione valida. - - - Non è possibile collegarsi a un simulatore. - - - Il tuo account può accedere a Second Life solo -tra le [START] e le [END] fuso orario del Pacifico. -Torna durante quell'orario. -Se ritieni che si tratta di un errore, contatta support@secondlife.com. - - - Parametri errati. -Se ritieni che si tratta di un errore, contatta support@secondlife.com. - - - Il parametro Nome deve includere solo caratteri alfanumerici. -Se ritieni che si tratta di un errore, contatta support@secondlife.com. - - - Il parametro Cognome deve includere solo caratteri alfanumerici. -Se ritieni che si tratta di un errore, contatta support@secondlife.com. - - - La regione sta passando allo stato non in linea. -Prova ad accedere nuovamente tra un minuto. - - - L'agente non è nella regione. -Prova ad accedere nuovamente tra un minuto. - - - La regione ha eseguito l'accesso in un'altre sessione. -Prova ad accedere nuovamente tra un minuto. - - - La regione stava eseguendo il logout della sessione precedente. -Prova ad accedere nuovamente tra un minuto. - - - La regione sta ancora eseguendo il logout della sessione precedente. -Prova ad accedere nuovamente tra un minuto. - - - La regione ha eseguito il logout dell'ultima sessione. -Prova ad accedere nuovamente tra un minuto. - - - La regione ha iniziato la procedura di logout. -Prova ad accedere nuovamente tra un minuto. - - - Il sistema ha iniziato il logout dell'ultima sessione. -Prova ad accedere nuovamente tra un minuto. - - - Questa regione sta avendo problemi. Verifica la tua connessione a Internet. - - - Salvataggio delle impostazioni... - - - Uscita... - - - Chiusura... - - - Sei scollegato dalla regione in cui ti trovavi. - - - Sei stato indirizzato in una regione non valida. - - - Verifica scollegamento viewer - - - Persona - - - (nessun nome) - - - Proprietario: - - - Pubblico - - - (Gruppo) - - - In Vendita: [AMOUNT]L$ - - - Costruzione solo con gruppo - - - Divieto di Costruire - - - Costruzione solo con gruppo - - - Non Sicuro - - - Divieto di Volare - - - Script solo con gruppo - - - Script vietati - - - Terreno: - - - Solo un singolo oggetto può essere creato qui - - - - Non puoi rezzare elementi nella tua casella in uscita del rivenditore - - - Almeno uno di questi oggetti non può essere venduto o trasferito. - - - La tua casella in uscita del rivenditore può accettare solo elementi provenienti dal tuo inventario - - - Non puoi mettere gli elementi che indossi nella casella in uscita del rivenditore - - - Non puoi inserire il tuo biglietto da visita nella tua casella in uscita del rivenditore - - - La profondità delle caselle nidificate è maggiore di 3 - - - Il numero di sottocartelle nella cartella al livello più alto è maggiore di 20 - - - Il numero di elementi nella cartella al livello più alto è maggiore di 200 - - - Non puoi spostare una cartella nella relativa cartella secondaria - - - Non puoi spostare una cartella in se stessa - - - Clicca per visitare questa pagina web - - - Clicca per avere maggiori informazioni sul luogo - - - Clicca per vedere il profilo di questo residente - - - Ulteriori informazioni su questo Residente - - - Clicca per disattivare l'audio di questo residente - - - Clicca per attivare l'audio del residente - - - Clicca per inviare un IM a questo residente - - - Clicca per pagare il residente - - - Fai clic per inviare un'offerta di teleport al residente - - - Fai clic per inviare una richiesta di amicizia al residente - - - Clicca per vedere la descrizione del gruppo - - - Clicca per vedere la descrizione dell'evento - - - Clicca per vedere questa inserzione - - - Clicca per vedere la descrizione del lotto - - - Clicca per effettuare il teleport a questa destinazione - - - Clicca per vedere la descrizione dell'oggetto - - - Clicca per vedere questo posto sulla mappa - - - Clicca per avviare il comando secondlife:// - - - - Teleportati a - - - Mostra la mappa per - - - Disattiva audio - - - Riattiva audio - - - IM - - - Paga - - - Offri teleport a - - - Richiesta di amicizia - - - Chiudi (⌘W) - - - Chiudi (Ctrl+W) - - - Chiudi - - - Ripristina - - - Minimizza - - - Distacca - - - Àncora - - - Mostra Aiuto - - - Ricerca in corso... - - - Nessun risultato. - - - Recupero dati in corso... - - - Note sulla versione - - - http://wiki.secondlife.com/wiki/Release_Notes/ - - - In caricamento... - - - (nessuno) - - - (in attesa) - - - (nessuno) - - - Chiamante Avaline [ORDER] - - - Nessun errore - - - Richiesta risorsa: fallita - - - Richiesta risorsa: file non esistente - - - Richiesta risorsa: risorsa non trovata nel database - - - Fine del file - - - Apertura del file non possibile - - - File non trovato - - - Tempo esaurito per il trasferimento file - - - Circuito perso - - - Il programma e il server non combaciano nel prezzo - - - Stato sconosciuto - - - texture - - - suono - - - biglietto da visita - - - punto di riferimento - - - script (vecchia versione) - - - vestiario - - - oggetto - - - biglietto - - - cartella - - - cartella principale - - - script LSL2 - - - bytecode LSL - - - tga texture - - - parte del corpo - - - fotografia - - - oggetti smarriti - - - immagine targa - - - Cestino - - - immagine jpeg - - - animazione - - - gesture - - - simstate - - - preferiti - - - link - - - link alla cartella - - - reticolo - - - (Modifica Aspetto) - - - Assente - - - Occupato - - - Mutato - - - Dispiaciuto - - - Arrabbiato - - - Assente - - - Salto all'indietro - - - Ridere a crepapelle - - - Gran sorriso - - - Lancia un bacio - - - Noia - - - Inchino - - - Applauso - - - Inchino a corte - - - Pianto - - - Ballo 1 - - - Ballo 2 - - - Ballo 3 - - - Ballo 4 - - - Ballo 5 - - - Ballo 6 - - - Ballo 7 - - - Dance 8 - - - Sdegno - - - Bere - - - Imbarazzo - - - Negare col dito - - - Esultare con pugno - - - Yoga fluttuante - - - Acciglio - - - Impazienza - - - Salto di gioia - - - Baciami il sedere - - - Bacio - - - Risata - - - Muscoli da spiaggia - - - No (Scontento) - - - No - - - Na-na-na - - - Uno-due pugno - - - Bocca aperta - - - Pace - - - Indicare altri - - - Indicare te stesso - - - Pugno a sinistra - - - Pugno a destra - - - Contare nella morra cinese - - - Carta nella morra cinese - - - Sasso nella morra cinese - - - Forbici nella morra cinese - - - Repulsione - - - Calcio con rotazione - - - Triste - - - Saluto - - - Urlo - - - Spallucce - - - Sorriso - - - Fumare - - - Fumare inspirazione - - - Fumare mandando giù - - - Sorpresa - - - Colpo di spada - - - Collera - - - Linguaccia - - - Saluto con mano - - - Sussurro - - - Fischio - - - Ammicca - - - Ammicca (Hollywood) - - - Preoccupato - - - Si (Felice) - - - Si - - - Multiple - - - Caricamento in corso... - - - Offline - - - L$ [PRICE] - [AREA] m² - - - Nessun risultato. - - - OK - - - Fine prematura del file - - - Impossibile trovare ROOT o JOINT. - - - sussurra: - - - grida: - - - In connessione alla Voice Chat in-world... - - - Connesso - - - Il voice non è disponibile nel posto dove ti trovi ora - - - Disconnesso dalla Voice Chat in-world - - - Sarai riconnesso alla chat vocale nei dintorni - - - A '[OBJECTNAME]', un oggetto di proprietà di '[OWNERNAME]', situato in [REGIONNAME] [REGIONPOS], è stato concesso il permesso di: [PERMISSIONS]. - - - A '[OBJECTNAME]', un oggetto di proprietà di '[OWNERNAME]', situato in [REGIONNAME] [REGIONPOS], è stato negato il permesso di: [PERMISSIONS]. - - - Se consenti l'accesso al tuo account, consentirai anche all'oggetto di: - - - Prendere dollari Linden (L$) da te - - - Agire sul tuo controllo degli input - - - Rimappare il tuo controllo degli input - - - Animare il tuo avatar - - - Far indossare al tuo avatar - - - Rilasciare la propietà è far diventare pubblico. - - - Collegare e scollegare dagli altri oggetti - - - Aggiungere e rimuovere le giunzioni insieme con gli altri oggetti - - - Cambiare i permessi - - - Tracciare la fotocamera - - - Controllare la tua fotocamera - - - Teleportarti - - - Generale - - - Moderato - - - Adulti - - - Offline - - - Sconosciuto - - - (sconosciuto) - - - Proprietà immobiliare / Regione completa - - - Proprietà immobiliare / Homestead - - - Continente / Homestead - - - Continente / Regione completa - - - Tutti i file - - - Suoni - - - Animazioni - - - Immagini - - - Salva - - - Carica - - - Immagini Targa - - - Immagini Bitmap - - - File video AVI - - - File animazione XAF - - - File XML - - - File RAW - - - Immagini compresse - - - Carica i file - - - Scegli la cartella - - - Script - - - Dizionari - - - Imposta come non assente - - - Imposta come assente - - - Imposta come non occupato - - - Imposta come occupato - - - Figura corporea - - - Pelle - - - Capigliature - - - Occhi - - - Camicia - - - Pantaloni - - - Scarpe - - - Calzini - - - Giacca - - - Guanti - - - Maglietta intima - - - Slip - - - Gonna - - - Alfa (Trasparenza) - - - Tatuaggio - - - Fisica - - - non valido - - - nessuno - - - Camicia non indossata - - - Pantaloni non indossati - - - Scarpe non indossate - - - Calzini non indossati - - - Giacca non indossata - - - Guanti non indossati - - - Maglietta intima non indossata - - - Slip non indossati - - - Gonna non indossata - - - Alpha non portato - - - Tatuaggio non portato - - - Fisica non indossata - - - non valido - - - Crea nuova figura corporea - - - Crea nuova pelle - - - Crea nuovi capelli - - - Crea nuovi occhi - - - Crea nuova camicia - - - Crea nuovi pantaloni - - - Crea nuove scarpe - - - Crea nuove calze - - - Crea nuova giacca - - - Crea nuovi guanti - - - Crea nuova maglietta intima - - - Crea nuovi slip - - - Crea nuova gonna - - - Crea nuovo Alpha - - - Crea un nuovo tatuaggio - - - Crea nuova fisica - - - non valido - - - Nuovo [WEARABLE_ITEM] - - - Avanti - - - OK - - - Avviso di gruppo - - - Avvisi di gruppo - - - Inviato da - - - Allegato: - - - Visualizza gli avvisi precedenti o scegli qui di non riceverne. - - - Apri l'allegato - - - Salva l'allegato - - - Offerta di Teleport - - - Mentre eri assente sono arrivate nuove notifiche... - - - Hai ancora [%d] notifiche - - - Braccio destro - - - Testa - - - Braccio sinistro - - - Gamba sinistra - - - Torace - - - Gamba destra - - - Basso - - - Medio - - - Alto - - - Premi ESC per tornare in visualizzazione normale - - - Non riesci a trovare quello che cerchi? Prova [secondlife:///app/search/all/[SEARCH_TERM] Cerca]. - - - Non riesci a trovare quello che cerchi? Prova [secondlife:///app/search/places/[SEARCH_TERM] Cerca]. - - - Trascina qui un punto di riferimento per aggiungerlo ai Preferiti. - - - Non hai una copia di questa texture nel tuo inventario - - - Gli acquissti dal mercato verranno mostrati qui. Potrai quindi trascinarli nel tuo inventario per usarli. - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/ - - - http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4 - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/imports - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/learn_more - - - Chiunque può vendere oggetti nel Marketplace. - - - - Per diventare un venditore, devi [[MARKETPLACE_CREATE_STORE_URL] creare un negozio nel Marketplace]. - - - La tua casella in uscita è vuota. - - - - Trascina le cartelle in questa area e clicca su "Invia a Marketplace" per metterle in vendita su [[MARKETPLACE_DASHBOARD_URL] Marketplace]. - - - Nessun errore - - - Errore: Prima di inviare elementi al Marketplace devi essere impostato come rivenditore (gratis). - - - Errore: questa cartella non include alcun contenuto. - - - Errore: Oggetto non caricato perché il tuo account venditore ha troppi oggetti che non sono associati con dei prodotti. Per risolvere questo errore, esegui l'accesso al sito di Marketplace e riduci il numero di oggetti non associati. - - - Errore: questo elemento contiene troppi oggetti. Per risolvere questo problema, inserisci più oggetti insieme in una scatola per ridurre a meno di 200 il numero totale di oggetti. - - - Errore: questo elemento contiene troppi livelli di cartelle nidificate. Riorganizzalo per ottenere un massimo di 3 livelli di cartelle nidificate. - - - Errore: Questo elemento non può essere venduto nel Marketplace. - - - Errore: problema con questo elemento. Riprova più tardi. - - - Apri luoghi di riferimento - - - - - - - - - Caricamento del contenuto... - - - Nessun contenuto - - - - - - - - - - - - - - - - - - - - - - - - - - - - Il mio inventario - - - Libreria - - - Texture - - - Suoni - - - Biglietti da visita - - - Punti di riferimento - - - Script - - - Vestiario - - - Oggetti - - - Biglietti - - - Nuova cartella - - - Inventario - - - Immagini non compresse - - - Parti del corpo - - - Cestino - - - Album fotografico - - - Oggetti smarriti - - - Suoni non compressi - - - Animazioni - - - Gesture - - - I miei preferiti - - - I miei preferiti - - - Abbigliamento attuale - - - Vestiario iniziale - - - Il mio vestiario - - - Accessori - - - Reticoli - - - Amici - - - Tutto - - - Nessun allegato indossato - - - Allegati ([COUNT] spazi restanti) - - - Acquista - - - Acquista per L$ - - - Pietra - - - Metallo - - - Vetro - - - Legno - - - Carne - - - Plastica - - - Gomma - - - Luce - - - Maiusc - - - Ctrl - - - Petto - - - Cranio - - - Spalla sinistra - - - Spalla destra - - - Mano sinistra - - - Mano destra - - - Piede sinisto - - - Piede destro - - - Spina dorsale - - - Pelvi - - - Bocca - - - Mento - - - Orecchio sinistro - - - Orecchio destro - - - Bulbo sinistro - - - Bulbo destro - - - Naso - - - Avambraccio destro - - - Braccio destro - - - Avambraccio sinistro - - - Braccio sinistro - - - Anca destra - - - Coscia destra - - - Gamba destra - - - Anca sinista - - - Coscia sinistra - - - Gamba sinistra - - - Stomaco - - - Petto sinistro - - - Petto destro - - - Collo - - - Centro avatar - - - Punto di collegamento non valido - - - Nato da [AGEYEARS] [AGEMONTHS] - - - Nato da [AGEYEARS] - - - Nato da [AGEMONTHS] - - - Nato da [AGEWEEKS] - - - Nato da [AGEDAYS] - - - Iscritto oggi - - - [COUNT] anno - - - [COUNT] anni - - - [COUNT] anni - - - [COUNT] mese - - - [COUNT] mesi - - - [COUNT] mesi - - - [COUNT] settimana - - - [COUNT] settimane - - - [COUNT] settimane - - - [COUNT] giorno - - - [COUNT] giorni - - - [COUNT] giorni - - - [COUNT] iscritto - - - [COUNT] iscritti - - - [COUNT] iscritti - - - Residente - - - In prova - - - Socio onorario - - - Dipendente Linden Lab - - - Informazioni di pagamento usate - - - Informazioni di pagamento registrate - - - Nessuna informazione di pagamento disponibile - - - Età verificata - - - Età non verificata - - - Centro 2 - - - In alto a destra - - - in alto - - - In alto a sinistra - - - Al centro - - - In basso a sinistra - - - In basso - - - In basso a destra - - - Scaricato, in compilazione - - - Script non trovato sul server. - - - Problema nel download - - - Permessi insufficenti per scaricare lo script. - - - Permessi insufficenti per - - - Errore di dowload sconosciuto - - - Avanzamento ricompilazione - - - ricompila - - - Azzera avanzamento - - - azzera - - - Attiva avanzamento - - - attiva - - - Disattiva avanzamento - - - disattiva - - - Compilazione riuscita! - - - Compilazione riuscita, in salvataggio... - - - Salvataggio completato. - - - Script (oggetto fuori portata) - - - Oggetto [OBJECT] di proprietà di [OWNER] - - - nessuno - - - - (Sconosciuto) - - - - - [mthnum,datetime,utc]/[day,datetime,utc]/[year,datetime,utc] - - - - - Saldo - - - Ringraziamenti - - - Debiti - - - Totale - - - Nessun dato trovato per questo gruppo - - - Proprietà principale - - - continente - - - teen - - - chiunque - - - errore - - - tutte le proprietà immobiliari di [OWNER] - - - tutte le tue proprietà immobiliari - - - tutte le proprietà immobiliari che gestisci per conto di [OWNER] - - - Residenti consentiti: ([ALLOWEDAGENTS], massimo [MAXACCESS]) - - - Gruppi ammessi: ([ALLOWEDGROUPS], massimo [MAXACCESS]) - - - Memoria dello script del lotto - - - Lotti in elenco: [PARCELS] - - - Memoria utilizzata: [COUNT] kb di [MAX] kb; [AVAILABLE] kb disponibili - - - Memoria utilizzata: [COUNT] kb - - - URL degli script lotti - - - URL utilizzati: [COUNT] di [MAX]; [AVAILABLE] disponibili - - - URL utilizzati: [COUNT] - - - Errore nella richiesta di informazioni - - - Nessun lotto selezionato - - - Errore: le informazioni sullo script sono disponibili solo nella tua regione attuale - - - Recupero informazioni in corso... - - - Non hai il permesso di visionare questo lotto - - - Seduto su - - - Petto - - - Testa - - - Spalla sinistra - - - Spalla destra - - - Mano sinistra - - - Mano destra - - - Piede sinisto - - - Piede destro - - - Indietro - - - Pelvi - - - Bocca - - - Mento - - - Orecchio sinistro - - - Orecchio destro - - - Occhio sinistro - - - Occhio destro - - - Naso - - - Braccio destro - - - Avambraccio destro - - - Braccio sinistro - - - Avambraccio sinistro - - - Anca destra - - - Coscia destra - - - Coscia destra - - - Anca sinista - - - Coscia sinistra - - - Polpaccio sinistro - - - Addome - - - Petto destro - - - Petto sinistro - - - HUD in centro 2 - - - HUD alto a destra - - - HUD alto in centro - - - HUD alto a sinistra - - - HUD in centro 1 - - - HUD basso a sinistra - - - HUD basso - - - HUD basso a destra - - - Riga [LINE], Colonna [COLUMN] - - - [COUNT] trovato/i - - - Contenuto dell'oggetto - - - Nuovo script - - - Il residente al quale hai inviato un messaggio è in modalità 'occupato', ovvero ha chiesto di non essere disturbato. Il tuo messaggio comparirà nel suo pannello IM, dove potrà essere letto in un secondo momento. - - - (In base al nome) - - - (Residente) - - - (Oggetto) - - - (Gruppo) - - - (esterno) - - - Non esiste alcun regolamento per questa proprietà. - - - Non esiste alcun regolamento per questa proprietà. Il terreno di questa proprietà è messo in vendita dal proprietario, non dalla Linden Lab. Contatta il proprietario del terreno per i dettagli della vendita. - - - - - - Di proprietà di un gruppo - - - Pubblica - - - Impostazioni locali - - - Impostazioni regione - - - Clicca: [TELEPORT] teleport, [MAP] mappa, [PROFILE] profilo - - - (si aggiornerà dopo la pubblicazione) - - - Non hai creato luoghi preferiti né inserzioni. Clicca il pulsante + qui sotto per creare un luogo preferito o un'inserzione. - - - L'utente non ha luoghi preferiti né inserzioni - - - Caricamento in corso... - - - Anteprima - - - Beni immobiliari - - - Un oggetto denominato - - - di proprietà del gruppo - - - di proprietà di un gruppo sconosciuto - - - di proprietà di - - - di proprietà di un utente sconosciuto - - - Ti ha offerto - - - Non hai accettato [DESC] da <nolink>[NAME]</nolink>. - - - Totale - - - comprato - - - ti ha pagato - - - ha pagato - - - ha comprato il pass - - - pagato la tassa per l'evento - - - pagato il premio per l'evento - - - Saldo - - - Ringraziamenti - - - Debiti - - - [weekday,datetime,utc] [mth,datetime,utc] [day,datetime,utc], [year,datetime,utc] - - - Contenuto - - - Oggetti acquisiti - - - Annulla - - - Il caricamento di [NAME] costa L$ [AMOUNT] - - - L'acquisto di [NAME] costa L$ [AMOUNT] - - - Estensione del file sconosciuta [.%s] -Tipi conosciuti .wav, .tga, .bmp, .jpg, .jpeg, or .bvh - - - Blocca - - - Aggiungi punto di riferimento... - - - Modifica punto di riferimento... - - - ⌃ - - - ⌘ - - - ⌥ - - - ⇧ - - - Ctrl+ - - - Alt+ - - - Shift+ - - - File salvato - - - In ricezione - - - antemeridiane - - - pomeridiane - - - Ora Pacifico - - - Ora legale Pacifico - - - Avanti - - - Sinistra - - - Destra - - - Indietro - - - Nord - - - Sud - - - Ovest - - - Est - - - Su - - - Giù - - - Qualsiasi categoria - - - Acquisti - - - Affitto terreno - - - Affitto proprietà - - - Attrazioni speciali - - - Nuovi prodotti - - - Lavoro - - - Cercasi - - - Servizio - - - Personale - - - Nessuno - - - Luogo dei Linden - - - Adult - - - Arte & Cultura - - - Affari - - - Educazione - - - Gioco - - - Divertimento - - - Accoglienza nuovi residenti - - - Parchi & Natura - - - Residenziale - - - Fase - - - Altro - - - Affitto - - - Tutti - - - Tu - - - Più supporti - - - Riproduci/Pausa supporto - - - Un errore è stato riscontrato analizzando la linea di comando. -Per informazioni: http://wiki.secondlife.com/wiki/Client_parameters -Errore: - - - Uso linea di comando del programma [APP_NAME] : - - - Il programma [APP_NAME] non è in grado di accedere ad un file necessario. - -Potrebbe darsi che tu abbia copie multiple attivate o che il tuo sistema reputi erroneamente che il file sia già aperto. -Se il problema persiste, riavvia il computer e riprova. -Se il problema continua ancora, dovresti completamente disinstallare l'applicazione [APP_NAME] e reinstallarla. - - - Errore critico - - - Il programma [APP_NAME] richiede un processore con AltiVec (G4 o superiore). - - - Il programma [APP_NAME] è già attivo. -Controlla che il programma non sia minimizzato nella tua barra degli strumenti. -Se il messaggio persiste, riavvia il computer. - - - Sembra che [APP_NAME] si sia bloccata o interrotta nella sessione precedente. -Vuoi mandare un crash report? - - - Avviso - - - Il programmma [APP_NAME] non riesce a trovare una DirectX 9.0b o superiore. -[APP_NAME] usa DirectX per rilevare hardware e/o i driver non aggiornati che possono causare problemi di stabilità, scarsa performance e interruzioni. Benché tu possa avviare il programma [APP_NAME] senza di esse, consigliamo caldamente l'esecuzione con DirectX 9.0b. - -Vuoi continuare? - - - Attenzione - - - L'aggiornamento automatico non è stato ancora realizzato per Linux. -Consigliamo di scaricare l'ultima versione direttamente da www.secondlife.com. - - - RegisterClass non riuscito - - - Errore - - - Impossibile visualizzare a schermo intero con risoluzione [WIDTH] x [HEIGHT]. -Visualizzazione corrente in modalità finestra. - - - Errore di arresto durante il tentativo di chiusura della finestra (DestroyWindow() non riuscito) - - - Errore di arresto - - - Impossibile caricare i driver GL - - - Impossibile trovare un formato pixel adatto - - - Impossibile ottenere una descrizione del formato pixel - - - [APP_NAME] richiede True Color (32 bit) per funzionare. -Vai alle impostazioni dello schermo del tuo computer e imposta il colore in modalità 32 bit. - - - [APP_NAME] non funziona poichè è impossibile trovare un canale alpha a 8 bit. Questo problema normalmente deriva dai driver della scheda video. -Assicurati di avere installato i driver della scheda video più recenti. -Assicurati anche che il monitor sia impostato a True Color (32 bit) nel Pannello di controllo > Schermo > Impostazioni. -Se il messaggio persiste, contatta [SUPPORT_SITE]. - - - Impossibile impostare il formato pixel - - - Impossibile creare il GL rendering - - - Impossibile attivare il GL rendering - - - [APP_NAME] Non riesce ad avviarsi perchè i driver della tua scheda video non sono stati installati correttamente, non sono aggiornati, o sono per un hardware non supportato. Assicurati di avere i driver della scheda video più recenti e anche se li hai installati, prova a installarli di nuovo. - -Se il messaggio persiste, contatta [SUPPORT_SITE]. - - - Barba leggera - - - Tutti bianchi - - - Occhi grandi - - - Arcuato - - - Lunghezza braccia - - - Attaccato - - - Lobi attaccati - - - Frangetta all'indietro - - - Larghi - - - Frange - - - Occhi piccoli - - - Punto vita - - - Grande - - - Sedere grande - - - Capigliatura grande: Indietro - - - Capigliatura grande: anteriore - - - Capigliatura grande: in alto - - - Grande testa - - - Grandi pettorali - - - Capelli con punte - - - Nero - - - Biondo - - - Capelli biondi - - - Fard - - - Colore fard - - - Opacità fard - - - Definizione muscolare - - - Grasso corporeo - - - Lentiggini e nei - - - Corpo più robusto - - - Robustezza del corpo - - - Corpo più magro - - - Gambe arcuate - - - Altezza del seno - - - Décolleté - - - Grandezza del seno - - - Larghezza setto - - - Largo - - - Grandezza delle sopracciglia - - - Occhi sporgenti - - - Occhi sporgenti - - - Bulboso - - - Naso bulboso - - - Massa seno - - - Lisciatura seno - - - Gravità seno - - - Resistenza seno - - - Massimo effetto - - - Elasticità - - - Guadagno - - - Attenuazione - - - Massimo effetto - - - Elasticità - - - Guadagno - - - Attenuazione - - - Massimo effetto - - - Elasticità - - - Guadagno - - - Attenuazione - - - Massa pancia - - - Lisciatura pancia - - - Gravità pancia - - - Resistenza pancia - - - Massimo effetto - - - Elasticità - - - Guadagno - - - Attenuazione - - - Massa natiche - - - Lisciatura natiche - - - Gravità natiche - - - Resistenza natiche - - - Massimo effetto - - - Elasticità - - - Guadagno - - - Attenuazione - - - Massimo effetto - - - Elasticità - - - Guadagno - - - Attenuazione - - - Sopracciglia cespugliose - - - Capelli a cespuglio - - - Grandezza del sedere - - - Gravità natiche - - - Crinolina - - - Nessuna crinolina - - - Più crinolina - - - Baffetti - - - Zigomi - - - Ampiezza del torace - - - Angolo del mento - - - Fossetta sul mento - - - Barba sottomento - - - Profondità mento - - - Mento forte - - - Mento in dentro - - - Mento sporgente - - - Mento-collo - - - Trasparente - - - Fossetta - - - Occhi ravvicinati - - - Chiusa - - - Chiuso dietro - - - Chiuso davanti - - - Chiuso sinistra - - - Chiuso destra - - - Meno pronunciati - - - Colletto posteriore - - - Colletto anteriore - - - Angolo all'ingiù - - - Angolo all'insù - - - Piega - - - Naso storto - - - Svasato con risvolto - - - Scuro - - - Verde scuro - - - Più scuro - - - Profondo - - - Tacchi standard - - - Folti - - - Doppio mento - - - All'ingiù - - - Più pronunciati - - - Angolo orecchie - - - Grandezza orecchie - - - Estremità orecchie - - - Ovalizzazione testa - - - Occhiaie - - - Colore degli occhi - - - Profondità degli occhi - - - Luminosità degli occhi - - - Apertura degli occhi - - - Prominenza degli occhi - - - Grandezza occhi - - - Distanza occhi - - - Arco delle sopracciglia - - - Densità delle sopracciglia - - - Altezza delle sopracciglia - - - Sopracciglia appuntite - - - Grandezza sopracciglia - - - Lunghezza delle ciglia - - - Eyeliner - - - Colore dell'eyeliner - - - Occhi sporgenti - - - Taglio del viso - - - Definizione del viso - - - Occhi distanti - - - Labbra carnose - - - Femmina - - - Senza dita - - - Dita - - - Risvolti svasati - - - Piatto - - - Sedere piatto - - - Testa piatta - - - Punta piatta - - - Misura piede - - - Angolo della fronte - - - Fronte sporgente - - - Lentiggini - - - Frangetta - - - Dietro gonfi - - - Eyeliner marcato - - - Anteriore gonfio - - - Lati capelli gonfi - - - Lati gonfi - - - Lucido - - - Dita con guanti - - - Lunghezza guanti - - - Capigliature - - - Capelli: Indietro - - - Capelli: anteriore - - - Capelli: lati - - - Direzione capigliatura - - - Foltezza - - - Foltezza - - - Inclinazione - - - Verso sinistra - - - Verso destra - - - Capelli: Volume - - - Grandezza mani - - - Baffi a manubrio - - - Lunghezza testa - - - Forma della testa - - - Grandezza della testa - - - Allungamento testa - - - Altezza tacchi - - - Forma tacchi - - - Altezza - - - Alto - - - Tacchi alti - - - Mandibola alta - - - Alta - - - Alto e stretto - - - Più alto - - - Altezza bacino - - - Larghezza bacino - - - Dentro - - - Colore ombretto interno - - - Opacità ombretto interno - - - Angolo interno - - - Ombretto interno - - - Ombretto interno - - - Lunghezza giacca - - - Grinze della giacca - - - Angolo mandibola - - - Prognatismo mento - - - Forma del mento - - - Iscriviti - - - Guance - - - Angolo ginocchia - - - Gambe ad X - - - Grande - - - Mani grandi - - - Riga a sinistra - - - Lunghezza gambe - - - Muscoli gambe - - - Meno - - - Meno grasso corporeo - - - Meno - - - Meno lentiggini - - - Meno piene - - - Più alto - - - Meno maniglie - - - Meno muscoli - - - Meno muscolari - - - Meno rosato - - - Meno rotondo - - - Meno a sella - - - Meno quadrato - - - Meno volume - - - Meno - - - Più leggero - - - Distanza fossetta labbro - - - Prof. fossetta labbro - - - Volume labbra - - - Tonalità rosa labbra - - - Proporzione labbra - - - Carnosità labbra - - - Larghezza labbra - - - Lipgloss - - - Rossetto - - - Colore rossetto - - - Lungo - - - Testa lunga - - - Bacino alto - - - Gambe lunghe - - - Collo lungo - - - Codini lunghi - - - Codino lungo - - - Torace lungo - - - Braccia lunghe - - - Pantaloni ampi - - - Camicia ampia - - - Maniche non attillate - - - Maniglie dell'amore - - - Basso - - - Tacchi bassi - - - Mandibola bassa - - - Bassa - - - Basso e ampio - - - Più basso - - - Parte bassa del setto - - - Guance inferiori - - - Maschio - - - Riga nel mezzo - - - Altro - - - Più fard - - - Più grasso corporeo - - - Più - - - Più ombretto - - - Più lentiggini - - - Più piene - - - Più calato - - - Più rossetto - - - Più maniglie - - - Labbro inf. pronunciato - - - Più muscoli - - - Più muscolatura - - - Più rosato - - - Più rotondo - - - Più a sella - - - Più orizzontale - - - Più quadrato - - - Labbro sup. pronunciato - - - Più verticale - - - Più volume - - - Più - - - Baffi - - - Angolo della bocca - - - Posizione della bocca - - - Moicana - - - Muscolatura - - - Basette lunghe - - - Smalto - - - Colore smalto - - - Socchiusi - - - Laterali post. vicini - - - Laterali ant. vicini - - - Labbra strette - - - Naturale - - - Lunghezza del collo - - - Grandezza del collo - - - Senza fard - - - Senza eyeliner - - - Senza ombretto - - - Senza lipgloss - - - Senza rossetto - - - Senza riga - - - Senza smalto - - - Senza rosso - - - Senza punte - - - Senza bianco - - - Senza pieghe - - - Inferiore normale - - - Superiore normale - - - Naso a sinistra - - - Naso a destra - - - Grandezza naso - - - Spessore naso - - - Angolo punta naso - - - Forma punta naso - - - Larghezza naso - - - Divisione narici - - - Larghezza narici - - - Opaco - - - Apri - - - Retro aperto - - - Davanti aperto - - - Lato sin. aperto - - - Lato des. aperto - - - Arancio - - - Fuori - - - Colore ombretto esterno - - - Opacità ombretto esterno - - - Angolo esterno occhio - - - Ombretto esterno - - - Ombreggiatura esterna - - - Denti sup. in fuori - - - Genitali - - - Unghie smaltate - - - Pallido - - - Cavallo - - - Vestibilità pantaloni - - - Lunghezza pantaloni - - - Taglia pantalone - - - Pantaloni con le grinze - - - Con riga - - - Frangetta divisa - - - Pettorali - - - Pigmento - - - Codini - - - Rosa - - - Più rosato - - - Altezza pianta - - - Larghezza pianta - - - Appuntito - - - Tacchi a spillo - - - Codino - - - Gonna gonfia - - - Sinistro più aperto - - - Destro più aperto - - - Paffute - - - Palpebre gonfie - - - Tonalità - - - Presenza di rosso nei capelli - - - Normale - - - Riga a destra - - - Incarnato - - - Rotondo - - - Rossore - - - Rosse - - - Capelli mossi - - - Rotondità fianchi - - - Gambe magre - - - Separati - - - Meno pronunciato - - - Taglio posteriore - - - Taglio del viso - - - Taglio anteriore - - - Distorto a sinistra - - - Distorto a destra - - - Taglio verso dietro - - - Taglio verso davanti - - - A sinistra - - - Spostamento bocca - - - A destra - - - Parte inferiore camicia - - - Vestibilità camicia - - - Camicia con le grinze - - - Altezza scarpe - - - Basso - - - Braccia corte - - - Gambe corte - - - Collo corto - - - Codini corti - - - Codino corto - - - Basette corte - - - Torace corto - - - Bacino corto - - - Spalle - - - Ciuffi laterali - - - Basette - - - Capigliatura di lato - - - Capigliatura di lato sciolta - - - Capigliatura di lato raccolta - - - Collo fino - - - Vestibilità gonna - - - Lunghezza gonna - - - Fronte inclinata - - - Lunghezza maniche - - - Morbidezza maniche - - - Spacco: Indietro - - - Spacco: anteriore - - - Spacco: Sinistra - - - Spacco: Destra - - - Piccola - - - Mani piccole - - - Testa piccola - - - Liscio - - - Capelli lisci - - - Lunghezza calze - - - Pizzetto labbro inferiore - - - Piu rade - - - Capelli a punta - - - Quadrato - - - Punta quadrata - - - Testa schiacciata - - - Testa allungata - - - Scarne - - - Senza pettorali - - - Occhi infossati - - - Indietro - - - Avanti - - - Alto - - - Ravv. lat. posteriore - - - Ravv. lat. frontale - - - Tacchi spessi - - - Collo grosso - - - Punta spessa - - - Sottili - - - Sopracciglia sottili - - - Labbra sottili - - - Naso sottile - - - Mento stretto - - - Fondo stretto - - - Pantaloni attillati - - - Camicia attillata - - - Gonna attillata - - - Maniche strette - - - Forma della punta - - - Spessore della punta - - - Lunghezza del torace - - - Muscoli del torace - - - Torso Scrawny - - - Distaccato - - - Senza piega - - - Denti inf. in fuori - - - Innaturale - - - Parte alta del setto - - - Parte alta degli zigomi - - - Fossetta sup. del mento - - - Piega palpebra sup. - - - All'insù - - - Molto rossi - - - Vita alta - - - Pienotte - - - Capelli bianchi - - - Largo - - - Dietro largo - - - Davanti largo - - - Labbra larghe - - - Colorati - - - Grinze - - - Aggiungi ai miei punti di riferimento - - - Modifica i miei punti di riferimento - - - Maggiori informazioni sulla posizione attuale - - - La cronologia delle mie posizioni - - - Regione con categoria adulti - - - Regione con categoria moderata - - - Regione generale - - - Avatar visibili e chat consentita fuori di questo lotto - - - Gli oggetti che si muovono potrebbero non comportarsi correttamente in questa regione fino a quando non viene eseguito il rebake della regione. - - - Il pathfinding dinamico non è attivato in questa regione. - - - Aggiornamento [APP_NAME] - - - Aggiornamento di [APP_NAME]... - - - Installazione di [APP_NAME]... - - - Il Viewer del programma [APP_NAME] si sta aggiornando all'ultima versione. Potrebbe volerci del tempo, attendi. - - - Download dell'aggiornamento... - - - Download dell'aggiornamento - - - Download dell'aggiornamento non riuscito - - - Il programma [APP_NAME] ha riscontrato un'errore durante il tentativo di aggiornamento. Consigliamo di scaricare l'ultima versione direttamente da www.secondlife.com. - - - Installazione dell'aggiornamento non riuscita - - - Errore nell'avvio del viewer - - - [APP_NAME]: Oggetti in arrivo troppo velocemente da [FROM_NAME], anteprima automatica disattivata per [TIME] secondi - - - [APP_NAME]: Oggetti in arrivo troppo velocemente, anteprima automatica disattivata per [TIME] secondi - - - -- Registrazione messaggi instantanei abilitata -- - - - [NAME] sta scrivendo... - - - (anonimo) - - - (Moderato: Voci disattivate di default) - - - La chat di testo non è disponibile per questa chiamata. - - - La chat di testo è stata disabilitata da un moderatore di gruppo. - - - Clicca qui per inviare un messaggio instantaneo. - - - A - - - (Moderatore) - - - (Salvato [LONG_TIMESTAMP]) - - - Per vedere questo messaggio, devi deselezionare 'Solo amici e gruppi possono chiamarmi o mandarmi IM' in Preferenze/Privacy. - - - Risposto alla chiamata - - - Hai iniziato una chiamata vocale - - - Ti sei collegato alla chiamata in voce - - - [NAME] ha iniziato una chiamata vocale - - - Collegamento alla chiamata vocale... - - - Collegato, clicca Chiudi chiamata per agganciare - - - Chiusa la chiamata - - - Chiamata in conferenza con [AGENT_NAME] - - - (La sessione IM non esiste) - - - Sei l'unico utente di questa sessione. - - - [NAME] è offline - - - Clicca il tasto [BUTTON NAME] per accettare/connetterti a questa voice chat. - - - Hai bloccato questo residente. Quando gli invii un messaggio, verrà automaticamente sbloccato. - - - Errore nella richiesta, riprova più tardi. - - - Errore durante la richiesta, riprova più tardi. - - - Non hai sufficienti permessi. - - - Questa sessione non esiste più - - - Non hai questa abilitazione. - - - Non hai questa abilitazione. - - - Non sei un moderatore. - - - Il moderatore del gruppo ha disattivato la tua chat di testo. - - - Un moderatore di gruppo ti ha disabilitato dalla chat di testo. - - - Impossibile aggiungere utenti alla chat con [RECIPIENT]. - - - Impossibile spedire il tuo messaggio nella sessione chat con [RECIPIENT]. - - - Impossibile inviare il messaggio nella chat con [RECIPIENT]. - - - Errore durante la moderazione. - - - Sei stato rimosso dal gruppo. - - - Sei stato espulso dal gruppo. - - - Non hai più le abilitazioni per rimanere nella sessione chat. - - - [SOURCES] ha detto qualcosa di nuovo - - - [SOURCES] ha detto qualcosa di nuovo - - - Sessione di inizializzazione scaduta - - - Posizione di base impostata. - - - http://secondlife.com/landing/voicemorphing - - - [NAME] ti ha inviato un pagamento di L$[AMOUNT] [REASON]. - - - [NAME] ti ha inviato un pagamento di L$[AMOUNT]. - - - Hai inviato un pagamento di L$[AMOUNT] a [NAME] [REASON]. - - - Hai pagato L$ [AMOUNT]. - - - Hai inviato un pagamento di L$[AMOUNT] a [NAME]. - - - Hai pagato L$ [AMOUNT] [REASON]. - - - Non hai pagato [NAME] L$[AMOUNT] [REASON]. - - - Non hai pagato L$ [AMOUNT]. - - - Non hai pagato [NAME] L$[AMOUNT]. - - - Non hai pagato L$ [AMOUNT] [REASON]. - - - per [ITEM] - - - per un lotto di terreno - - - per un permesso di accesso al terreno - - - per la cessione di terreno - - - per creare un gruppo - - - per aderire a un gruppo - - - per caricare - - - per pubblicare un annuncio - - - Contributo di L$ [AMOUNT] - - - Il costo per il caricamento è di L$ [AMOUNT] - - - Il costo è L$ [AMOUNT] - - - L'acquisto del terreno prescelto costa L$ [AMOUNT] - - - Il costo dell'oggetto è L$ [AMOUNT] - - - Tutti - - - Funzionari - - - Proprietari - - - Online - - - Caricamento in corso... - -Segnala abuso - - - Nuova figura corporea - - - Nuova pelle - - - Nuovi capelli - - - Nuovi occhi - - - Nuova camicia - - - Nuovi pantaloni - - - Nuove scarpe - - - Nuove calze - - - Nuova giacca - - - Nuovi guanti - - - Nuova maglietta intima - - - Nuovi slip - - - Nuova gonna - - - Nuovo Alpha (trasparenza) - - - Nuovo tatuaggio - - - Nuova fisica - - - Capo da indossare non valido - - - Nuova gesture - - - Nuovo script - - - Nuovo appunto - - - Nuova cartella - - - Contenuto - - - Gesture - - - Gesture maschili - - - Gesture femminili - - - Altre gesture - - - Gesture del parlato - - - Gesture comuni - - - Maschio - Chiedere scusa - - - Maschio - Levati dai piedi! - - - Maschio - Butta un bacio - - - Maschio - Bu - - - Maschio - Annoiato - - - Maschio - Ehi - - - Maschio - Ridere - - - Maschio - Disgustato - - - Maschio - Spallucce - - - Maschio - Tira fuori la lingua - - - Maschio - Accipicchia - - - Femmina - Risatina - - - Femmina - Pianto - - - Femmina - Imbarazzata - - - Femmina - Chiedere scusa - - - Femmina - Levati dai piedi! - - - Femmina - Butta un bacio - - - Femmina - Bu - - - Femmina - Annoiata - - - Femmina - Ehi - - - Femmina - Ehi tu - - - Femmina - Ridere - - - Femmina - Sei in forma - - - Femmina - Per di qua - - - Femmina - Per cortesia - - - Femmina - Disgustata - - - Femmina - Spallucce - - - Femmina - Tira fuori la lingua - - - Femmina - Accipicchia - - - /inchino - - - /applausi - - - /numero - - - /estingui - - - /chissene - - - /muscolo - - - /no - - - /no! - - - /carta - - - /indicome - - - /indicotu - - - /sasso - - - /forbici - - - /fumo - - - /stiracchiata - - - /fischietto - - - /si - - - /si! - - - non alla tastiera - - - danza1 - - - danza2 - - - danza3 - - - danza4 - - - danza5 - - - danza6 - - - danza7 - - - danza8 - - - [day,datetime,slt]/[mthnum,datetime,slt]/[year,datetime,slt] - - - nessuna/nessuna - - - Impossibile caricare immagini di dimensioni superiori a [WIDTH]*[HEIGHT] - - - - Nonostante i nostri tentativi, si è verificato un errore imprevisto. - - Consulta la pagina status.secondlifegrid.net per determinare se si sia verificato un problema noto con il servizio. - Se il problema continua, ti consigliamo di controllare le tue impostazioni di rete e della firewall. - - - lunedì:martedì:mercoledì:giovedì:venerdì:sabato:domenica - - - lun:mar:mer:gio:ven:sab:dom - - - gennaio:febbraio:marzo:aprile:maggio:giugno:luglio:agosto:settembre:ottobre:novembre:dicembre - - - gen:feb:mar:apr:mag:giu:lug:ago:sett:ott:nov:dic - - - [MDAY] - - - antemeridiane - - - pomeridiane - - - US$ [AMOUNT] - - - Abbonamento - - - Ruoli - - - Identità gruppo - - - Gestione lotto - - - Identità lotto - - - Impostazioni lotto - - - Poteri lotto - - - Accesso al lotto - - - Contenuto lotto - - - Gestione oggetti - - - Contabilità - - - Avvisi - - - Chat - - - Cancellare gli elementi selezionati? - - - Cancellare l’elemento selezionato? - - - Questo vestiario non contiene alcun elemento - - - Seleziona un editor usando le impostazioni ExternalEditor. - - - L'editor esterno specificato non è stato trovato. -Prova a racchiudere il percorso dell'editor in doppie virgolette. -(per es. "/percorso per il mio/editor" "%s") - - - Errore nell'elaborazione del comando dell'editor esterno. - - - L'editor esterno non è stato avviato. - - - Traduzione non riuscita: [REASON] - - - Errore di elaborazione della risposta della traduzione. - - - Esc - - - Space - - - Enter - - - Tab - - - Ins - - - Del - - - Backsp - - - Shift - - - Ctrl - - - Alt - - - CapsLock - - - Home - - - End - - - PgUp - - - PgDn - - - F1 - - - F2 - - - F3 - - - F4 - - - F5 - - - F6 - - - F7 - - - F8 - - - F9 - - - F10 - - - F11 - - - F12 - - - Aggiungi - - - Sottrai - - - Moltiplica - - - Dividi - - - PAD_DIVIDE - - - PAD_LEFT - - - PAD_RIGHT - - - PAD_DOWN - - - PAD_UP - - - PAD_HOME - - - PAD_END - - - PAD_PGUP - - - PAD_PGDN - - - PAD_CENTER - - - PAD_INS - - - PAD_DEL - - - PAD_Enter - - - PAD_BUTTON0 - - - PAD_BUTTON1 - - - PAD_BUTTON2 - - - PAD_BUTTON3 - - - PAD_BUTTON4 - - - PAD_BUTTON5 - - - PAD_BUTTON6 - - - PAD_BUTTON7 - - - PAD_BUTTON8 - - - PAD_BUTTON9 - - - PAD_BUTTON10 - - - PAD_BUTTON11 - - - PAD_BUTTON12 - - - PAD_BUTTON13 - - - PAD_BUTTON14 - - - PAD_BUTTON15 - - - - - - - = - - - ` - - - ; - - - [ - - - ] - - - \ - - - 0 - - - 1 - - - 2 - - - 3 - - - 4 - - - 5 - - - 6 - - - 7 - - - 8 - - - 9 - - - A - - - B - - - C - - - D - - - E - - - F - - - G - - - H - - - I - - - J - - - K - - - L - - - M - - - N - - - O - - - P - - - Q - - - R - - - S - - - T - - - U - - - V - - - W - - - X - - - Y - - - Z - - - Visualizzazione marcatori particelle (blu) - - - Visualizzazione marcatori oggetti fisici (verde) - - - Visualizzazione marcatori oggetti scriptati (rosso) - - - Visualizzazione marcatori oggetti scriptati con funzione tocco (rosso) - - - Visualizzazione marcatori suoni (giallo) - - - Visualizzazione marcatori multimedia (bianco) - - - Particelle nascoste - - - Informazioni sul terreno - - - Aspetto fisico - - - Avatar - - - Costruisci - - - Chat - - - Bussola - - - Destinazioni - - - Gesture - - - Istruzioni - - - Inventario - - - Mappa - - - Mercato - - - Mini mappa - - - Cammina / corri / vola - - - Casella in uscita del rivenditore - - - Persone - - - Preferiti - - - Luoghi - - - Preferenze - - - Profilo - - - Ricerca - - - Istantanea - - - Parla - - - Controlli fotocamera - - - Impostazioni voce - - - Informazioni sul terreno che visiti - - - Cambia l'avatar - - - Seleziona un avatar completo - - - Costruzione oggetti e modifica terreno - - - Chatta con persone vicine usando il testo - - - Bussola - - - Destinazioni interessanti - - - Gesti per il tuo avatar - - - Come eseguire le attività più comuni - - - Visualizza e usa le tue cose - - - Mappa del mondo - - - Vai allo shopping - - - Mostra le persone vicine - - - Movimento avatar - - - Trasferisci elementi al tuo mercato per la vendita - - - Amici, gruppi e persone vicine - - - Luoghi da mostrare come preferiti nel profilo - - - Luoghi salvati - - - Preferenze - - - Modifica o visualizza il tuo profilo - - - Trova luoghi, eventi, persone - - - Scatta una foto - - - Parla con persone vicine usando il microfono - - - Modifica angolo fotocamera - - - I controlli per il volume per le chiamate e per le persone nelle vicinanze nel mondo virtuale - - - attualmente nella barra degli strumenti in basso - - - attualmente nella barra degli strumenti a sinistra - - - attualmente nella barra degli strumenti a destra - - - Mantieni% - - - Dettagli - - - Migliori dettagli - - - Superficie - - - Solido - - - Involucro - - - Anteprima - - - Normale - - - http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer - - - Nessuno - - - Influenza il navmesh - - - Personaggio - - - (Multiple) - - - Molto basso - - - Basso - - - Medio - - - Alto - - - Molto alto - - - Il Residente non può visitare questa regione. - - - [User] - - diff --git a/indra/newview/skins/steam/xui/ja/strings.xml b/indra/newview/skins/steam/xui/ja/strings.xml deleted file mode 100644 index d474703fca..0000000000 --- a/indra/newview/skins/steam/xui/ja/strings.xml +++ /dev/null @@ -1,5054 +0,0 @@ - - - - - Second Life - - - Second Life - - - SECOND LIFE - - - Second Life Grid - - - Second Life サポートポータル - - - ハードウェアの検出中です... - - - [APP_NAME] をインストール中です... - - - キャッシュをクリア中です... - - - テクスチャキャッシュを初期化中です... - - - VFS を初期化中です... - - - グラフィックを初期化できませんでした。グラフィックドライバを更新してください。 - - - 復元中です... - - - 解像度を変更中です... - - - 明るさ全開(レガシー) - - - ログイン中です。[APP_NAME]の表示がフリーズする場合があります。 しばらくお待ちください。 - - - ログイン中です... - - - 認証しています... - - - アカウントのメンテナンスを実行しています... - - - 前回のログインに失敗しました。 ログイン中です([NUMBER] 回目) - - - ワールドを読み込んでいます... - - - 内蔵 Web ブラウザを初期化しています... - - - マルチメディアを初期化しています... - - - フォントをローディング中... - - - キャッシュファイルを検証しています(所要時間は 60 ~ 90 秒)... - - - 対応を処理中です... - - - ワールドを初期化中です... - - - 画像をデコードしています... - - - QuickTime を初期化しています... - - - QuickTime が見つかりません。初期化に失敗しました。 - - - QuickTime が正常に初期化されました。 - - - リージョンの機能をリクエストしています... - - - リージョンの機能をリクエスト、試行 [NUMBER] 回.... - - - リージョンのハンドシェイクを待っています... - - - リージョンに接続しています... - - - 衣類をダウンロードしています... - - - 証明書が無効または壊れています。 グリッド管理者にご連絡ください。 - - - 無効なホストネームがサーバーにアクセスしていました。SLURL かグリッドのホストネームをご確認ください。 - - - グリッドが返した証明書は有効期限が終了していると考えられます。お使いのシステムクロックを確認するか、グリッドの管理者にお問い合わせください。 - - - SSL 証明書のエラーが発生しました。 グリッド管理者にご連絡ください。 - - - サーバーの証明書チェーンに証明書が多すぎます。 グリッド管理者にご連絡ください。 - - - グリッドサーバーが返した証明書の署名を認証できませんでした。グリッドの管理者にお問い合わせください。 - - - ネットワークエラー:接続を確立できませんでした。お使いのネットワーク接続をご確認ください。 - - - ログインに失敗しました。 - - - 終了 - - - http://join.secondlife.com/index.php?sourceid=1206_steam&lang=ja-JP - - - お使いの古いビューワでは Second Life にアクセスできません。以下のページから新しいビューワをダウンロードしてください: -http://secondlife.com/download - -詳細については、FAQ を参照してください: -http://secondlife.com/viewer-access-faq - - - ビューワアップデート(オプション)があります: [VERSION] - - - ビューワアップデート(必須): [VERSION] - - - このエージェントは既にログインされています。 - - - 申し訳ありませんが、ログインできませんでした。 -以下の情報が正しく入力されたことを確認してください: -* ユーザー名(bobsmith12 または steller.sunshine など) -* パスワード -また、Caps Lock キーが有効になっていないこともお確かめください。 - - - セキュリティ上の理由により、あなたのパスワードは変更されました。 -アカウントページ(http://secondlife.com/password)に移動し、 -秘密の質問に答えて、パスワードをリセットしてください。 -ご迷惑をおかけして申し訳ございません。 - - - 弊社はシステムを若干変更しました。お手数ですが、パスワードのリセットをお願いします。 -アカウントページ(http://secondlife.com/password)に移動し、 -秘密の質問に答えて、パスワードをリセットしてください。 -ご迷惑をおかけして申し訳ございません。 - - - Second Life はメンテナンスのため、一時的にご利用いただけません。 -従業員以外にはログインをご遠慮いただいています。 -最新の状況については www.secondlife.com/status でお確かめください。 - - - 既にログインしているユーザーに最上のインワールド体験を提供するため、Second Life へのログインは一時的に制限されています。 - -申し訳ございませんが、有料アカウントのユーザーを優先するため、現在のところ無料アカウントのユーザーには Second Life へのアクセスをご遠慮いただいています。 - - - このパソコンからは Second Life にアクセスできません。 -システムのエラーだと思われる場合は、 -support@secondlife.com にお問い合わせください。 - - - あなたのアカウントは -太平洋時間の [TIME] までご利用いただけません。 - - - 現在リクエストを完了することができません。 -Second Life のサポート(http://secondlife.com/support)にお問い合わせください。 -パスワードを変更できない場合には、(866) 476-9763 に電話でお問い合わせください。 - - - ログイン時にデータの不一致が見つかりました。 -support@secondlife.com にお問い合わせください。 - - - 現在アカウントのメインテナンスが行われています。 -あなたのアカウントは -太平洋時間の [TIME] までご利用いただけません。 -システムのエラーだと思われる場合は、support@secondlife.com にお問い合わせください。 - - - ログアウトをリスクエストしたら、シミュレーターから「fault」が返されました。 - - - システムによるログアウトが実行されました。 -あなたのアカウントは -太平洋時間の [TIME] までご利用いただけません。 - - - 有効なセッションを生成できません。 - - - シミュレーターに接続できませんでした。 - - - あなたの Second Life アカウントにアクセスできるのは、 -太平洋時間の [START] ~ [END] の間に限られます。 -有効な時間帯に再度お試しください。 -システムのエラーだと思われる場合は、support@secondlife.com にお問い合わせください。 - - - パラメーターが正しくありません。 -システムのエラーだと思われる場合は、support@secondlife.com にお問い合わせください。 - - - ファーストネームのパラメーターは英数字です。 -システムのエラーだと思われる場合は、support@secondlife.com にお問い合わせください。 - - - ラストネームのパラメーターは英数字です。 -システムのエラーだと思われる場合は、support@secondlife.com にお問い合わせください。 - - - リージョンはオフライン中です。 -しばらくしてから再度お試しください。 - - - エージェントがリージョンに不在です。 -しばらくしてから再度お試しください。 - - - このリージョンでは別のセッションがログインされていました。 -しばらくしてから再度お試しください。 - - - このリージョンでは前のセッションがログアウトされました。 -しばらくしてから再度お試しください。 - - - このリージョンでは現在も前のセッションのログアウトが処理されています。 -しばらくしてから再度お試しください。 - - - リージョンでは前のセッションがログアウトされました。 -しばらくしてから再度お試しください。 - - - リージョンではログアウトプロセスが開始されました。 -しばらくしてから再度お試しください。 - - - あなたの前のセッションはシステムによってログアウトされています。 -しばらくしてから再度お試しください。 - - - このリージョンに不都合が発生している可能性があります。 ご使用のインターネット接続をご確認ください。 - - - 設定を保存中です... - - - ログアウト中です... - - - シャットダウン中です... - - - あなたがいたリージョンへの接続が切れました。 - - - 無効なリージョンにテレポートされました。 - - - ビューワの接続を切るテスト中 - - - 人 - - - (名前なし) - - - 所有者: - - - 公共 - - - (グループ) - - - 売り出し中:L$ [AMOUNT] - - - グループ作成 - - - 作成禁止 - - - グループ作成 - - - 危険 - - - 飛行禁止 - - - グループスクリプト - - - スクリプトなし - - - 土地: - - - アイテムは 1 つだけここにドラッグできます - - - - マーチャントボックス内のアイテムを Rez することはできません - - - これらオブジェクトの 1 つまたは複数は売り渡したり譲渡したりできないものです。 - - - マーチャントアウトボックスでは、ご自分のインベントリからのアイテムしか受け入れることができません - - - 着用しているアイテムをマーチャントアウトボックスに入れることはできません - - - コーリングカードをマーチャントアウトボックスに入れることはできません - - - ネスト入りフォルダの深さが 3 を超えています - - - 最上位フォルダ内のサブフォルダ数が 20 を超えています - - - 最上位フォルダ内のアイテム数が 200 を超えています - - - フォルダをその子フォルダに移動することはできません - - - フォルダをそのフォルダ自身に移動することはできません - - - クリックしてこの Web ページを見ます - - - クリックしてこの場所の情報を見ます - - - クリックしてこの住人のプロフィールを見ます - - - この住人の詳細を参照 - - - クリックしてこの住人に対して無視設定をします - - - クリックしてこの住人に対する無視設定を解除します - - - クリックしてこの住人に IM を送ります - - - クリックしてこの住人に支払います - - - クリックしてこの住人にテレポートのリクエストを送ります - - - クリックしてこの住人にフレンド登録リクエストを送ります - - - クリックしてこのグループの説明文を見ます - - - クリックしてこのグループの説明文を見ます - - - クリックしてこのクラシファイド広告を見ます - - - クリックしてこの区画の説明文を見ます - - - クリックしてこの場所にテレポートします - - - クリックしてこのオブジェクトの説明文を見ます - - - クリックしてこの場所を地図に表示します - - - クリックして secondlife:// コマンドを出します - - - - テレポート - - - 地図に表示 - - - 無視 - - - ミュート解除 - - - IM - - - 支払う - - - 次の場所にテレポートを送ります: - - - フレンド登録リクエスト - - - 閉じる (⌘W) - - - 閉じる (Ctrl+W) - - - 閉じる - - - 復元 - - - 元に戻す(縮小) - - - 別ウィンドウで開く - - - ドッキング - - - ヘルプを表示 - - - 検索中... - - - 見つかりませんでした。 - - - 取得中... - - - リリースノート - - - http://wiki.secondlife.com/wiki/Release_Notes/ - - - ローディング... - - - (なし) - - - (待機中) - - - (複数) - - - (なし) - - - Avaline コール [ORDER] - - - エラーなし - - - アセットリクエスト: 失敗 - - - アセットリクエスト: 存在しないファイル - - - アセットリクエスト: データベースに見つかりませんでした - - - 終了 - - - ファイルを開けません - - - ファイルが見つかりません - - - ファイルの転送タイムアウト - - - サーキットエラー - - - ビューワとサーバーの価格が一致していません - - - 不明のステータス - - - テクスチャ - - - サウンド - - - コーリングカード - - - ランドマーク - - - レガシースクリプト - - - 衣類 - - - オブジェクト - - - ノートカード - - - フォルダ - - - ルート - - - LSL2 スクリプト - - - LSL バイトコード - - - tga テクスチャ - - - 身体部位 - - - スナップショット - - - 紛失物 - - - targa 画像 - - - ごみ箱 - - - jpeg 画像 - - - アニメーション - - - ジェスチャー - - - 統計 - - - お気に入り - - - リンク - - - フォルダのリンク - - - メッシュ - - - (容姿の編集中) - - - 一時退席中 - - - 取り込み中 - - - ブロック中 - - - 怖れ - - - 怒り - - - 退席中 - - - 後ろ宙返り - - - 大笑い - - - 満面の笑顔 - - - 投げキッス - - - 退屈 - - - おじぎ - - - 拍手 - - - 深いおじぎ - - - 泣く - - - ダンス 1 - - - ダンス 2 - - - ダンス 3 - - - ダンス 4 - - - ダンス 5 - - - ダンス 6 - - - ダンス 7 - - - ダンス 8 - - - 侮蔑 - - - 酔っぱらう - - - 困惑 - - - 指を振る - - - ガッツポーズ - - - ヨガ浮遊 - - - しかめっ面 - - - いらいらする - - - 飛び上がって喜ぶ - - - 挑発ポーズ - - - キス - - - 笑う - - - 力こぶを見せる - - - 不満げに否定する - - - 否定する - - - 冷やかす - - - ワンツー・パンチ - - - 口を開けて驚く - - - ピース・サイン - - - 他人を指差す - - - 自分を指差す - - - 左パンチ - - - 右パンチ - - - じゃんけんポーズ - - - パー - - - グー - - - チョキ - - - 嫌悪感 - - - まわし蹴り - - - 悲しむ - - - 敬礼する - - - 叫ぶ - - - 肩をすくめる - - - 微笑む - - - たばこをくゆらす - - - たばこを吸う - - - たばこを捨てる - - - 驚く - - - 剣で斬りつける - - - じだんだを踏む - - - 舌を出す - - - 手を振る - - - 小声で話す - - - 口笛を吹く - - - ウィンク - - - ウィンク(ハリウッド) - - - 心配する - - - 笑顔で頷く - - - 頷く - - - 複数 - - - ローディング... - - - オフライン - - - [AREA] 平方メートル L$[PRICE] - - - 見つかりませんでした。 - - - OK - - - 不完全なファイル - - - ROOT または JOINT が見つかりません - - - のささやき: - - - の叫び: - - - インワールドボイスチャットに接続中... - - - 接続しました - - - 現在地では、ボイスを利用できません。 - - - インワールドボイスチャットの通話が切断されました - - - 「近くのボイスチャット」に再接続されます - - - [REGIONNAME] の [REGIONPOS] という場所にある、「 [OWNERNAME] 」が所有する「 [OBJECTNAME] 」というオブジェクトは、次の権限を許可しました: [PERMISSIONS] - - - [REGIONNAME] の [REGIONPOS] という場所にある、「 [OWNERNAME] 」が所有する「 [OBJECTNAME] 」というオブジェクトは、次の権限を拒否しました: [PERMISSIONS] - - - あなたのアカウントへのアクセスを許可すると、このオブジェクトには次の操作も許可されます: - - - リンデンドル(L$)を支払う - - - 制御入力に基づいて行動 - - - 制御入力のリマップ - - - アバターへのアニメーション動作 - - - アバターに装着 - - - 所有権を解放し公のものとする - - - 他のオブジェクトのリンクとリンク解除 - - - 他のオブジェクトとのジョイントの追加と削除 - - - 権限の変更 - - - カメラ追従 - - - カメラのコントロール - - - あなたをテレポート - - - 接続されていません - - - General - - - Moderate - - - Adult - - - オフライン - - - 不明 - - - (不明) - - - 不動産 / フルリージョン - - - エステート/ホームステッド - - - メインランド/ホームステッド - - - メインランド / フルリージョン - - - 全てのファイル - - - サウンド - - - アニメーション - - - 画像 - - - 保存 - - - ロード - - - Targa 画像 - - - ビットマップ画像 - - - AVI ムービーファイル - - - XAF アニメーションファイル - - - XML ファイル - - - RAW ファイル - - - 圧縮画像 - - - ファイルのロード - - - 参照 - - - スクリプト - - - 辞書 - - - 一時退席中解除 - - - 一時退席中 - - - 取り込み中解除 - - - 取り込み中 - - - シェイプ - - - スキン - - - 髪 - - - 目 - - - シャツ - - - パンツ - - - 靴 - - - 靴下 - - - ジャケット - - - 手袋 - - - 下着シャツ - - - 下着パンツ - - - スカート - - - アルファ - - - タトゥ - - - 物理作用 - - - 無効 - - - なし - - - シャツ未着用 - - - パンツ未着用 - - - 靴未着用 - - - 靴下未着用 - - - ジャケット未着用 - - - 手袋未着用 - - - 下着(上)未着用 - - - 下着(下)未着用 - - - スカート未着用 - - - アルファ未着用 - - - タトゥー未着用 - - - 物理作用なし - - - 無効 - - - 新しいシェイプを作成 - - - 新しいスキンを作成 - - - 新しい髪を作成 - - - 新しい目を作成 - - - 新しいシャツを作成 - - - 新しいパンツを作成 - - - 新しい靴を作成 - - - 新しい靴下を作成 - - - 新しい上着を作成 - - - 新しい手袋を作成 - - - 新しい下着シャツを作成 - - - 新しい下着パンツを作成 - - - 新しいスカートを作成 - - - 新しいアルファを作成 - - - 新しいタトゥを作成 - - - 新しい物理作用を作成 - - - 無効 - - - 新しい [WEARABLE_ITEM] - - - 次へ - - - OK - - - グループ通知 - - - グループ通知 - - - 送信者 - - - 添付: - - - ここで過去の通知を表示するか、メッセージを受け取らない設定をします。 - - - 添付アイテムを開く - - - 添付アイテムを保存 - - - テレポートを送る - - - 不在中に新しい通知が届きました。 - - - あなたにはさらに %d 通の通知があります - - - 右腕 - - - 頭 - - - 左腕 - - - 左脚 - - - 上半身 - - - 右脚 - - - 低 - - - 中 - - - 高 - - - ESC キーを押してワールドビューに戻ります - - - お探しのものは見つかりましたか? [secondlife:///app/search/all/[SEARCH_TERM] 検索] をお試しください。 - - - お探しのものは見つかりましたか? [secondlife:///app/search/places/[SEARCH_TERM] 検索] をお試しください。 - - - ここにランドマークをドラッグしてお気に入りに追加します。 - - - インベントリ内にこのテクスチャのコピーがありません - - - マーケットプレイスで購入した商品はここに表示されます。その後、アイテムをインベントリにドラッグすれば、それらのアイテムを使用できます。 - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/ - - - http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4 - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/imports - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/learn_more - - - マーケットプレイスでは誰でもアイテムを売ることができます。 - - - - マーチャントになりたい方は、[[MARKETPLACE_CREATE_STORE_URL] マーケットプレイスストアを作成]する必要があります。 - - - アウトボックスは空です。 - - - - [[MARKETPLACE_DASHBOARD_URL] マーケットプレイス]に販売するアイテムを一覧するには、フォルダをこのエリアにドラッグし、「マーケットプレイスに送信」をクリックします。 - - - エラーなし - - - エラー:マーケットプレイスにアイテムを送る前に、あなた自身をマーチャント登録する必要があります(登録は無料です)。 - - - エラー:このフォルダは空です。 - - - エラー:あなたのマーチャントアカウントには、商品に関連付けられていないアイテムが多すぎるため、このアイテムをアップロードできませんでした。このエラーを解消するには、マーケットプレイスの Web サイトにログインし、関連付けられていないアイテムの数を減らしてください。 - - - エラー:このアイテムに含まれるオブジェクトが多すぎます。オブジェクトをいくつかボックスにまとめ、オブジェクト数を200以下に減らしてください。 - - - エラー:このアイテムはネスト入りフォルダの階層が多すぎます。ネスト入りフォルダを 3 階層以内にまとめ直してください。 - - - エラー:このアイテムをマーケットプレイスで販売することはできません。 - - - エラー:このアイテムに関して問題が発生しました。しばらくしてからお試しください。 - - - ランドマークを開く - - - - - - - - - コンテンツをロード中です... - - - コンテンツなし - - - - - はい - - - いいえ - - - - - - - - - - - - - - - - - - - - - - - - - - インベントリ - - - ライブラリ - - - テクスチャ - - - サウンド - - - コーリングカード - - - ランドマーク - - - スクリプト - - - 衣類 - - - オブジェクト - - - ノートカード - - - 新規フォルダ - - - インベントリ - - - 圧縮されていない画像 - - - 身体部位 - - - ごみ箱 - - - フォトアルバム - - - 紛失物 - - - 圧縮されていないサウンド - - - アニメーション - - - ジェスチャー - - - お気に入り - - - お気に入り - - - 着用中のアウトフィット - - - 最初のアウトフィット - - - マイ アウトフィット - - - アクセサリ - - - メッシュ - - - フレンド - - - 全員 - - - 着用しているアタッチメントはありません - - - アタッチメント(残りのスロット数:[COUNT]) - - - 買う - - - L$ で購入 - - - 石 - - - 金属 - - - ガラス - - - 木 - - - 肌 - - - プラスチック - - - ゴム - - - ライト - - - Shift - - - Ctrl - - - 胸部 - - - 頭蓋 - - - 左肩 - - - 右肩 - - - 左手 - - - 右手 - - - 左足 - - - 右足 - - - 背骨 - - - 骨盤 - - - 口 - - - あご - - - 左耳 - - - 右耳 - - - 左目眼球 - - - 右目眼球 - - - 鼻 - - - 右上腕 - - - 右前腕 - - - 左上腕 - - - 左前腕 - - - 右腰 - - - 右上脚 - - - 右下脚 - - - 左腰 - - - 左上脚 - - - 左下脚 - - - 腹 - - - 左胸筋 - - - 右胸筋 - - - 首 - - - アバターの中央 - - - 装着先が正しくありません - - - [AGEYEARS] [AGEMONTHS] - - - [AGEYEARS] - - - [AGEMONTHS] - - - [AGEWEEKS] - - - [AGEDAYS] - - - 今日参加 - - - [COUNT] 年 - - - [COUNT] 年 - - - [COUNT] 年 - - - [COUNT] ヶ月 - - - [COUNT] ヶ月 - - - [COUNT] ヶ月 - - - [COUNT] 週間 - - - [COUNT] 週間 - - - [COUNT] 週間 - - - [COUNT] 日間 - - - [COUNT] 日間 - - - [COUNT] 日間 - - - [COUNT] 人 - - - [COUNT] 人 - - - [COUNT] 人 - - - 住人 - - - トライアル - - - 創立メンバー - - - Linden Lab 従業員 - - - 支払情報使用履歴あり - - - 支払情報登録済 - - - 支払情報未登録 - - - 年齢確認済 - - - 年齢未確認 - - - 中心 2 - - - 右上 - - - 上部 - - - 左上 - - - 中心 - - - 左下 - - - 下部 - - - 右下 - - - ダウンロード完了、コンパイル中 - - - サーバー上にスクリプトが見つかりません。 - - - ダウンロードに問題があります - - - 権限不足のためスクリプトをダウンロードできません。 - - - 権限不足: - - - 原因不明の失敗によりダウンロードができません - - - リコンパイル進行 - - - リコンパイル - - - 進行をリセット - - - リセット - - - 実行を続けるよう設定 - - - 実行中に設定 - - - 実行を停止するよう設定 - - - 実行停止に設定 - - - コンパイルが完了しました! - - - コンパイル完了、保存中です... - - - 保存完了。 - - - スクリプト(オブジェクトが範囲外にあります) - - - [OWNER] 所有のオブジェクト「[OBJECT]」 - - - なし - - - - (不明) - - - - - [year,datetime,utc]/[mthnum,datetime,utc]/[day,datetime,utc] - - - - - 残高 - - - 収入 - - - 支出 - - - 合計 - - - グループのデータが見つかりませんでした - - - parent estate - - - メインランド - - - ティーン - - - 全員 - - - エラー - - - [OWNER] が所有するすべての不動産 - - - あなたが所有するすべての不動産 - - - あなたが [OWNER] のために管理するすべての不動産 - - - 許可された住人: ([ALLOWEDAGENTS] 人、最大 [MAXACCESS] 人) - - - 許可されたグループ: ([ALLOWEDGROUPS]、最大 [MAXACCESS] ) - - - 区画スクリプトメモリ - - - 区画一覧: [PARCELS] - - - 使用されたメモリ: [MAX] kb 中 [COUNT] kb:[AVAILABLE] kb 利用可 - - - 使用されたメモリ: [COUNT] kb - - - 区画のスクリプトURL - - - 使用された URL: [MAX] 中 [COUNT] :[AVAILABLE] 利用可 - - - 使用された URL: [COUNT] - - - 情報のリクエスト中にエラーが発生しました - - - 区画が選択されていません。 - - - エラー: スクリプト情報は現在地のみ取得できます - - - 情報を取得中... - - - この区画を調査する権限がありません。 - - - 着席中 - - - 胸部 - - - 頭 - - - 左肩 - - - 右肩 - - - 左手 - - - 右手 - - - 左足 - - - 右足 - - - 背中 - - - 骨盤 - - - 口 - - - あご - - - 左耳 - - - 右耳 - - - 左目 - - - 右目 - - - 鼻 - - - 右腕(上) - - - 右腕(下) - - - 左腕(上) - - - 左腕(下) - - - 右腰 - - - 右脚(上) - - - 右脚(下) - - - 左腰 - - - 左脚(上) - - - 左脚(下) - - - お腹 - - - 右胸筋 - - - 左胸筋 - - - HUD(中央 2) - - - HUD(右上) - - - HUD(上・中央) - - - HUD(左上) - - - HUD(中央 1) - - - HUD(左下) - - - HUD(下) - - - HUD(右下) - - - [LINE] 行目、[COLUMN] 列目 - - - [COUNT] 件見つかりました - - - [hour12,datetime,utc]:[min,datetime,utc] [ampm,datetime,utc] - - - [mthnum,datetime,slt]/[day,datetime,slt] - - - オブジェクトの中身 - - - 新規スクリプト - - - メッセージを送った住人は、誰にも邪魔をされたくないため現在「取り込み中」モードです。 あなたのメッセージは、あとで確認できるように IM パネルに表示されます。 - - - (名称別) - - - (住人) - - - (オブジェクト) - - - (グループ) - - - (外部) - - - この不動産には約款がありません。 - - - この不動産には約款がありません。 この不動産上の土地は不動産所有者により販売され、Linden Lab は販売しません。 販売に関するお問い合わせは、不動産所有者までお願い致します。 - - - - - - グループ所有 - - - パブリック - - - ローカル設定 - - - リージョン(地域)の設定 - - - クリック数: [TELEPORT] テレポート、 [MAP] 地図、 [PROFILE] プロフィール - - - (掲載後更新) - - - ピックやクラシファイド広告を作成していません。 作成するには、下にある「プラス」ボタンをクリックします。 - - - ピック、またはクラシファイド広告がありません - - - ローディング... - - - プレビュー - - - プロパティ - - - オブジェクト名 - - - 所有グループ - - - 不明なグループ所有 - - - 所有者 - - - 不明なユーザー所有 - - - が渡しました - - - <nolink>[NAME]</nolink> からの [DESC] を拒否しました。 - - - 合計 - - - 購入: - - - あなたに支払い: - - - paid into - - - 入場許可を購入: - - - がイベント用の費用を支払いました - - - がイベント用の賞金を支払いました - - - 残高 - - - 収入 - - - 支出 - - - [year,datetime,utc]年[mth,datetime,utc]月[day,datetime,utc]日[weekday,datetime,utc] - - - 内容 - - - 取得アイテム - - - キャンセル - - - [NAME] のアップロード料金は L$[AMOUNT] です - - - これを L$[AMOUNT] で購入します - - - 不明の拡張子: %s -使用可能な拡張子: .wav, .tga, .bmp, .jpg, .jpeg, or .bvh - - - ブロック - - - ブロック - - - ブロック解除 - - - ブロック解除 - - - マイ ランドマークに追加... - - - マイ ランドマークを編集... - - - ⌃ - - - ⌘ - - - ⌥ - - - ⇧ - - - Ctrl+ - - - Alt+ - - - Shift+ - - - ファイルが保存されました - - - 取得中 - - - AM - - - PM - - - 太平洋標準時 - - - 太平洋夏時間 - - - 前 - - - 左 - - - 右 - - - 戻る - - - 北 - - - 南 - - - 西 - - - 東 - - - 上 - - - 下 - - - 全カテゴリ - - - ショッピング - - - 土地のレンタル - - - プロパティのレンタル - - - 特別アトラクション - - - 新製品 - - - 雇用 - - - 求む - - - サービス - - - パーソナル - - - なし - - - Linden 所在地 - - - Adult - - - アートとカルチャー - - - ビジネス - - - 教育的 - - - ゲーム - - - たまり場 - - - 新住人に優しい - - - 公園と自然 - - - 住宅用 - - - ステージ - - - その他 - - - レンタル - - - 全員 - - - あなた - - - : - - - , - - - ... - - - *** - - - ( - - - ) - - - . - - - ' - - - --- - - - 複数のメディア - - - メディアを再生/一時停止 - - - コマンドラインにエラーが見つかりました。 -リンク先を参照してください: http://wiki.secondlife.com/wiki/Client_parameters -エラー: - - - [APP_NAME] コマンドライン使用: - - - [APP_NAME] は必要なファイルにアクセスできません。 - -複数のコピーを実行中か、ファイルが既に開いているとあなたのシステムが誤認識している可能性があります。 -このメッセージが何度も出る場合は、コンピューターを再起動してもう一度お試しください。 -それでも問題が続く場合、[APP_NAME] を完全にアンインストールして、再インストールをしてください。 - - - 致命的なエラー - - - [APP_NAME] は、AltiVec搭載のプロセッサが必要です。(G4 以降) - - - [APP_NAME] はすでに実行中です。 -最小化されたプログラムのコピーをタスクバーで確認してください。 -このメッセージが何度も出る場合はコンピューターを再起動してください。 - - - [APP_NAME] は前回の実行時にフリーズしています。 -クラッシュ報告を送信しますか? - - - 通知 - - - [APP_NAME] は DirectX 9.0b 及びそれ以降のバージョンを検出することができませんでした。 -[APP_NAME] は DirectX を使用して安定性の問題、低パフォーマンス、クラッシュ等悪影響を与えるハードウェアや古いドライバを検出します。 DirectX 9.0b がなくても [APP_NAME] を実行できますが、DirectX 9.0bとのご使用を強く推奨します。 - -続けますか? - - - 警告 - - - 自動更新は現在 Linux には対応していません。 -www.secondlife.com から最新バージョンをダウンロードしてください。 - - - RegisterClass 失敗 - - - エラー - - - [WIDTH] x [HEIGHT] では全画面で実行することができません。 -ウィンドウモードで実行中です。 - - - シャットダウンエラー(DestroyWindow() 失敗) - - - シャットダウンエラー - - - GL ディバイスコンテキストが作れません - - - 適切なピクセル形式が見つかりません - - - ピクセル形式情報が見つかりません - - - [APP_NAME] を実行するには、True Color (32ビット)が必要です。 -お使いのコンピューターの「コントロールパネル」>「画面」>「設定」に行き、「最高 (32 ビット)」に設定してください。 - - - [APP_NAME] は 8 ビットのアルファチャンネルを取得できないため実行できません。 通常ビデオカードのドライバの問題で起こります。 -お使いのコンピューターに最新のビデオカードドライバがインストールされているかご確認ください。 -また、「コントロールパネル」>「画面」>「設定」内で、モニターが「最高 (32 ビット)」に設定されていることもご確認ください。 -このメッセージが何度も出る場合は、[SUPPORT_SITE] へご連絡ください。 - - - ピクセル形式が設定できません - - - GL レンダーコンテキストが作れません - - - GL レンダーコンテキストをアクティベートできません - - - お使いのコンピューターのビデオカードドライバが正常にインストールできなかった、または古いかサポート対象外のため、[APP_NAME] は実行できません。 最新のビデオカードドライバがインストールされているのを確認し、されている場合は再インストールをお試しください。 - -このメッセージが何度も出る場合は、[SUPPORT_SITE] へご連絡ください。 - - - うっすらとしたヒゲ - - - 真っ白 - - - アニメ風の目 - - - アーチ - - - 腕の長さ - - - 小 - - - 耳たぶ - - - 後ろ髪の毛先 - - - たるんだ下まぶた - - - 前髪 - - - ビーズのような目 - - - お腹の大きさ - - - 大 - - - 大 - - - 髪の大部分: 後ろ - - - 髪の大部分: 前 - - - 髪の大部分: 上部 - - - 大 - - - 大 - - - とげあり - - - 黒 - - - ブロンド - - - ブロンドの髪 - - - チーク - - - チークカラー - - - チークの濃さ - - - 体の精細度 - - - 体脂肪 - - - 体のしみ・そばかす - - - 骨太 - - - 体の厚み - - - 細め - - - 外股 - - - 胸の重力 - - - 胸の谷間 - - - 胸の大きさ - - - 両目の間の幅 - - - 広 - - - 眉毛上の隆起 - - - Bug Eyes - - - 突き出た目 - - - だんご - - - だんご鼻 - - - 胸の豊かさ - - - 胸の平滑化 - - - 胸の垂れ具合 - - - 胸の空気抵抗 - - - 最大エフェクト数 - - - 振動速度 - - - 増加率 - - - 減衰 - - - 最大エフェクト数 - - - 振動速度 - - - 増加率 - - - 減衰 - - - 最大エフェクト数 - - - 振動速度 - - - 増加率 - - - 減衰 - - - お腹の豊かさ - - - お腹の平滑化 - - - お腹の垂れ具合 - - - お腹の空気抵抗 - - - 最大エフェクト数 - - - 振動速度 - - - 増加率 - - - 減衰 - - - お尻の豊かさ - - - お尻の平滑化 - - - お尻の垂れ具合 - - - お尻の空気抵抗 - - - 最大エフェクト数 - - - 振動速度 - - - 増加率 - - - 減衰 - - - 最大エフェクト数 - - - 振動速度 - - - 増加率 - - - 減衰 - - - 太 - - - ぼさぼさヘア - - - お尻の大きさ - - - お尻の垂れ具合 - - - 後ろの膨らみ - - - 膨らみなし - - - 膨らみ大 - - - チャップリン - - - ほお骨 - - - 胸部の大きさ - - - あごの角度 - - - あごの先の割れ - - - あごに沿ったひげ - - - あごの長さ - - - あごを強調 - - - ひいたあご - - - 突き出たあご - - - あごと首 - - - クリア - - - 割れた - - - 顔の中心寄りの目 - - - クローズ - - - 後ろとじ - - - 前とじ - - - 左とじ - - - 右とじ - - - コイン入れ - - - 後ろえり - - - 前えり - - - ダウン - - - アップ - - - 重いまぶた - - - 曲がった鼻 - - - 袖口のフレア - - - ダーク - - - ダークグリーン - - - ダーク - - - 尖った - - - デフォルトのかかと - - - 濃 - - - 二重あご - - - 下向き - - - ダッフルバッグ - - - 耳の角度 - - - 耳の大きさ - - - 耳の先 - - - たまご頭 - - - 下まぶた - - - 瞳の色 - - - 目のくぼみ - - - 瞳の明るさ - - - 見開き具合 - - - 両目の大きさの対称 - - - 目の大きさ - - - 目と目のあいだの幅 - - - 眉毛のアーチ - - - 眉毛の密集度 - - - 眉毛の高さ - - - 眉毛の角 - - - 眉毛の大きさ - - - まつげの長さ - - - アイライナー - - - アイライナーの色 - - - 下まぶたがたるんだ目 - - - 顔のゆがみ - - - 顔の精細度 - - - 離れた目 - - - 大 - - - 女性 - - - 指なし - - - 指あり - - - 広がった袖口 - - - 平ら - - - 小 - - - 絶壁頭 - - - フラット - - - 足の大きさ - - - ひたいの角度 - - - ひたいを強調 - - - しみ・そばかす - - - 前髪の毛先 - - - 刈られていない髪 - - - あり - - - 刈られていない髪 - - - サイドの髪 - - - 生え揃ったサイド - - - あり - - - 手袋の指 - - - 手袋の長さ - - - 髪 - - - 髪: 後ろ - - - 髪: 前 - - - 髪: サイド - - - 流す - - - 髪の量 - - - 髪の量 - - - 髪の向き - - - 左向き - - - 右向き - - - 髪: ボリューム - - - 手の大きさ - - - ハンドルバー - - - 頭の長さ - - - 頭の形 - - - 頭の大きさ - - - 縦横の長さ - - - ヒールの高さ - - - ヒールの形 - - - 身長 - - - 高 - - - ハイヒール - - - 高 - - - 厚底 - - - 高めでタイト - - - 高 - - - 腰の長さ - - - 腰の幅 - - - 内向き - - - 内側のシャドウカラー - - - 内側のシャドウの濃さ - - - 目頭 - - - 内側のアイシャドウ - - - 内側のシャドウ - - - ジャケット丈 - - - ジャケットのしわ - - - あごの角度 - - - あごの突出 - - - あごの形 - - - 寄せた胸 - - - えら - - - 膝の角度 - - - 内股 - - - 大 - - - 大 - - - 左分け - - - 脚の長さ - - - 脚の筋肉 - - - 小 - - - 少なめ - - - 少なめ - - - 少なめ - - - 薄い - - - 少 - - - 少 - - - 少なめ - - - 少なめ - - - 少な目 - - - 丸み少な目 - - - 小 - - - 小 - - - ボリューム少な目 - - - 小 - - - ライト - - - 唇の山 - - - 唇の山の高さ - - - 唇の厚み - - - 唇の赤み - - - 上下唇の大きさ - - - 唇の前後幅 - - - 口の大きさ - - - リップグロス - - - 口紅 - - - 口紅の色 - - - ロング - - - 前後幅が広い頭 - - - 長 - - - 長 - - - 長 - - - 長 - - - 長 - - - 長 - - - 長 - - - ゆったり - - - ゆるめ - - - ゆるめ - - - ウエスト周り - - - 低 - - - ローヒール - - - 低 - - - 低め - - - 低めでゆったり - - - 低 - - - 鼻筋 - - - ほおの下部 - - - 男性 - - - 真ん中分け - - - 大 - - - きつめ - - - 多め - - - 多め - - - あり - - - 多め - - - 厚い - - - 大 - - - あり - - - 大 - - - 大 - - - 多め - - - 筋骨隆々 - - - 多め - - - まる - - - 大 - - - なだらか - - - 四角 - - - 大 - - - 垂直 - - - 大 - - - 大 - - - 口ひげ - - - 口角 - - - 口の位置 - - - モヒカン - - - 筋骨たくましい - - - マトンチョップス - - - マニキュア - - - マニキュアの色 - - - 狭 - - - 狭 - - - 狭 - - - おちょぼ口 - - - ナチュラル - - - 首の長さ - - - 首の太さ - - - なし - - - なし - - - なし - - - なし - - - なし - - - なし - - - なし - - - なし - - - なし - - - なし - - - なし - - - 下 - - - 上 - - - 左曲がり - - - 右曲がり - - - 鼻の大きさ - - - 鼻の厚み - - - 鼻先の角度 - - - 鼻先の形 - - - 鼻の幅 - - - 鼻の穴の高さ - - - 鼻の穴の幅 - - - 濃いめ - - - オープン - - - 後ろあき - - - 前あき - - - 左あき - - - 右あき - - - オレンジ - - - 外向き - - - 外側のシャドウカラー - - - 外側のシャドウの濃さ - - - 目尻 - - - 外側のアイシャドウ - - - 外側のシャドウ - - - 出っ歯 - - - パッケージ - - - あり - - - 悪い - - - パンツの股 - - - パンツのフィット感 - - - パンツ丈 - - - ウエスト - - - パンツのしわ - - - あり - - - 分けた前髪 - - - 胸筋 - - - 色素 - - - 下げ髪 - - - ピンク - - - ピンク - - - 靴底の高さ - - - 靴底の幅 - - - 尖った - - - 幅狭 - - - ポニーテール - - - ふんわり - - - 左目を大きく - - - 右目を大きく - - - ふっくら - - - 目の周りの膨らみ - - - 虹色 - - - 赤毛 - - - 均整のとれた - - - 右分け - - - ほおの赤らみ - - - まるっこい - - - 血色 - - - 良い - - - くしゃくしゃヘア - - - 腰回りの肉付き - - - 骨張った脚 - - - 離れた胸 - - - なだらか - - - 後方を刈る - - - 顔のゆがみ - - - 前方を刈る - - - 左半分を上に - - - 右半分を上に - - - 後ろを刈られた髪 - - - 前を刈られた髪 - - - 左向き - - - 口の向き - - - 右向き - - - シャツの裾 - - - シャツのフィット感 - - - シャツのしわ - - - 靴の長さ - - - ショート - - - 短 - - - 短 - - - 短 - - - 短 - - - 短 - - - 短め - - - 短 - - - 短 - - - 肩 - - - サイドの毛先 - - - もみあげ - - - サイドの髪 - - - ダウン - - - アップ - - - 細 - - - スカートのフィット感 - - - スカート丈 - - - 傾斜した額 - - - 袖丈 - - - 袖のフィット感 - - - スリット: 後ろ - - - スリット: 前 - - - スリット: 左 - - - スリット: 右 - - - 小 - - - 小 - - - 小 - - - 滑らか - - - スムーズ - - - 靴下丈 - - - ソウルパッチ - - - 薄 - - - とげとげヘア - - - 角張った - - - スクエアトゥ - - - カボチャ型 - - - 縦長 - - - こけたほお - - - 小 - - - くぼんだ目 - - - 後ろへ - - - 前へ - - - トール - - - 後ろに先細 - - - 前に先細 - - - 幅広 - - - 太 - - - 厚め - - - 薄い - - - 細 - - - 小 - - - 細い鼻 - - - ひきしまったあご - - - タイト - - - タイト - - - タイト - - - タイト - - - タイト - - - つま先の形 - - - つま先の厚み - - - 上半身の長さ - - - 上半身の筋肉 - - - 上半身の骨張り - - - 大 - - - 軽いまぶた - - - 受け口 - - - ユニーク - - - 両目のあいだ - - - ほおの上部 - - - あご上部の割れ - - - 二重の幅 - - - 上向き - - - 真っ赤 - - - ウエストの高さ - - - つまったほお - - - 白髪 - - - 広 - - - 広 - - - 広 - - - 大きい口 - - - ワイルド - - - しわ - - - マイ ランドマークに追加 - - - マイ ランドマークを編集 - - - 現在地の詳細を見る - - - マイ ロケーション履歴 - - - この土地を購入 - - - ここではボイスの利用ができません - - - 飛行は禁止されています - - - プッシュ禁止 - - - オブジェクトの制作・ドロップは禁止されています - - - スクリプト不可 - - - 体力 - - - Adult リージョン - - - Moderate リージョン - - - General リージョン - - - この区画外にアバターを見えるようにして、チャットも許可 - - - 地域(リージョン)が再構築されるまで、移動するオブジェクトは正しく動作しない可能性があります。 - - - この地域(リージョン)でダイナミックパスファインディングが有効になっていません。 - - - [APP_NAME] アップデート - - - 只今 [APP_NAME] をアップデート中です... - - - [APP_NAME] をインストール中です... - - - お使いの [APP_NAME] ビューワが最新バージョンにアップデートされています。 数分かかることがありますのでしばらくお待ちください。 - - - アップデートをダウンロード中です... - - - アップデートをダウンロード中 - - - アップデートのダウンロードに失敗しました - - - [APP_NAME] をアップデート中にエラーが発生しました。 www.secondlife.com から最新バージョンをダウンロードしてください。 - - - アップデートのインストールに失敗しました - - - ビューワの起動に失敗しました - - - [APP_NAME] : アイテムが [FROM_NAME] から同時にたくさん読み込まれているため、自動プレビューが [TIME] 秒間無効となります。 - - - [APP_NAME] : アイテムが同時にたくさん読み込まれているため、自動プレビューが [TIME] 秒間無効となります。 - - - -- インスタントメッセージの保存開始 -- - - - [NAME] は入力中です... - - - (名前なし) - - - (モデレート: デフォルトでボイスはオフ) - - - このコールでは文字チャットが利用できません。 - - - グループのモデレータが、あなたの文字チャットを使用禁止にしました。 - - - ここをクリックしてインスタントメッセージを開始。 - - - 宛先 - - - (モデレータ) - - - (保存日時:[LONG_TIMESTAMP]) - - - このメッセージを表示するには、「環境設定」の「プライバシー」で「フレンドとグループ以外からはコールと IM を受信しない」チェックボックスをオフにします。 - - - 相手がコールを受けました - - - ボイスコールを開始します - - - ボイスコールに参加しました - - - [NAME] はボイスコールを開始します - - - ボイスコールに参加... - - - 接続しました。コール終了をクリックして切ります - - - ボイスコールから退席しました - - - 接続中... - - - アドホックコンファレンス - - - [AGENT_NAME] とコンファレンスする - - - インベントリアイテムを送りました - - - インベントリからここにアイテムをドラッグします - - - (IM セッションが存在しません) - - - このセッションにいるユーザーはあなただけです。 - - - [NAME] はオフライン中です。 - - - このボイスチャットに応答・接続する場合は、[BUTTON NAME] をクリックしてください。 - - - この住人をブロックしています。 メッセージを送ると、ブロックが自動的に解除されます。 - - - リクエスト中にエラーが発生しました。あとでもう一度お試しください。 - - - 要求中にエラーが発生しました。後でもう一度試してください。 - - - あなたには充分な権限がありません。 - - - このセッションは既に切断されています。 - - - あなたにはその能力がありません。 - - - あなたにはその能力がありません。 - - - あなたはセッションモデレータではありません。 - - - グループのモデレーターが、あなたのテキストチャットを禁止しました。 - - - グループモデレータがあなたのテキストチャットを無効化しました - - - [RECIPIENT] とのチャットセッションにユーザーを追加することができません - - - [RECIPIENT] とのチャットセッションに、メッセージを送信することができません。 - - - [RECIPIENT] とのチャットセッションにメッセージを送ることができません - - - モデレート中にエラーが発生しました。 - - - グループから脱退しました。 - - - あなたはグループから削除されました。 - - - このチャットセッションを継続することはできません - - - [SOURCES] は何か新しいことを言いました。 - - - [SOURCES] は何か新しいことを言いました。 - - - セッションの初期化がタイムアウトしました - - - 家の配置の設定。 - - - http://secondlife.com/landing/voicemorphing - - - [NAME] は [REASON] のために L$[AMOUNT] を支払いました。 - - - [NAME] は L$[AMOUNT] を支払いました。 - - - [NAME] に L$ [AMOUNT] を支払いました:[REASON] - - - L$ [AMOUNT] を支払いました。 - - - [NAME] に L$ [AMOUNT] を支払いました。 - - - L$ [AMOUNT] を支払いました:[REASON] - - - [NAME] に [REASON] の代金 L$ [AMOUNT] を支払えませんでした。 - - - L$ [AMOUNT] を支払えませんでした。 - - - [NAME] に L$ [AMOUNT] を支払えませんでした。 - - - [REASON] の 代金 L$ [AMOUNT] を支払えませんでした。 - - - [ITEM] 向け - - - 土地区画のため - - - 土地の入場許可を得るため - - - 土地を譲渡するため - - - グループを作成するため - - - グループに参加するため - - - アップロードするため - - - クラシファイド広告を掲載する - - - L$[AMOUNT] を渡します - - - アップロード料金は L$[AMOUNT] です - - - 料金は L$[AMOUNT] です - - - 選択した土地を L$ [AMOUNT] で購入します - - - このオブジェクトは L$[AMOUNT] です - - - 全員 - - - オフィサー - - - オーナー - - - オンライン - - - アップロード中... - -嫌がらせの報告 - - - 新しいシェイプ - - - 新しいスキン - - - 新しい髪 - - - 新しい目 - - - 新しいシャツ - - - 新しいパンツ - - - 新しい靴 - - - 新しい靴下 - - - 新しいジャケット - - - 新しい手袋 - - - 新しい下着(上) - - - 新しい下着(下) - - - 新しいスカート - - - 新しいアルファ - - - 新しいタトゥ - - - 新規の物理作用 - - - 無効な着用物 - - - ジェスチャー - - - 新規スクリプト - - - ノート - - - 新規フォルダ - - - コンテンツ - - - ジェスチャー - - - 男性用ジェスチャー - - - 女性用ジェスチャー - - - その他のジェスチャー - - - 会話ジェスチャー - - - 一般的ジェスチャー - - - 男性 - すみません - - - 男性 - Get lost - - - 男性 - 投げキッス - - - 男性 - Boo - - - 男性 - 退屈 - - - 男性 - Hey - - - 男性 - 笑う - - - 男性 - 拒絶 - - - 男性 - 肩をすくめる - - - 男性 - 舌を出す - - - 男性 - Wow - - - 女性 – クスクス - - - 女性 – 泣く - - - 女性 – 恥ずかしい - - - 女性 – すみません - - - 女性 – あっち行ってよ - - - 女性 - 投げキッス - - - 女性 – ワッ! - - - 女性 - 退屈 - - - 女性 - Hey - - - 女性 – ヘイ、ベィビー! - - - 女性 - 笑う - - - 女性 – いい感じ - - - 女性 – こっちよ - - - 女性 – プリーズ - - - 女性 - 拒絶 - - - 女性 - 肩をすくめる - - - 女性 - 舌を出す - - - 女性 - Wow - - - /おじぎする - - - /拍手 - - - /数える - - - /消す - - - /くそくらえ - - - /筋肉もりもり - - - /いいえ - - - /だめ! - - - /パー - - - /自分を指差す - - - /相手を指差す - - - /グー - - - /チョキ - - - /タバコを吸う - - - /伸びをする - - - /口笛を吹く - - - /はい - - - /イエス! - - - 一時退席中 - - - ダンス1 - - - ダンス2 - - - ダンス3 - - - ダンス4 - - - ダンス5 - - - ダンス6 - - - ダンス7 - - - ダンス8 - - - [year,datetime,slt]/[mthnum,datetime,slt]/[day,datetime,slt] - - - なし/なし - - - [WIDTH]*[HEIGHT] 以上の画像は読み込めません - - - - 大変申し訳ございませんが、予期しない問題が発生しました。 - - サービスに関する既知の問題については、status.secondlifegrid.net をご覧ください。 -問題が引き続き発生する場合は、お使いのネットワークやファイアウォールの設定を調べてください。 - - - 日曜日:月曜日:火曜日:水曜日:木曜日:金曜日:土曜日 - - - 日:月:火:水:木:金:土 - - - 1月:2月:3月:4月:5月:6月:7月:8月:9月:10月:11月:12月 - - - 1月:2月:3月:4月:5月:6月:7月:8月:9月:10月:11月:12月 - - - [MDAY] - - - AM - - - PM - - - US$ [AMOUNT] - - - 会員 - - - 役割 - - - グループの識別情報 - - - 区画の管理 - - - 区画の識別情報 - - - 区画の設定 - - - 区画の権限 - - - 区画へのアクセス - - - 区画のコンテンツ - - - オブジェクトの管理 - - - 会計 - - - 通知 - - - チャット - - - 選択したアイテムを削除しますか - - - 選択したアイテムを削除しますか - - - このアウトフィットにはアイテムがありません - - - ExternalEditor 設定を使ってエディターを選択します。 - - - 指定された外部エディターが見つかりません。 -エディターへのパスを二重引用符で囲んでみてください。 -(例:"/path to my/editor" "%s") - - - 外部エディターのコマンドの解析中にエラーが見つかりました。 - - - 外部エディターを実行できませんでした。 - - - 翻訳できませんでした:[REASON] - - - 翻訳の応答にエラーが発生しました。 - - - Esc - - - Space - - - Enter - - - Tab - - - Ins - - - Del - - - Backsp - - - Shift - - - Ctrl - - - Alt - - - CapsLock - - - ホーム - - - End - - - PgUp - - - PgDn - - - F1 - - - F2 - - - F3 - - - F4 - - - F5 - - - F6 - - - F7 - - - F8 - - - F9 - - - F10 - - - F11 - - - F12 - - - 追加 - - - 減算 - - - 乗算 - - - 除算 - - - PAD_DIVIDE - - - PAD_LEFT - - - PAD_RIGHT - - - PAD_DOWN - - - PAD_UP - - - PAD_HOME - - - PAD_END - - - PAD_PGUP - - - PAD_PGDN - - - PAD_CENTER - - - PAD_INS - - - PAD_DEL - - - PAD_Enter - - - PAD_BUTTON0 - - - PAD_BUTTON1 - - - PAD_BUTTON2 - - - PAD_BUTTON3 - - - PAD_BUTTON4 - - - PAD_BUTTON5 - - - PAD_BUTTON6 - - - PAD_BUTTON7 - - - PAD_BUTTON8 - - - PAD_BUTTON9 - - - PAD_BUTTON10 - - - PAD_BUTTON11 - - - PAD_BUTTON12 - - - PAD_BUTTON13 - - - PAD_BUTTON14 - - - PAD_BUTTON15 - - - - - - - = - - - ` - - - ; - - - [ - - - ] - - - \ - - - 0 - - - 1 - - - 2 - - - 3 - - - 4 - - - 5 - - - 6 - - - 7 - - - 8 - - - 9 - - - A - - - B - - - C - - - D - - - E - - - F - - - G - - - H - - - I - - - J - - - K - - - L - - - M - - - N - - - O - - - P - - - Q - - - R - - - S - - - T - - - U - - - V - - - W - - - X - - - Y - - - Z - - - パーティクル源ビーコン(青)を表示中 - - - 物理的オブジェクトのビーコン(緑)を表示中 - - - スクリプトのオブジェクトのビーコン(赤)を表示中 - - - タッチ機能のビーコンが付いたスクリプトのオブジェクト(赤)を表示中 - - - サウンドビーコン(黄)を表示中 - - - メディアビーコン(白)を表示中 - - - パーティクルを非表示 - - - 土地情報 - - - 容姿 - - - アバター - - - 制作 - - - チャット - - - コンパス - - - 行き先 - - - ジェスチャー - - - ハウツー - - - インベントリ - - - 地図 - - - マーケットプレイス - - - ミニマップ - - - 歩行 / 走行 / 飛行 - - - マーチャントアウトボックス - - - 人 - - - ピック - - - 場所 - - - 環境設定 - - - プロフィール - - - 検索 - - - スナップショット - - - 話す - - - カメラコントロール - - - ボイス設定 - - - 訪問先に関する情報 - - - アバターを変更 - - - アバター一式を選択 - - - オブジェクトの制作と地形の変形 - - - 近くの人と文字チャットする - - - コンパス - - - 行ってみたい場所 - - - アバターのジェスチャー - - - 一般的タスクの実行方法 - - - インベントリを表示・使用 - - - 世界地図 - - - ショッピングに出掛ける - - - 近くの人を表示する - - - アバターの移動 - - - 販売用にアイテムをマーケットプレイスに転送 - - - フレンド、グループ、近くの人 - - - プロフィールで紹介するお気に入りの場所 - - - 保存済みの場所 - - - 環境設定 - - - プロフィールを編集・表示 - - - 場所、イベント、人を検索 - - - ピクチャを撮る - - - マイクを使って近くの人と話す - - - カメラの角度を変更 - - - インワールドでのコールや近くにいる人の音量調整 - - - 現在、下のツールバーにあります - - - 現在、左のツールバーにあります - - - 現在、右のツールバーにあります - - - 維持率 - - - 詳細 - - - もっと詳しく - - - 表面 - - - ソリッド - - - ラップ - - - プレビュー - - - 普通 - - - http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer - - - なし - - - ナビメッシュに影響を与える - - - キャラクター - - - (複数) - - - 非常に低い - - - 低 - - - 中 - - - 高 - - - 非常に高い - - - 住人はこの地域(リージョン)を訪問できません。 - - - [User] - - diff --git a/indra/newview/skins/steam/xui/pl/strings.xml b/indra/newview/skins/steam/xui/pl/strings.xml deleted file mode 100644 index f6dec8536b..0000000000 --- a/indra/newview/skins/steam/xui/pl/strings.xml +++ /dev/null @@ -1,4340 +0,0 @@ - - - - - SECOND LIFE - - - Portal Pomocy Second Life - - - Wykrywanie dysku twardego... - - - Ładowanie [APP_NAME]... - - - Czyszczenie bufora danych... - - - Inicjowanie bufora danych tekstur... - - - Inicjowanie VFS... - - - Przywracanie... - - - Zmiana rozdzielczości... - - - Trwa logowanie. [APP_NAME] Proszę czekać. - - - Logowanie... - - - Autoryzacja - - - W trakcie obslugi konta... - - - Poprzednie logowanie nie udalo się. Logowanie, próba numer [NUMBER] - - - Ładowanie świata... - - - Inicjalizacja przeglądarki internetowej... - - - Inicjalizacja multimediów... - - - Ładowanie czcionek... - - - Weryfikacja bufora danych na dysku (może trwać od 60 do 90 sekund)... - - - Przetwarzanie odpowiedzi... - - - Inicjacja świata... - - - Przetwarzanie obrazów... - - - Inicjacja QuickTime... - - - QuickTime nie został znaleziony - inicjacja przerwana. - - - QuickTime zainicjowany. - - - Oczekiwanie na połączenie z regionem... - - - Łączenie z regionem... - - - Ładowanie ubrania... - - - Serwer zwrócił nieważny lub zniekształcony certyfikat. Proszę skontaktuj się z administratorem Grida. - - - Nazwa hosta jest nieważna, proszę sprawdź SLURL lub nazwę hosta Grida. - - - Termin ważności certyfikatu zwróconego przez Grid minął. Proszę sprawdzić swój zegar systemowy lub skontaktować się z administratorem Grida. - - - Certyfikat zwrócony przez serwer nie może być użyty dla SSL. Proszę skontaktuj się z administratorem Grida. - - - Zbyt wiele certyfikatów w łańcuchu certyfikatów serwera. Proszę skontaktować się z administratorem Grida. - - - Podpis certyfikatu zwrócony przez Grid nie mógł zostać zweryfikowany. Proszę skontaktować się z administratorem Grida. - - - Błąd sieci: Brak połączenia z siecią, sprawdź status swojego połączenia internetowego. - - - Logowanie nie powiodło się. - - - Wyłącz program - - - Ten region może mieć problemy. Sprawdź podłączenie do Internetu. - - - Zachowanie ustawień... - - - Trwa wylogowanie... - - - Zamykanie... - - - Nastąpiło rozłączenie z regionem. - - - Region jest niedostępny. - - - Nastąpiło rozłączenie testowania klienta - - - Osoba - - - (brak nazwy) - - - Właściciel: - - - Publiczny - - - (Grupa) - - - Na sprzedaż: L$[AMOUNT] - - - Budowanie grupowe - - - Budowanie zabronione - - - Edycja zabroniona - - - Niebezpieczny obszar - - - Latanie zabronione - - - Skrypty grupowe - - - Skrypty zabronione - - - Posiadłość: - - - Tylko pojedynczy obiekt może być tutaj przeciągnięty - - - - Kliknij aby zobaczyć zawartość tej strony internetowej - - - Kliknij aby zobaczyć szczegóły tego miejsca - - - Kliknij aby zobaczyc profil Rezydenta - - - Dowiedz się więcej o tym Rezydencie - - - Kliknij aby wyciszyc tego Rezydenta - - - Kliknij aby cofnąć zablokowanie tego Rezydenta - - - Kliknij aby wysłać wiadomość IM do tego Rezydenta - - - Kliknij aby zapłacić temu Rezydentowi - - - Kliknij aby oferować teleport temu Rezydentowi - - - Kliknij aby wysłać temu Rezydentowi zaproszenie do Znajomych - - - Kliknij aby zobaczyć opis tej grupy - - - Klinij aby zobaczyć szczegóły tego wydarzenia - - - Kliknij aby zobaczyć tę reklamę - - - Kliknij aby zobaczyć opis tej posiadłości - - - Kliknij aby teleportować się do tego miejsca - - - Kliknij aby zobaczyć opis tego obiektu - - - Kliknij aby zobaczyć to miejsce na mapie - - - Kliknij aby uruchomić secondlife:// command - - - - Teleportuj do - - - Pokaż na mapie - - - Zablokuj - - - Cofnij zablokowanie - - - IM - - - Zapłać - - - Teleportuj do - - - Oferta znajomości - - - Zamknij (⌘W) - - - Zamknij (Ctrl+W) - - - Zamknij - - - Odzyskaj - - - Minimalizuj - - - Oderwij - - - Przyłącz - - - Pokaż Pomoc - - - Wyszukiwanie... - - - Nie odnaleziono. - - - Odzyskiwanie danych... - - - O tej wersji - - - http://wiki.secondlife.com/wiki/Release_Notes/ - - - Ładowanie danych... - - - (brak danych) - - - (ładowanie) - - - (brak danych) - - - Avaline [ORDER] - - - OK - - - Pobieranie danych: błąd - - - Pobieranie danych: brak pliku - - - Pobieranie danych: dane nie zostały znalezione w bazie danych - - - Koniec pliku - - - Nie można otworzyć pliku - - - Brak pliku - - - Transfer pliku - przekroczony limit czasu - - - Połączenie przerwane - - - Brak zgodności pomiędzy serwerem i klientem na realizację podanej ceny. - - - Status nieznany - - - tekstury - - - dźwięku - - - wizytówki - - - ulubionego miejsca - - - skryptu - - - ubrania - - - obiek - - - notatki - - - folder - - - podstawy - - - skrypt LSL2 - - - kod LSL - - - tekstury typu tga - - - części ciała - - - zdjęcia - - - Zgubione i odnalezione - - - obraz typu targa - - - Kosz - - - obraz typu jpg - - - animacja - - - gesturka - - - simstate - - - ulubione - - - link - - - link folderu - - - (Edycja Wygląd) - - - Śpi - - - Pracuje - - - Wyciszony - - - Strach - - - Złość - - - Sen - - - Salto - - - Śmiech do rozpuku - - - Wielki uśmiech - - - Całusek - - - Ale nudy! - - - Ukłon - - - Oklaski - - - Dworski ukłon - - - Płacz - - - Taniec 1 - - - Taniec 2 - - - Taniec 3 - - - Taniec 4 - - - Taniec 5 - - - Taniec 6 - - - Taniec 7 - - - Taniec 8 - - - Pogarda - - - Picie - - - Zakłopotanie - - - Grożenie paluszkiem - - - Udało się! - - - Yoga - - - Grymas - - - Niecierpliwość - - - Radocha - - - Pocałuj mnie gdzieś - - - Pocałunek - - - Śmiech - - - Szpan - - - Nie (Smutno) - - - Nie - - - Nie-nie-nie - - - Za ciosem cios - - - Szczęka opada - - - Pokój - - - Wskazuj na innych - - - Wskazuj na siebie - - - Uderz z lewej - - - Uderz z prawej - - - KPN licz - - - KPN papier - - - KPN kamień - - - KPN nożyce - - - Odrzuca mnie - - - Kopniak - - - Smutek - - - Pozdrów - - - Krzycz - - - Wzrusz ramionami - - - Uśmiechaj się - - - Pal - - - Pal i zaciągaj się - - - Rzuć papierosa - - - Zaskoczenie - - - Uderz mieczem - - - Wściekłość - - - Pokaż język - - - Pomachaj - - - Zaszeptaj - - - Zagwiżdż - - - Puść oko - - - Puść oko (Hollywood) - - - Zmartwienie - - - Tak (Szczęście) - - - Tak - - - Ładowanie... - - - Mapa Świata jest niedostępna - - - [AREA] m² L$[PRICE] - - - Miejsce nieodnalezione. - - - OK - - - Przedwczesna końcówka pliku - - - PODSTAWA lub ŁĄCZNIK nieodnaleziona/y - - - szepcze: - - - krzyczy: - - - Łączenie z rozmowami głosem w Świecie... - - - Połączenie uzyskane. - - - Niestety, rozmowy głosem są niedozwolone w tym miejscu. - - - Połączenie rozmowy utracone. - - - Przełączanie do pobliskich rozmów głosowych - - - '[OBJECTNAME]', właściciel: '[OWNERNAME]', położenie: [REGIONNAME] [REGIONPOS], pozwala Ci na: [PERMISSIONS]. - - - '[OBJECTNAME]', właściciel: '[OWNERNAME]', położenie: [REGIONNAME] [REGIONPOS], nie pozwala Ci na: [PERMISSIONS]. - - - Zabiera Lindeny (L$) od Ciebie - - - Używaj klawiszy sterowania - - - Zmień klawisze sterowania - - - Animuj Awatara - - - Dołącz do Awatara - - - Usuń prawo własności (zmień na publiczne) - - - Łącz / rozłącz z innymi obiektami - - - Dodaj / usuń połączenia z innymi obiektami - - - Ustaw zezwolenia - - - Chodź za kamerą - - - Kontroluj kamerę - - - 'General' - - - 'Moderate' - - - 'Adult' - - - Niedostępny - - - Nieznany - - - (nieznane) - - - Majątek / Region - - - Estate / Homestead - - - Mainland / Homestead - - - Mainland / Region - - - Wszystkie pliki - - - Dźwięki - - - Animacje - - - Obrazy - - - Zapisz - - - Załaduj - - - Obrazy targa - - - Obrazy bitmap - - - Pliki filmowe AVI - - - Plik animacji XAF - - - Plik XML - - - Plik RAW - - - Obrazy skomprensowane - - - Załaduj pliki - - - Wybierz katalog - - - Ustaw Nie Śpij - - - Śpij - - - Ustawiaj Nie Pracuj - - - Pracuj - - - Kształt - - - Skórka - - - Włosy - - - Oczy - - - Koszulka - - - Spodnie - - - Buty - - - Skarpetki - - - Kurtka - - - Rękawiczki - - - Podkoszulka - - - Bielizna - - - Spódnica - - - Ubranie Alpha - - - Tatuaż - - - Fizyka - - - niewłaściwa funkcja - - - żadne - - - Koszula nie jest założona - - - Spodnie nie są założone - - - Buty nie są założone - - - Skarpetki nie są założone - - - Kurtka nie jest założona - - - Rękawiczki nie są założone - - - Podkoszulek nie jest założony - - - Bielizna nie jest założona - - - Spódnica nie jest założona - - - Alpha nie jest założone - - - Tatuaż nie jest założony - - - Fizyka niezałożona - - - nieważny - - - Nowy kształt - - - Nowa skórka - - - Nowe włosy - - - Nowe oczy - - - Nowa koszula - - - Nowe spodnie - - - Nowe buty - - - Nowe skarpetki - - - Nowa kurtka - - - Nowe rękawiczki - - - Nowy podkoszulek - - - Nowa bielizna - - - Nowa spódnica - - - Nowe alpha - - - Nowy tatuaż - - - Stwórz nową fizykę - - - nieważny - - - Nowa [WEARABLE_ITEM] - - - Następne - - - OK - - - Ogłoszenie grupowe - - - Ogłoszenia grupowe - - - Wysłane przez - - - Załączone: - - - Zobacz poprzednie zawiadomienia lub otrzymane wiadomości tutaj. - - - Otwórz załącznik - - - Zapisz załącznik - - - Oferta teleportacji - - - Nowe zawiadomienia zostały wysłane kiedy byłeś/byłaś w trybie oddalenia... - - - Masz jeszcze [%d] powiadomień - - - Prawe ramię - - - Głowa - - - Lewe ramię - - - Lewa noga - - - Tułów - - - Prawa noga - - - Niska - - - Średnia - - - Wysoka - - - Wybierz ESC aby powrócić do trybu widoku normalnego - - - Nie znaleziono tego czego szukasz? Spróbuj [secondlife:///app/search/all/[SEARCH_TERM] Szukaj]. - - - Nie znaleziono tego czego szukasz? Spróbuj [secondlife:///app/search/places/[SEARCH_TERM] Szukaj]. - - - Przeciągnij landmark tutaj aby dodać go do swoich ulubionych. - - - Nie posiadasz kopii tej tekstury w Twojej Szafie. - - - - - - - - - Ładowanie zawartości... - - - Brak zawartości - - - - - - - - - - - - - - - - - - - - - - - - - - - - Moja Szafa - - - Biblioteka - - - Tekstury - - - Dźwięki - - - Wizytówki - - - Landmarki - - - Skrypty - - - Ubrania - - - Obiekty - - - Noty - - - Nowy folder - - - Szafa - - - Nieskompresowane obrazy - - - Części ciała - - - Kosz - - - Album ze zdjęciami - - - Zagubione i odnalezione - - - Nieskompresowane dźwięki - - - Animacje - - - Gesturki - - - Moje ulubione - - - Moje ulubione - - - Obecny strój - - - Początkowe stroje - - - Moje stroje - - - Akcesoria - - - Znajomi - - - Wszystkie - - - Kup - - - Kup za L$ - - - Kamień - - - Metal - - - Szkło - - - Drewno - - - Tkanka - - - Plastik - - - Guma - - - Lekkie - - - Shift - - - Ctrl - - - Klatka piersiowa - - - Czaszka - - - Lewe ramię - - - Prawe ramię - - - Lewa dłoń - - - Prawa dłoń - - - Lewa stopa - - - Prawa stopa - - - Kręgosłup - - - Miednica - - - Usta - - - Szczęka - - - Lewe ucho - - - Prawe ucho - - - Lewe oko - - - Prawe oko - - - Nos - - - P Ramię - - - P przedramię - - - L ramię - - - L przedramię - - - Prawe biodro - - - P udo - - - P dolna noga - - - Lewe biodro - - - L udo - - - L dolna noga - - - Brzuch - - - Lewy Pec - - - Prawy Pec - - - Nieważny punkt załącznika - - - [AGEYEARS] [AGEMONTHS] - - - [AGEYEARS] - - - [AGEMONTHS] - - - [AGEWEEKS] - - - [AGEDAYS] - - - Dołączył dzisiaj - - - [COUNT] rok - - - [COUNT] lat - - - [COUNT] lat - - - [COUNT] miesiąc - - - [COUNT] miesięcy - - - [COUNT] miesięcy - - - [COUNT] tydzień - - - [COUNT] tygodni - - - [COUNT] tygodni - - - [COUNT] dzień - - - [COUNT] dni - - - [COUNT] dni - - - [COUNT] członek - - - [COUNT] członków - - - [COUNT] członków - - - Rezydent - - - Proces - - - Wyróżniony członek - - - Pracownik Linden Lab - - - Dane konta używane - - - Dane płatnicze na koncie - - - Brak danych na koncie - - - Weryfikacja wieku przeprowadzona - - - Brak weryfikacji wieku - - - Środek 2 - - - Prawa góra - - - Góra - - - Lewa góra - - - Środek - - - Lewy dół - - - Dół - - - Prawy dół - - - Pobieranie zakończone, rozpoczęcie kompilacji - - - Skrypt nie został odnaleziony na serwerze. - - - Problem z pobieraniem - - - Brak odpowiedniej zgody do pobrania skryptu. - - - Brak odpowiedniej zgody dla - - - Nieznany błąd podczas próby pobierania - - - Postęp rekompilacji - - - rekompiluj - - - Zresetuj - - - zresetuj - - - Ustaw uruchomiaj progres - - - ustaw uruchom - - - Ustaw nie uruchamiaj progres - - - ustaw nie uruchamiaj - - - Kompliacja zakończona pomyślnie! - - - Komplilacja zakończona pomyślnie, zapisywanie... - - - Zapisywanie zakończone. - - - Skrypt (obiekt poza zasięgiem) - - - Obiekt [OBJECT] należący do [OWNER] - - - żadne - - - - (nieznane) - - - - - - - Stan - - - Kredyty - - - Debet - - - Suma - - - Brak informacji na temat podanej grupy - - - parent estate - - - główny - - - dla niepełnoletnich - - - błąd - - - wszystkie majątki, które są własnością [OWNER] - - - wszystkie majątki, które posiadasz - - - wszystkie majątki, które nadzorujesz dla [OWNER] - - - Dozwoleni Rezydenci: ([ALLOWEDAGENTS], maks. [MAXACCESS]) - - - Grupy mające dostęp: ([ALLOWEDGROUPS], max [MAXACCESS]) - - - Pamięć skryptów Posiadłości - - - Posiadłości: [PARCELS] - - - Pamięć wykorzystana: [COUNT] kb z [MAX] kb; [AVAILABLE] kb pozostało - - - Pamięć wykorzystana: [COUNT] kb - - - Skrypty URL Posiadłości - - - URL: [COUNT] z [MAX]; [AVAILABLE] dostępne - - - URL: [COUNT] - - - Błąd wyszukiwania informacji - - - Posiadłość nie została wybrana - - - Błąd: informacja o skrypcie jest dostępna tylko w obecnym regionie. - - - Wyszukiwanie informacji... - - - Nie masz pozwolenia na sprawdzenie pasiadłości. - - - Usiądź na - - - Klatka piersiowa - - - Głowa - - - Lewe ramię - - - Prawe ramię - - - Lewa ręka - - - Prawa ręka - - - Lewa stopa - - - Prawa stopa - - - Plecy - - - Miednica - - - Usta - - - Podbródek - - - Ucho lewe - - - Prawe ucho - - - Lewe oko - - - Prawe oko - - - Nos - - - Prawe górne ramię - - - Prawe dolne ramię - - - Ramię L Górne - - - Lewe dolne ramię - - - Biodro prawe - - - Prawa górna noga - - - Prawa dolna noga - - - Biodro lewe - - - Lewa gorna noga - - - Lewa dolna noga - - - Brzuch - - - Prawa klatka - - - Lewa klatka - - - HUD środek 2 - - - HUD prawy górny - - - HUD środek górny - - - HUD lewa gora - - - HUD środek 1 - - - HUD lewa dolna strona - - - HUD dolny - - - HUD prawa dolna strona - - - Linia [LINE], Kolumna [COLUMN] - - - [COUNT] odnalezionych - - - Zawartość obiektu - - - Nowy skrypt - - - Rezydent, do którego wysłałeś wiadomość prywatną znajduje się w trybie pracy. Oznacza to, iż Twoja wiadomość zostanie zapisana do przejrzenia poźniej. - - - (Nazwa) - - - (Rezydent) - - - (Obiekt) - - - (Grupę) - - - (Zewnętrzne) - - - Brak umowy dla tego majątku. - - - Brak umowy dla tego majątku. Każda posiadłość w tym majątku została sprzedana przez właściciela majątku nie Linden Lab. Skontaktuj się z właścicielem majątku w celu uzuskania szczegółów sprzedaży. - - - - - - Własność grupy - - - Publiczny - - - Kliknij: [TELEPORT] teleportuj, [MAP] mapa, [PROFILE] profil - - - (zostanie zaktualizowane po publikacji) - - - Nie dodałeś nic do Ulubionych i Reklam. Kliknij na poniższy przycisk Dodaj aby dodać miejsce do Ulubionych lub Reklamy. - - - Brak ulubionych miejsc/reklam - - - Ładowanie... - - - Podgląd - - - Właściwości - - - Obiekt o nazwie - - - należacy do grupy - - - należący do nieznanej grupy - - - należy do - - - należący do nieznanego właściciela - - - oddany Tobie - - - Odrzucono [DESC] od <nolink>[NAME]</nolink>. - - - Suma - - - zakupione - - - zapłać sobie - - - zapłać do - - - kup dostęp do - - - zapłać opłatę za wydarzenie - - - zapłać za wydarzenia - - - Stan - - - Kredyty - - - Debet - - - Zawartość - - - Zdobyte obiekty - - - Anuluj - - - Załadowanie [NAME] kosztuje [AMOUNT]L$ - - - Cena zakupu tego wynosi L$ [AMOUNT] - - - Nieznane rozszerzenie dla pliku [.%s] -Expected .wav, .tga, .bmp, .jpg, .jpeg, or .bvh - - - Zablokuj - - - Dodaj Ulubione Miejsce... - - - Edytuj Ulubione Miejce... - - - ⌃ - - - ⌘ - - - ⌥ - - - ⇧ - - - Ctrl+ - - - Alt+ - - - Shift+ - - - Zapisane pliki - - - Otrzymane - - - AM - - - PM - - - PST - - - PDT - - - Do przodu - - - Lewo - - - Prawo - - - Wstecz - - - Północ - - - Południe - - - Zachód - - - Wschód - - - W górę - - - W dół - - - Każda Kategoria - - - Zakupy - - - Wynajem ziemi - - - Wynajem Posiadłości - - - Specjalne Oferty - - - Nowe produkty - - - Praca - - - Poszukiwane - - - Serwis - - - Personalne - - - Żadne - - - Linden Lokalizacja - - - 'Adult' - - - Sztuka i Kultura - - - Biznes - - - Edukacyjna - - - Gra - - - Poznawanie ludzi - - - Przyjazne dla nowych - - - Parki i Natura - - - Mieszkalna - - - Scena - - - Inna - - - Wynajem - - - Jakiekolwiek - - - Ty - - - Multimedia - - - Uruchom/Zatrzymaj media - - - Podczas realizacji podanej komendy, wystąpił błąd. -Prosimy odwiedzić stronę internetową: http://wiki.secondlife.com/wiki/Client_parameters -Błąd: - - - [APP_NAME] zastosowana komenda: - - - Aplikacja [APP_NAME] nie odnalazła poszukiwanego pliku. - -Może być to spowodowane aktywnością kilku kopii oprogramowania w tej samej chwili lub Twój system błędnie odczytuje proces zakończenia dla uruchomionuch aplikacji. -Jeżeli nadal otrzymujesz ten komunikat, uruchom swój komputer ponownie. -Jeżeli problem nadal występuje, proponujemy całkowite odinstalowanie aplikacji [APP_NAME] oraz ponowną jej instalację. - - - Błąd krytyczny - - - Aplikacja [APP_NAME] wymaga procesora z AltiVec (wersja G4 lub starsza). - - - Aplikacja [APP_NAME] została już uruchomiona. -Sprawdź czy Twój pasek aplikacji nie ma zminimalizowanych okien programu. -Jeżeli nadal otrzymujesz ten komunikat, uruchom swój komputer ponownie. - - - Aplikacja [APP_NAME] znajduje się w trybie zatrzymania lub zawieszenia po poprzedniej próbie uruchomienia. -Czy chcesz wysłać raport na temat zawieszenia? - - - Powiadomienie - - - Aplikacja [APP_NAME] nie wykryła oprogramowania DirectX 9.0b lub wersji nowszej. -[APP_NAME] używa oprogramowaniau DirectX w celu wykrycia dysku twardego i/lub nieaktualizowanych dysków twardych, które mogą przyczynić się do obniżenia stabilności, wydajności systemowej oraz zawieszeń. Jeżeli chcesz uruchomić aplikację [APP_NAME] bez problemów, doradzamy korzystanie z uruchomionym oprogramowaniem min. DirectX 9.0b. - -Czy chcesz kontynuować? - - - Ostrzeżenie - - - Automatyczna aktualizacja nie została jeszcze zaimplementowana dla platformy Linux. -Prosimy o pobranie najnowszej wersji ze strony internetowej: www.secondlife.com. - - - błąd rejestru - - - Błąd - - - Nie można uruchomić trybu pełnoekranowego w proporcji [WIDTH] x [HEIGHT]. -Uruchomione w oknie. - - - Błąd w próbie wyłączenia podczas zamykania okna (DestroyWindow() failed) - - - Błąd w próbie wyłączenia - - - Brak możliwości stworzenia zawartości GL dla sterownika - - - Brak odnalezienia właściwego formatu pikselowego - - - Brak otrzymania formatu pikselowego opisu - - - Aplikacja [APP_NAME] wymaga ustawienia koloru na (32-bit) do uruchomienia. -Sprawdź swoje ustawienia dla wyświetlacza i ustaw tryb koloru na 32-bity. - - - Aplikacja [APP_NAME] nie może zostać uruchomiona, ponieważ nie jest możliwe dostanie się na kanał 8 bitowy alpha. Najcześciej jest to spowodowane błędami sterowników karty video. -Upewnij się, że posiadasz najnowsze aktualizacje sterowników karty video. -Dodatkowo, sprawdź czy Twój monitor posiada poprawną konfigurację koloru (32-bity) w Panelu Kontroli > Display > Ustawienia. -Jeżeli nadal otrzymujesz ten komunikat, skontaktuj się z [SUPPORT_SITE]. - - - Brak ustawienie formatu pikselowego - - - Brak możliwości stworzenia renderowania zawartości GL - - - Brak aktywacji renderowania zawartości GL - - - Aplikacja [APP_NAME] nie może zostać uruchomiona, ponieważ Twoja karta video jest niepoprawnie zainstalowana, nieaktualizowana lub przeznaczona jest dla innego rodzaju dysków twardych. Upewnij się, że Twoja karta video została zaktualizowana poprawnie lub spróbuj zainstalować ponownie. - -Jeżeli nadal otrzymujesz ten komunikat, skontaktuj się z [SUPPORT_SITE]. - - - Cień o godzinie 5 - - - Wszystko białe - - - Animuj oczy - - - Obrócony - - - Długość ramienia - - - Dołączone - - - Płatki uszu dołączone - - - Tylnia grzywka - - - Wypchane - - - Pasemka - - - Oczy załzawione - - - Rozmiar brzucha - - - Duży - - - Duży pośladek - - - Duże włosy: z tyłu - - - Duże włosy: z przodu - - - Duże włosy: z góry - - - Duża głowa - - - Duże mięśnie piersiowe - - - Duże kolce - - - Czarne - - - Blond - - - Włosy blond - - - Rumieniec - - - Kolor rumieńca - - - Intensywność rumieńca - - - Detale ciała - - - Zawartość tkanki tłuszczowej - - - Piegi - - - Zagęszczenie ciała - - - Grubość ciała - - - Szczupłość - - - Bow Legged - - - Jędrność piersi - - - Odstęp między piersiami - - - Rozmiar piersi - - - Szerokość - - - Szerokie - - - Rozmiar czoła - - - Wytrzeszcz oczu - - - Wytrzeszczone oczy - - - Bulwiasty - - - Bulwiasty nos - - - Masa piersi - - - Wygładzanie piersi - - - Grawitacja piersi - - - Ściśnięcie piersi - - - Efekt max - - - Sprężystość - - - Wzmocnienie - - - Tłumienie - - - Efekt max - - - Sprężystość - - - Wzmocnienie - - - Tłumienie - - - Efekt max - - - Sprężystość - - - Wzmocnienie - - - Tłumienie - - - Masa brzucha - - - Wygładzanie brzucha - - - Grawitacja brzucha - - - Ściśnięcie brzucha - - - Efekt max - - - Sprężystość - - - Wzmocnienie - - - Tłumienie - - - Masa pośladków - - - Wygładzanie pośladków - - - Grawitacja pośladków - - - Ściśnięcie pośladków - - - Efekt max - - - Sprężystość - - - Wzmocnienie - - - Tłumienie - - - Efekt max - - - Sprężystość - - - Wzmocnienie - - - Tłumienie - - - Bujne brwi - - - Bujne włosy - - - Rozmiar pośladków - - - Grawitacja pośladków - - - Bustle Skirt - - - No Bustle - - - More Bustle - - - Chaplin - - - Kości policzkowe - - - Rozmiar klatki piersiowej - - - Kąt podbródka - - - Dołek w podbródku - - - Zasłonięcie podbródka - - - Długość podbródka - - - Ciężar podbródka - - - Podbródek wewnątrz - - - Podbródek zewnętrzny - - - Podwójny podbródek - - - Wyczyść - - - Rozszczepienie - - - Oczy blisko ustawione - - - Zamknięte - - - Zamknięte z tyłu - - - Zamknięte z przodu - - - Lewe oko zamknięte - - - Prawe oko zamknięte - - - Coin Purse - - - Kołnierz z tyłu - - - Kołnierz z przodu - - - Kącik w dół - - - Kącik w górę - - - Pognieciony - - - Skrzywienie nosa - - - Szeroki rękaw - - - Ciemne - - - Ciemne zielone - - - Ciemniejsze - - - Glębokie - - - Domyślne buty na obcasie - - - Gęstość - - - Podwójny podbródek - - - Downturned - - - Duffle Bag - - - Odstawanie uszu - - - Rozmiar uszu - - - Wierzchołki uszu - - - Jajowata głowa - - - Woreczek łzowy - - - Kolor oczu - - - Głębokość osadzenia oczu - - - Ustawienie jasności oczu - - - Oczy otwarte - - - Różnica w wielkości oczu - - - Rozmiar oczu - - - Rozstaw oczu - - - Łuk brwiowy - - - Gęstość brwi - - - Wysokość brwi - - - Kształt brwi - - - Rozmiar brwi - - - Długość rzęs - - - Kredka do oczu - - - Kolor kredki do oczu'a - - - Wytrzeszczone oczy - - - Usunięcie twarzy - - - Detale twarzy - - - Oczy szeroko rozstawione - - - Grube usta - - - Kobieta - - - Bez palców - - - Palce - - - Rozszerzane rękawy - - - Płaskość - - - Płaskie pośladki - - - Płaska głowa - - - Płaski palec - - - Rozmiar stopy - - - Kształt czoła - - - Ciężar czoła - - - Piegi - - - Przednia grzywka - - - Gęstość włosów po bokach - - - Gęsta kredka do oczu - - - Gęsty przód - - - Gęste włosy po bokach - - - Gęste boki - - - Błyszczące - - - Rękawiczki - - - Długość rękawiczek - - - Włosy - - - Włosy: z tyłu - - - Włosy: z przodu - - - Włosy: boki - - - Kierunek zaczesania - - - Grubość włosów - - - Grubość włosów - - - Przes. fryzury - - - Przes. fryzury L - - - Przes. fryzury P - - - Włosy: objętość - - - Rozmiar dłoni - - - Handlebars - - - Długość głowy - - - Kształt głowy - - - Rozmiar głowy - - - Rozciągnięcie głowy - - - Wysokość obcasa - - - Ksztalt obcasa - - - Wysokość - - - Wysoka - - - Wysokie obcasy - - - Wysoka szczęka - - - Wysokie obcasy - - - Wysokie i wąskie - - - Wyżej - - - Długość bioder - - - Szerokość bioder - - - W - - - Wewnętrzny kolor cienia - - - Wewnętrzna intensywność cienia - - - Wewnętrzny bok oka - - - Wewnętrzny cień oka - - - Wewnętrzny cień - - - Długość kurtki - - - Zmarszczki na kurtce - - - Kąt szczęki - - - Wystająca szczęka - - - Kształt szczęki - - - Złącz - - - Dolna część policzka - - - Kąt kolana - - - Iksowate nogi - - - Duże - - - Duże dłonie - - - Lewa część - - - Długość nogi - - - Umięśnione nogi - - - Mniej - - - Mniejsza zawartości tkanki tłuszczowej - - - Less Curtains - - - Mniej piegów - - - Mniej pełne - - - Mniej ciężaru - - - Less Love - - - Mniej mięśni - - - Mniej umięśnienia - - - Mniej zaróżowione - - - Mniej zaaokrąglone - - - Less Saddle - - - Mniej kwadratowe - - - Mniej objętości - - - Less soul - - - Lżejsze - - - Szerokość rozszczepienia górnej wargi - - - Głębokość rozszczepienia górnej wargi - - - Pełne usta - - - Róż ust - - - Proporcje ust - - - Grubość ust - - - Szerokość ust - - - Połysk - - - Szminka - - - Kolor szminki - - - Dlugość - - - Długa głowa - - - Długie biodra - - - Długie nogi - - - Długi kark - - - Długi warkocz - - - Długi kucyk - - - Długi tułów - - - Dlugie ramiona - - - Luźne spodnie - - - Luźna koszulka - - - Luźne rękawy - - - Love Handles - - - Nisko - - - Niskie obcasy - - - Niska szczęka - - - Niskie obcasy - - - Niskie i luźne - - - Niżej - - - Dolny mostek - - - Niższe policzki - - - Mężczyzna - - - Część środkowa - - - Więcej - - - Bardziej zarumienione - - - Więcej zawartości tkanki tłuszczowej - - - More Curtains - - - Ciemniejszy cień oczu - - - Więcej piegów - - - Bardziej pełne - - - Więcej ciężaru - - - Więcej szminki - - - More Love - - - Więcej dolnej wargi - - - Więcej mięśni - - - Więcej umięśnienia - - - Bardziej zaróżowione - - - Więcej zaokrąglenia - - - More Saddle - - - Bardziej spadziste - - - Więcej kwadratowy - - - Więcej górnej wargi - - - Bardziej pionowe - - - Więcej objętości - - - More soul - - - Wąsy - - - Kąciki ust - - - Pozycja ust - - - Mowhawk - - - Umięśnienie - - - Mutton Chops - - - Lakier na paznokciach - - - Kolor lakieru na paznokciach - - - Wąskie - - - Wąski tył - - - Wąski przód - - - Wąskie usta - - - Naturalne - - - Długość karku - - - Grubość karku - - - Brak rumieńca - - - Brak kredki do oczu's - - - Brak cienia pod powieką - - - Brak połysku - - - Brak szminki - - - No Part - - - Brak lakieru - - - Brak czerwieni - - - Brak szpiców - - - Brak białego - - - Brak zmarszczek - - - Dół normalny - - - Góra normalna - - - Nos w stronę lewą - - - Nos w stronę prawą - - - Rozmiar nosa - - - Grubość nosa - - - Kąt czubka nosa - - - Kształt czubka nosa - - - Szerokość nosa - - - Przegroda nosa - - - Wielkość dziurek w nosie - - - Intensywność - - - Otwarte - - - Otwarte z tyłu - - - Otwarte z przodu - - - Otwarte z lewej - - - Otwarte z prawej - - - Pomarańczowe - - - Zewnętrznie - - - Zewnętrzny kolor cienia - - - Zewnętrzna grubość cienia - - - Zewnętrzny bok oka - - - Zewnętrzny cień oka - - - Zewnętrzny cień - - - Przodozgryz górny - - - Package - - - Pomalowane paznokcie - - - Blady - - - Krocze spodni - - - Dopasowanie spodni - - - Długość spodni - - - Talia spodni - - - Zmarszczki spodni - - - Część - - - Część grzywki - - - Mięśnie klatki piersiowej - - - Pigment - - - Warkocz - - - Różowe - - - Róż - - - Wysokie obcasy - - - Szerokie obcasy - - - Pointy - - - Obcasy pointy - - - Kucyk - - - Poofy Skirt - - - Wybałuszone lewe oko - - - Wybałuszone prawe oko - - - Opuchnięty - - - Spuchnięte powieki - - - Kolor tęczy - - - Czerwone włosy - - - Regularne - - - Prawa część - - - Kompleksowość różu - - - Zaokrąglenie - - - Rudowatość - - - Rudy - - - Włosy w nieładzie - - - Saddle Bags - - - Koścista noga - - - Odzielne - - - Płytkie - - - Tylne usunięcie włosów - - - Usunięcie twarzy - - - Przednie usunięcie włosów - - - Usunięcie od lewej strony do góry - - - Usunięcie od prawej strony do góry - - - Tylnie usunięcie włosów - - - Przednie usunięcie włosów - - - Przesuń w lewo - - - Przesuń usta - - - Przesuń w prawo - - - Dolna część koszuli - - - Dopasowanie koszuli - - - Zmarszczki na koszuli - - - Wysokość buta - - - Krótkie - - - Krótkie ramiona - - - Krótkie nogi - - - Krótki kark - - - Krótkie warkoczyki - - - Krótki kucyk - - - Krótkie baczki - - - Krótki tułów - - - Krótkie biodra - - - Ramiona - - - Boczna grzywka - - - Baczki - - - Boczne włosy - - - Boczne włosy w dół - - - Boczne włosy do góry - - - Smukły kark - - - Dopasowanie spódnicy - - - Długość spódnicy - - - Ukośne czoło - - - Długość rękawów - - - Luźne rękawy - - - Rozcięcie: tył - - - Rozcięcie: przód - - - Rozcięcie: po prawej - - - Rozcięcie: po lewej - - - Małe - - - Małe dłonie - - - Mała głowa - - - Gładkie - - - Gładkie włosy - - - Długość skarpetek - - - Zarost na dolnej wardze - - - Rzadki - - - Kolczaste włosy - - - Kwadratowe - - - Kwadratowy palec - - - Ściśnięta głowa - - - Rozciągnięta głowa - - - Zapadnięte - - - Zapadnięta klatka piersiowa - - - Zapadnięte oczy - - - Sweep Back - - - Sweep Forward - - - Wysokość - - - Zwężenie do przodu - - - Zwężenie do tyłu - - - Grube obcasy - - - Gruby kark - - - Gruby palec - - - Wąski - - - Wąskie brwi - - - Wąskie usta - - - Wąski nos - - - Obcisły podbródek - - - Obcisłe rękawy - - - Obciesłe spodnie - - - Obcisły podkoszulek - - - Wąska spódnica - - - Obcisłe rękawy - - - Kształt palca - - - Grubość palca - - - Długość tułowia - - - Mięśnie tułowia - - - Wychudzony tułów - - - Nieprzyłączone - - - Uncreased - - - Przodozgryz - - - Nienaturalne - - - Górny mostek - - - Górne policzki - - - Roszczepienie górnego podbródka - - - Górna powieka - - - Zadarta - - - Bardzo czerwona - - - Wysokość talii - - - Dobrze odżywiony - - - Białe włosy - - - Szerokie - - - Szeroki tył - - - Szeroki przód - - - Szerokie usta - - - Dzikość - - - Zmarszczki - - - Dodaj do landmarków - - - Edytuj Landmarki - - - Zobacz więcej szczegółów na temat obecnej lokalizacji - - - Historia odwiedzonych miejsc - - - Region Adult - - - Region Moderate - - - Region - - - [APP_NAME] Aktualizacja - - - Pobieranie [APP_NAME]... - - - Instalizacja [APP_NAME]... - - - Twoja [APP_NAME] wersja klienta jest aktualizowana do najnowszej wersji. Prosimy o cierpliwość. - - - Pobieranie aktualizacji... - - - Pobieranie aktualizacji - - - Pobieranie aktualizacji nie powiodło się - - - Podczas aktualizacji [APP_NAME] wystąpił błąd. Prosimy o pobranie najnowszej wersji klienta ze strony internetowej: www.secondlife.com. - - - Instalacja aktualizacji nie powiodła się - - - Uruchomienie klienta nie powiodło się - - - [APP_NAME]: Obiekty ładują się zbyt szybko od [FROM_NAME], automatyczny podgląd jest wyłączony na [TIME] sekund - - - [APP_NAME]: Obiekty ładują się zbyt szybko, automatyczny podgląd jest wyłączony na [TIME] sekund - - - -- Zapisywanie logów rozmowy aktywowane -- - - - [NAME] pisze... - - - (Brak nazwy) - - - (Moderacja: Komunikacja głosowa wyłączona domyślnie) - - - Czat tekstowy jest nieaktywny dla tej rozmowy. - - - Twój tekst w czacie grupowym został wyłączony przez Moderatora Grupy. - - - Klknij tutaj by wysłać wiadomość prywatną (IM). - - - Do - - - (Moderator) - - - (Zapisano [LONG_TIMESTAMP]) - - - Twoja rozmowa głosowa została odebrana - - - Rozmowa głosowa została rozpoczęta - - - Dołączyłeś/Dołączyłaś do rozmowy głosowej - - - [NAME] zaczyna rozmowę głosową - - - Rozmowa głosowa... - - - Połączono, kliknij Zakończ rozmowę aby się rozłączyć - - - Rozmowa głosowa zakończona - - - Konferencja z [AGENT_NAME] - - - (Sesja IM wygasła) - - - Jesteś jedyną osobą w tej konferencji. - - - [NAME] opuszcza Second Life. - - - Kliknij na [BUTTON NAME] przycisk by zaakceptować/dołączyć do tej rozmowy. - - - Zablokowałeś tego Rezydenta. Wysłanie wiadomości automatycznie odblokuje go. - - - Błąd zapytania, proszę spróbować później - - - Błąd. Spróbuj ponownie za kilka minut. - - - Nie posiadasz praw do kontynuacji. - - - Ta konferencja jest już zakończona. - - - Nie posiadesz tego przywileju. - - - Nie posiadesz tego przywileju. - - - Nie jesteś moderatorem konferencji. - - - Moderator grupy wyłączył czat. - - - Moderator wyciszył Cię. - - - Nie można dodać nikogo do czatu z [RECIPIENT]. - - - Nie można wysłać Twojej wiadomości do sesji czatu z [RECIPIENT]. - - - Nie można wysłać Twojej wiadomości do sesji czatu z [RECIPIENT]. - - - Błąd poczas moderacji. - - - Zostałeś usunięty z grupy - - - Usunięto Cię z grupy. - - - Nie posiadasz praw by uczestniczyć w tej konferencji. - - - [SOURCES] powiedział/a coś nowego - - - [SOURCES] powiedział/a coś nowego - - - Inicjacja sesji wygasła - - - http://secondlife.com/landing/voicemorphing - - - [NAME] zapłaciła/zapłacił Tobie [AMOUNT]L$ [REASON]. - - - [NAME] zapłacił/zapłaciła Tobie L$[AMOUNT]. - - - Zapłacono [NAME] [AMOUNT]L$ [REASON]. - - - Zapłacono L$[AMOUNT]. - - - Zapłacono [NAME] [AMOUNT]L$. - - - Zapłacono [AMOUNT]L$ [REASON]. - - - dla [ITEM] - - - za Posiadłość - - - za przepustkę na Posiadłość - - - dla przypisania Posiadłości - - - aby stworzyć grupę - - - aby dołączyć do grupy - - - aby pobrać - - - publikacja reklamy - - - Dajesz L$ [AMOUNT] - - - Ładowanie kosztuje [AMOUNT]L$ - - - To kosztuje [AMOUNT]L$ - - - Kupno wybranej Posiadłości [AMOUNT]L$ - - - Ten obiekt kosztuje [AMOUNT]L$ - - - Każdy - - - Oficerowie - - - Właściciele - - - Obecnie w SL - - - Pobieranie... - -Raport o Nadużyciu - - - Nowy ksztalt - - - Nowa skórka - - - Nowe włosy - - - Nowe oczy - - - Nowa koszula - - - Nowe spodnie - - - Nowe buty - - - Nowe skarpetki - - - Nowa kurtka - - - Nowe rękawiczki - - - Nowy podkoszulek - - - Nowa bielizna - - - Nowa spódnica - - - Nowa alpha - - - Nowy tatuaż - - - Nowa fizyka - - - Nieaktualne ubranie/część ciała - - - Nowa gesturka - - - Nowy skrypt - - - Stwórz nowe ogłoszenie - - - Nowy folder - - - Zawartość - - - Gesturki - - - Gesturki dla mężczyzn - - - Gesturki dla kobiet - - - Inne gesturki - - - Gesturki przemówienia - - - Gesturki - - - Mężczyzna - Excuse me - - - Mężczyzna - Get lost - - - Mężczyzna - Całusek - - - Mężczyzna - Boo - - - Mężczyzna - Znudzony - - - Mężczyzna - Hey - - - Mężczyzna - Śmiech - - - Mężczyzna - Odrzucenie - - - Mężczyzna - Wzruszenie ramionami - - - Mężczyzna - Pokaż język - - - Mężczyzna - Wow - - - Kobieta - Chichot - - - Kobieta - Płacze - - - Kobieta - Zakłopotana - - - Kobieta - Excuse me - - - Kobieta - Get lost - - - Kobieta - Całusek - - - Kobieta - Boo - - - Kobieta - Znudzona - - - Kobieta - Hey - - - Kobieta - Hey baby - - - Kobieta - Śmiech - - - Kobieta - Looking good - - - Kobieta - Over here - - - Kobieta - Please - - - Kobieta - Odrzucenie - - - Kobieta - Wzruszenie ramionami - - - Kobieta - Pokaż język - - - Kobieta - Wow - - - [mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt] - - - żadne/żadne - - - Nie można załadować zdjęcia większego niż [WIDTH]*[HEIGHT] - - - - Pomimo naszych najlepszych starań wystąpił niespodziewany problem. - - Proszę sprawdzić czy na stronie status.secondlifegrid.net nie zostały umieszczone informacje o rozpoznanych problemach serwera. - Jeśli problemy będą występowały nadal, proszę sprawdź sieć i ustawienia firewall. - - - Niedziela:Poniedziałek:Wtorek:Środa:Czwartek:Piątek:Sobota - - - Niedz.:Pon.:Wt.:Śr.:Czw.:Pt.:Sob. - - - Styczeń:Luty:Marzec:Kwiecień:Maj:Czerwiec:Lipiec:Sierpień:Wrzesień:Październik:Listopad:Grudzień - - - St.:Lt.:Mrz.:Kw.:Maj:Cz.:Lp.:Sie.:Wrz.:Li.:Paź.:Gru. - - - [MDAY] - - - AM - - - PM - - - US$ [AMOUNT] - - - Członkostwo - - - Funkcje - - - Status grupy - - - Parcel Management - - - Parcel Identity - - - Parcel Settings - - - Parcel Powers - - - Dostęp do posiadłości - - - Parcel Content - - - Object Management - - - Accounting - - - Ogłoszenia - - - Czat - - - Usuń wybrane obiekty? - - - Usuń wybrane obiekty? - - - W tym stroju nie ma elementów - - - Wybierz edytor używając ustawień ExternalEditor. - - - Nie odnaleziono zewnętrzego edytora wskazanego przez Ciebie. -Spróbuj załączyć ścieżkę do edytora w cytowaniu. -(np. "/ścieżka do mojego/edytora" "%s") - - - Błąd w składni komendy zewnętrznego edytora. - - - Uruchomienie zewnętrznego edytora nie powiodło się. - - - Esc - - - Space - - - Enter - - - Tab - - - Ins - - - Del - - - Backsp - - - Shift - - - Ctrl - - - Alt - - - CapsLock - - - Miejsce Startu - - - End - - - PgUp - - - PgDn - - - F1 - - - F2 - - - F3 - - - F4 - - - F5 - - - F6 - - - F7 - - - F8 - - - F9 - - - F10 - - - F11 - - - F12 - - - Dodaj - - - Odejmij - - - Mnożenie - - - Podziel - - - PAD_DIVIDE - - - PAD_LEFT - - - PAD_RIGHT - - - PAD_DOWN - - - PAD_UP - - - PAD_HOME - - - PAD_END - - - PAD_PGUP - - - PAD_PGDN - - - PAD_CENTER - - - PAD_INS - - - PAD_DEL - - - PAD_Enter - - - PAD_BUTTON0 - - - PAD_BUTTON1 - - - PAD_BUTTON2 - - - PAD_BUTTON3 - - - PAD_BUTTON4 - - - PAD_BUTTON5 - - - PAD_BUTTON6 - - - PAD_BUTTON7 - - - PAD_BUTTON8 - - - PAD_BUTTON9 - - - PAD_BUTTON10 - - - PAD_BUTTON11 - - - PAD_BUTTON12 - - - PAD_BUTTON13 - - - PAD_BUTTON14 - - - PAD_BUTTON15 - - - - - - - = - - - ` - - - ; - - - [ - - - ] - - - \ - - - 0 - - - 1 - - - 2 - - - 3 - - - 4 - - - 5 - - - 6 - - - 7 - - - 8 - - - 9 - - - A - - - B - - - C - - - D - - - E - - - F - - - G - - - H - - - I - - - J - - - K - - - L - - - M - - - N - - - O - - - P - - - Q - - - R - - - S - - - T - - - U - - - V - - - W - - - X - - - Y - - - Z - - - Podgląd lokalizatorów cząsteczek (niebieski) - - - Podgląd lokalizatorów fizycznych obiektów (zielony) - - - Podgląd lokalizatorów obiektów skryptowanych (czerwony) - - - Podgląd lokalizatorów obiektów skryptowanych z opcją dotyku (czerwony) - - - Podgląd lokalizatorów dźwięków (żółty) - - - Podgląd lokalizatorów mediów (biały) - - - Ukryj cząsteczki - - diff --git a/indra/newview/skins/steam/xui/pt/strings.xml b/indra/newview/skins/steam/xui/pt/strings.xml deleted file mode 100644 index f2f66e8a6f..0000000000 --- a/indra/newview/skins/steam/xui/pt/strings.xml +++ /dev/null @@ -1,4921 +0,0 @@ - - - - - SECOND LIFE - - - Portal de Supporte Second Life - - - Detectando hardware... - - - Carregando [APP_NAME]... - - - Limpando o cache... - - - Iniciando cache de texturas... - - - Iniciando VFS... - - - Falha na inicialização dos gráficos. Atualize seu driver gráfico! - - - Restaurando... - - - Alterando a resolução... - - - Fullbright (antigo) - - - Fazendo login. [APP_NAME] pode parecer congelado. Por favor, aguarde. - - - Logando... - - - Autenticando - - - Executando manutenção da conta... - - - Falha na tentativa anterior de login. Login, tentativa [NUMBER] - - - Carregando mundo... - - - Inicializando navegador embutido... - - - Inicializando multimídia... - - - Carregando fontes... - - - Verificando arquivos cache (pode levar de 60-90 segundos)... - - - Processando resposta... - - - Inicializando mundo... - - - Decodificando imagens... - - - Inicializando o QuickTime... - - - O QuickTime não foi encontrado - falha ao iniciar. - - - O QuickTime foi inicializado com sucesso. - - - Solicitando recursos da região... - - - Solicitando recursos da região, tentativa [NUMBER]... - - - Aguardando handshake com a região... - - - Conectando à região... - - - Baixando roupas... - - - O servidor respondeu com um certificado inválido ou corrompido. Por favor contate o administrador do Grid. - - - Um hostname inválido foi usado para acessar o servidor. Verifique o SLURL ou hostname do Grid. - - - O certificado dado pelo Grid parece estar vencido. Verifique o relógio do sistema ou contate o administrador do Grid. - - - O certificado dado pelo servidor não pôde ser usado para SSL. Por favor contate o administrador do Grid. - - - A cadeia de certificados do servidor tinha certificados demais. Por favor contate o administrador do Grid. - - - A assinatura do certificado dado pelo servidor do Grid não pôde ser verificada. Contate o administrador do seu Grid. - - - Erro de rede: Falha de conexão: verifique sua conexão à internet. - - - Falha do login. - - - Sair - - - http://join.secondlife.com/index.php?sourceid=1206_steam&lang=pt-BR - - - O visualizador utilizado já não é compatível com o Second Life. Visite a página abaixo para baixar uma versão atual: http://secondlife.com/download - -Para saber mais, visite as perguntas frequentes abaixo: http://secondlife.com/viewer-access-faq - - - Existe uma versão atualizada do seu visualizador: [VERSION] - - - Atualização de visualizador obrigatória: [VERSION] - - - Este agente já fez login. - - - Desculpe! Não foi possível fazer seu login. Verifique se digitou o nome de usuário correto* (como kiki45 ou astro.física) * e senha. Verifique também que a tecla Maiúscula está desativada. - - - Como medida de precaução, sua senha foi alterada. Visite sua conta em http://secondlife.com/password e responda a pergunta de segurança para mudar sua senha. Lamentamos qualquer inconveniente. - - - Fizemos algumas alterações a seu sistema. Você precisa selecionar outra senha. Visite sua conta em http://secondlife.com/password e responda a pergunta de segurança para mudar sua senha. Lamentamos qualquer inconveniente. - - - O Second Life está fechado para manutenção no momento. Somente funcionários podem acessá-lo. Consulte www.secondlife.com/status para as últimas atualizações. - - - Logons do Second Life estão temporariamente restritos para garantir a melhor experiência possível para os usuários no mundo virtual. - -Pessoas com contas gratuitas não poderão acessar o Second Life no momento para dar espaço para aquelas que pagaram pelo Second Life. - - - O Second Life não pode ser acessado deste computador. Se você acredita que houve algum equívoco, contate support@secondlife.com. - - - Sua conta não está disponível para acesso até [TIME], horário do Pacífico nos EUA (GMT-08). - - - Não é possível concluir a solicitação neste momento. Para obter mais ajuda, conte o suporte em http://secondlife.com/support. Caso você não possa mudar sua senha, ligue para (866) 476-9763. - - - Dados discrepantes detectados durante o login. Contate support@secondlife.com. - - - Sua conta está passando por um breve período de manutenção. Sua conta não está disponível para acesso até [TIME], horário do Pacífico nos EUA (GMT-08). Se você acredita que houve algum equívoco, contate support@secondlife.com. - - - Reação à solicitação de saída foi uma falha do simulador. - - - O sistema está passando para o modo offline. Sua conta não está disponível para acesso até [TIME], horário do Pacífico nos EUA (GMT-08). - - - Impossível criar sessão válida. - - - Não foi possível conectar o simulador. - - - Sua conta possui acesso ao Second Life das [START] às [END], horário da costa leste dos EUA. Volte novamente durante seu horário de acesso. Se você acredita que houve algum equívoco, contate support@secondlife.com. - - - Parâmetros incorretos. Se você acredita que houve algum equívoco, contate support@secondlife.com. - - - O parâmetro de primeiro nome deve ser alfanumérico. Se você acredita que houve algum equívoco, contate support@secondlife.com. - - - O parâmetro de sobrenome deve ser alfanumérico. Se você acredita que houve algum equívoco, contate support@secondlife.com. - - - Região passando para modo offline. Tente novamente dentro de alguns instantes. - - - Não há agente na região. Tente novamente dentro de alguns instantes. - - - A região estava acessada por outra sessão. Tente novamente dentro de alguns instantes. - - - A região estava passando para o modo offline na sessão anterior. Tente novamente dentro de alguns instantes. - - - A região estava passando para o modo offline na sessão anterior. Tente novamente dentro de alguns instantes. - - - A região passou para o modo offline na última sessão. Tente novamente dentro de alguns instantes. - - - A região inicou o modo offline. Tente novamente dentro de alguns instantes. - - - O sistema iniciou o modo offline em sua sessão anterior. Tente novamente dentro de alguns instantes. - - - Esta região pode estar passando por problemas. Por favor, verifique sua conexão com a internet. - - - Salvando configurações... - - - Saindo... - - - Fechando... - - - Você foi desconectado da região onde estava. - - - Você foi enviado para uma região inválida. - - - Teste de desconexão - - - Pessoa - - - (sem nome) - - - Proprietário: - - - Público - - - (Grupo) - - - À venda: L$[AMOUNT] - - - Construído por Grupo - - - Não é permitido construir - - - Construído por Grupo - - - Não é seguro - - - Não é permitido voar - - - Scripts de Grupo - - - Não são permitidos scripts - - - Terreno: - - - Apenas um item único pode ser arrastado para este local - - - - Não é possível fazer rez do itens em sua caixa de saída do lojista - - - Um ou mais destes objetos não podem ser vendidos ou transferidos. - - - Sua caixa de saída do lojista aceita apenas itens direto do seu inventário - - - Você não pode colocar os itens que está vestindo na sua caixa de saída do lojista - - - Não é possível colocar cartões de visita em sua caixa de saída do lojista - - - A profundidade das pastas aninhadas excede 3 - - - A contagem de subpastas na pasta de nível superior excede 20 - - - A contagem de itens na pasta de nível superior excede 200 - - - Não é possível mover uma pasta para seu filho - - - Não é possível mover uma pasta para dentro dela mesma - - - Clique para ver a página web - - - Clique para ver os dados desta localização - - - Clique para ver o perfil deste residente - - - Saiba mais sobre este residente - - - Clique para silenciar este residente - - - Clique para desfazer silenciar neste residente - - - Clique para enviar uma MI para este residente - - - Clique para pagar este residente - - - Clique para enviar um pedido de amizade a este residente - - - Clique para enviar um pedido de amizade a este residente - - - Clique para ver a descrição deste Grupo - - - Clique para ver a descrição deste evento - - - Clique para ver este anúncio - - - Clique para ver a descrição desta parcela - - - Clique para teletransportar para esta localização - - - Clique para ver a descrição deste objeto - - - Clique para ver esta localização no mapa - - - Clique para ativar no secondlife:// comando - - - - Teletransportar para - - - Mostrar no mapa para - - - Silenciar - - - Desfazer silenciar - - - MI - - - Pagar - - - Oferecer teletransporte para - - - Pedido de amizade - - - Fechar (⌘W) - - - Fechar (Ctrl+W) - - - Fechar - - - Restaurar - - - Minimizar - - - Separar-se da janela - - - conectar-se à barra - - - Mostrar ajuda - - - Buscando... - - - Não encontrado. - - - Buscando... - - - Notas de versão - - - http://wiki.secondlife.com/wiki/Release_Notes/ - - - Carregando... - - - (ninguém) - - - (aguardando) - - - (nenhum) - - - Interlocutor Avaline [ORDER] - - - Nenhum erro - - - Item pedido falhou - - - Item pedido: arquivo inexistente - - - Item pedido: item não encontrado na base de dados. - - - Fim do arquivo - - - Não é possível abrir arquivo - - - Arquivo não encontrado - - - Tempo de transferência de arquivo expirado - - - Circuito caiu - - - Visualizador e servidor não concordam no preço - - - Status desconhecido - - - textura - - - som - - - cartão de visitas - - - landmark - - - script obsoleto - - - roupas - - - objeto - - - anotação - - - pasta - - - raiz - - - script LSL2 - - - bytecode LSL - - - textura tga - - - parte do corpo - - - fotografia - - - Achados e Perdidos - - - imagem targa - - - Lixo - - - imagem jpeg - - - animação - - - gesto - - - simstate - - - favorito - - - link - - - link da pasta - - - mesh - - - (Edição Aparência) - - - Distante - - - Ocupado - - - Mudo - - - Temeroso - - - Bravo - - - Distante - - - Virar para trás - - - Rir segurando a barriga - - - Sorriso largo - - - Mandar beijo - - - Entediado - - - Reverência - - - Aplaudir - - - Saudação formal - - - Chorar - - - Dança 1 - - - Dança 2 - - - Dança 3 - - - Dança 4 - - - Dança 5 - - - Dança 6 - - - Dança 7 - - - Dança 8 - - - Desdém - - - Beber - - - Envergonhado - - - Negar com o dedo. - - - Vibrar provocando - - - Levitar Yoga - - - Careta - - - Impaciente - - - Pular de alegria - - - Beije meu bumbum - - - Beijar - - - Rir - - - Exibir músculos - - - Não (descontente) - - - Não - - - Nya-nya-nya - - - Soco um-dois - - - Abrir a boca - - - Paz - - - Apontar para o outro - - - Apontar para si - - - Soco esquerdo - - - Soco direito - - - RPS contar - - - RPS papel - - - RPS pedra - - - RPS tesoura - - - Repulsa - - - Chute giratório - - - Triste - - - Saúde - - - Gritar - - - Encolher ombros - - - Sorrir - - - Fumar à toa - - - Inalar fumaça - - - Expelir fumaça - - - Surpresa - - - Golpe de espada - - - Enraivecer - - - Mostrar a língua - - - Onda - - - Sussurrar - - - Assobiar - - - Piscar - - - Piscar (Hollywood) - - - Preocupar-se - - - Sim (Feliz) - - - Sim - - - Múltiplo - - - Carregando... - - - Offline - - - L$[PRICE] por [AREA] m² - - - Nenhum encontrado. - - - OK - - - término prematuro do arquivo - - - Não é possível encontrar a raiz (ROOT) ou junção (JOINT). - - - sussurra: - - - grita: - - - Conectando à conversa de voz no mundo - - - Conectado - - - Voz não disponível na sua localização atual - - - Desconectado da conversa de Voz no mundo - - - Agora você será reconectado ao bate-papo local. - - - '[OBJECTNAME]', um objeto de '[OWNERNAME]', localizado em [REGIONNAME] a [REGIONPOS], obteve permissão para: [PERMISSIONS]. - - - '[OBJECTNAME]', um objeto de '[OWNERNAME]', localizado em [REGIONNAME] a [REGIONPOS], teve permissão negada para: [PERMISSIONS]. - - - Se você permitir acesso à sua conta, o objeto também poderá: - - - Tomar linden dólares (L$) de você - - - Atue nas suas entradas de controle - - - Remapeie suas entradas de controle - - - Faça uma animação para o seu avatar - - - Anexe ao seu avatar - - - Libere a propriedade e torne-a pública - - - Una e desuna de outros objetos - - - Adicione e remova junções com outros objetos - - - Modifique as permissões - - - Acompanhe sua câmera - - - Controle sua camera - - - Teletransportá-lo - - - Público geral - - - Moderado - - - Adulto - - - Desconectado - - - Desconhecido - - - (desconhecido) - - - Propriedadade / Região inteira: - - - Imóvel / Homestead - - - Continente / Homestead - - - Continente / Região inteira: - - - Todos os arquivos - - - Sons - - - Animações - - - Imagens - - - Salvar - - - Carregar - - - Imagens Targa - - - Imagens Bitmap - - - Arquivo de vídeo AVI - - - Arquivo de animação XAF - - - Arquivo XML - - - Arquivo RAW - - - Imagens compactadas - - - Carregar arquivos - - - Selecionar pasta - - - Scripts - - - Dicionários - - - deixar como ausente - - - deixar como ausente - - - deixar como não ocupado - - - Deixar como ocupado - - - Silhueta - - - Pele - - - Cabelo - - - Olhos - - - Camisa - - - Calças - - - Sapatos - - - Meias - - - Blusa - - - Luvas - - - Camiseta - - - Roupa de baixo - - - Saia - - - Alpha - - - Tatuagem - - - Físico - - - Inválido - - - nenhum - - - Camisa não vestida - - - Calças não vestidas - - - Sapatos não calçados - - - Meias não calçadas - - - Jaqueta não vestida - - - Luvas não calçadas - - - Camiseta não vestida - - - Roupa de baixo não vestida - - - Saia não vestida - - - Alpha não vestido - - - Tatuagem não usada - - - Físico não usado - - - inválido - - - Criar novo físico - - - Criar pele nova - - - Criar cabelo novo - - - Criar olhos novos - - - Criar camisa nova - - - Criar calças novas - - - Criar sapatos novos - - - Criar meias novas - - - Criar jaqueta nova - - - Criar luvas novas - - - Criar camiseta nova - - - Criar roupa de baixo nova - - - Criar saia nova - - - Criar Alpha novo - - - Criar nova tatuagem - - - Criar novo físico - - - inválido - - - Novo [WEARABLE_ITEM] - - - Próximo - - - OK - - - Anúncio de grupo - - - Anúncios do grupo - - - Enviado por - - - Anexo: - - - Ver últimos anúncios ou optar por não receber essas mensagens aqui. - - - Abrir anexo - - - Salvar anexo - - - Oferta de teletransporte - - - Novas notificações chegaram enquanto você estava fora... - - - Você tem mais [%d] notificações - - - Braço direito - - - Cabeça - - - Braço esquerdo - - - Perna esquerda - - - Tronco - - - Perna direita - - - Baixo - - - Meio - - - Alto - - - Pressione ESC para retornar para visão do mundo - - - Não encontrou o que procura? Tente buscar no [secondlife:///app/search/people/[SEARCH_TERM] Search]. - - - Não encontrou o que procura? Tente buscar no [secondlife:///app/search/groups/[SEARCH_TERM] Search]. - - - Arraste um marco para adicioná-lo aos seus favoritos. - - - Você não possui uma cópia desta textura no seu inventário - - - Suas compras do Marketplace aparecerão aqui. Depois, você poderá arrastá-las para seu inventário para usá-las. - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/ - - - http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4 - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/imports - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/learn_more - - - Qualquer um pode vender itens no Mercado. - - - - Se você deseja se tornar um lojista, precisará [[MARKETPLACE_CREATE_STORE_URL] criar uma loja no Mercado]. - - - Sua caixa de saída está vazia - - - - Arraste as pastas para estas áreas e então clique em "Enviar para Mercado" para listar os itens para venda no [[MARKETPLACE_DASHBOARD_URL] Mercado]. - - - Sem erros - - - Erro: antes de enviar os itens para o Mercado, é necessário que você se defina como um lojista (sem custos). - - - Erro: esta pasta está vazia. - - - Erro: ocorreu uma falha ao enviar este item, pois sua conta de lojista tem muitos itens não associados a produtos. Para corrigir esse erro, faça o login no site do mercado e reduza a contagem de itens não associados. - - - Erro: este item contém muitos objetos. Corrija esse erro unindo os objetos em caixa para reduzir a contagem total a menos de 200. - - - Erro: este item contém muitos níveis de pastas aninhadas. Reorganize-o em até 3 níveis de pastas aninhadas, no máximo. - - - Erro: este item não pode ser vendido no mercado. - - - Erro: ocorreu um problema com este item. Tente novamente mais tarde. - - - Marcos abertos - - - - - - - - - Carregando conteúdo... - - - Nenhum conteúdo - - - - - - - - - - - - - - - - - - - - - - - - - - - - Meu inventário - - - Biblioteca - - - Texturas - - - Sons - - - Cartões de visitas - - - Marcos - - - Scripts - - - Vestuário - - - Objetos - - - Anotações - - - Nova pasta - - - Inventário - - - Imagens descompactadas - - - Corpo - - - Lixo - - - Álbum de fotografias - - - Achados e Perdidos - - - Sons descompactados - - - Animações - - - Gestos - - - Meus favoritos - - - Meus favoritos - - - Look atual - - - Looks iniciais - - - Meus looks - - - Acessórios - - - Meshes: - - - Amigos - - - Tudo - - - Nenhum anexo vestido - - - Anexos ([COUNT] slots permanecem) - - - Comprar - - - Comprar por L$ - - - Pedra - - - Metal - - - Vidro - - - Madeira - - - Carne - - - Plástico - - - Borrracha - - - Luz - - - Shift - - - Ctrl - - - Peito - - - Crânio - - - Ombro esquerdo - - - Ombro direito - - - Mão esquerda - - - Mão direita - - - Pé esquerdo - - - Pé direito - - - Espinha - - - Pélvis - - - Boca - - - Queixo - - - Orelha esquerda - - - Orelha direita - - - Globo ocular esquerdo - - - Globo ocular direito - - - Nariz - - - Braço superior D - - - Antebraço D - - - Braço superior E - - - Antebraço E - - - Quadril direito - - - Coxa D - - - Perna inferior D - - - Quadril esquerdo - - - Coxa E - - - Perna inferior E - - - Estômago - - - Peitoral E - - - Peitoral D - - - Pescoço - - - Centro do avatar - - - Ponto de encaixe inválido - - - [AGEYEARS] [AGEMONTHS] de idade - - - [AGEYEARS] de idade - - - [AGEMONTHS] de idade - - - [AGEWEEKS] de idade - - - [AGEDAYS] de idade - - - Cadastrado hoje - - - [COUNT] ano - - - [COUNT] anos - - - [COUNT] anos - - - [COUNT] mês - - - [COUNT] meses - - - [COUNT] meses - - - [COUNT] semana - - - [COUNT] semanas - - - [COUNT] semanas - - - [COUNT] dia - - - [COUNT] dias - - - [COUNT] dias - - - [COUNT] membro - - - [COUNT] membros - - - [COUNT] membros - - - Residente - - - Prova - - - Lista de membros - - - Empregado da Linden Lab - - - Dados de pagamento usados - - - Dados de pagamento fornecidos - - - Nenhum dado de pagamento - - - Idade comprovada - - - Idade não comprovada - - - Centro 2 - - - Topo direita - - - Topo - - - Topo esquerda - - - Centro - - - Inferior esquerdo - - - Inferior - - - Inferior direito - - - Baixado, agora compilando - - - Script não encontrado no servidor. - - - Problema no download - - - Permissões insuficientes para fazer o download do script. - - - Permissões insuficientes para - - - Falha desconhecida para download - - - Progresso do recompilamento - - - recompilar - - - Reset Progresso - - - Zerar - - - Definir funcionamento do progresso - - - deixar funcionando - - - Definir progresso não funcionando - - - não deixar funcionando - - - Compilação bem sucedida - - - Compilação bem sucedida, salvando... - - - Salvo. - - - Script (objeto fora de alcance) - - - Objeto [OBJECT] de propriedade de [OWNER] - - - nenhum - - - - (Desconhecido) - - - - - [mthnum,datetime,utc]/[day,datetime,utc]/[year,datetime,utc] - - - - - Balanço - - - Créditos - - - Débitos - - - Total - - - Não há dados de grupo - - - Propriedade-pai - - - continente - - - adolescente - - - qualquer um - - - erro - - - todas as propriedades pertencem a [OWNER] - - - todas as propriedades que você possui - - - todas as propriedades que você gerencia para [OWNER] - - - Residentes autorizados: ([ALLOWEDAGENTS], max [MAXACCESS]) - - - Grupos permitidos: ([ALLOWEDGROUPS], max [MAXACCESS]) - - - Memória de scripts no lote - - - Lotes listados: [PARCELS] - - - Memória usada: [COUNT] kb de [MAX] kb; [AVAILABLE] kb disponíveis - - - Memória usada: [COUNT] kb - - - URL dos scripts do lote - - - URLs usados: [COUNT] de [MAX]; [AVAILABLE] disponíveis - - - URLs usados: [COUNT] - - - Erro ao solicitar dados - - - Nenhum lote foi selecionado - - - Erro: dados de script só disponíveis na região da posição atual - - - Obtendo dados... - - - Você não está autorizado a examinar este lote. - - - Sentado em - - - Peito - - - Cabeça - - - Ombro esquerdo - - - Ombro direito - - - Mão esquerda - - - Mão direita - - - Pé esquerdo - - - Pé direito - - - Atrás - - - Pélvis - - - Boca - - - Queixo - - - Orelha esquerda - - - Orelha direita - - - Olho esquerdo - - - Olho direito - - - Nariz - - - Braço direito - - - Antebraço direito - - - Braço esquerdo - - - Antebraço esquerdo - - - Quadril direito - - - Coxa direita - - - Perna direita - - - Quadril esquerdo - - - Coxa esquerda - - - Perna esquerda - - - Barriga - - - Peitorais D - - - Peitorais E - - - HUD Central 2 - - - HUD superior direito - - - HUD centro superior - - - HUD superior esquerdo - - - HUD Central 1 - - - HUD esquerda inferior - - - HUD inferior - - - HUD direito inferior - - - Linha [LINE], Coluna [COLUMN] - - - [COUNT] encontrado - - - Conteúdo do objeto - - - Novo Script - - - O residente para o qual escreveu está no modo 'ocupado', ou seja, ele prefere não receber nada no momento. Sua mensagem será exibida como uma MI mais tarde. - - - (por nome) - - - (residente) - - - (objeto) - - - (grupo) - - - (Externo) - - - Não foi definido um contrato para essa região. - - - Não foi definido um contrato para essa Região. O terreno nesta região está sendo vendido pelo Proprietário, não pela Linden Lab. Favor contatar o Proprietário da região para detalhes de venda. - - - - - - Propriedade do Grupo - - - Público - - - Configurações locais - - - Configurações da região - - - Cliques: [TELEPORT] teletransporte, [MAP] mapa, [PROFILE] perfil - - - (vai atualizar depois de publicado) - - - Você não criou nenhum Destaque ou Anúncio. Clique no botão "+" para criar um Destaque ou Anúncio. - - - O usuário não tem nenhum destaque ou anúncio - - - Carregando... - - - Preview - - - Propriedades - - - um objeto chamado - - - possuído pelo grupo - - - de um grupo desconhecido - - - de - - - de usuário desconhecido - - - deu a você - - - Você recusou um(a) [DESC] de <nolink>[NAME]</nolink>. - - - Total - - - comprou - - - pagou a você - - - depositado - - - comprou passe para - - - pagou taxa para o evento - - - pagou prêmio para o evento - - - Saldo - - - Créditos - - - Débitos - - - [weekday,datetime,utc] [mth,datetime,utc] [day,datetime,utc], [year,datetime,utc] - - - Conteúdo - - - Itens adquiridos - - - Cancelar - - - Carregar [NAME] custa L$ [AMOUNT] - - - Isso custa L$ [AMOUNT] - - - Extensão de arquivo desconhecida [.%s] -Expected .wav, .tga, .bmp, .jpg, .jpeg, or .bvh - - - Bloquear - - - Adicionar marco... - - - Editar marco... - - - ⌃ - - - ⌘ - - - ⌥ - - - ⇧ - - - Ctrl+ - - - Alt+ - - - Shift+ - - - Arquivo salvo - - - Recebendo - - - AM - - - PM - - - PST - - - PDT - - - Frente - - - Esquerda - - - Direita - - - Atrás - - - Norte - - - Sul - - - Oeste - - - Leste - - - P/ cima - - - P/ baixo - - - Qualquer categoria - - - Compras - - - Aluguel de terrenos - - - Aluguel de propriedade - - - Atração especial - - - Novos Produtos - - - Emprego - - - Desejado - - - Serviço - - - Pessoal - - - Nenhum - - - Locação Linden - - - Adulto - - - Artes e Cultura - - - Negócios - - - Educacional - - - Games - - - Moradia - - - Para recém-chegados - - - Parques & Natureza - - - Residencial - - - Estágio - - - Outros - - - Aluguel - - - Qualquer - - - Você - - - Mídia múltipla - - - Tocar/Pausar mídia - - - Um erro foi encontrado analisando a linha de comando. -Consulte: http://wiki.secondlife.com/wiki/Client_parameters -Erro: - - - [APP_NAME] Uso de linha de comando: - - - [APP_NAME] não é capaz de acessar um arquivo que ele precisa. - -Isto pode ocorrer porque você de alguma maneira tem várias cópias em execução, ou o seu sistema acredita de maneira incorreta que um arquivo está aberto. -Se a mensagem persistir, reinicie o computador e tente novamente. -Se o error persistir, pode ser necessário desinstalar completamente [APP_NAME] e reinstalá-lo. - - - Erro fatal - - - [APP_NAME] exige processador com AltiVec (G4 ou superior). - - - [APP_NAME] já está em execução. -Verifique a sua barra de tarefas para obter uma cópia do programa minimizado. -Se a mensagem persistir, reinicie o computador. - - - [APP_NAME] parece ter congelado ou falhado na execução anterior. Enviar relatório de falha? - - - Alerta - - - [APP_NAME] é incapaz de detectar o DirectX 9.0b ou superior. -[APP_NAME] usa o DirectX para a detecção de hardware e / ou controladores desatualizados que podem causar problemas de estabilidade, desempenho ruim e falhas. Embora você possa executar [APP_NAME] sem ele, nós recomendamos fortemente que utilize o DirectX 9.0b. - -Deseja continuar? - - - Aviso - - - Atualização automática ainda não está implementada para o Linux. -Faça o download da versão mais recente do www.secondlife.com. - - - RegisterClass falhou - - - Erro - - - Incapaz de funcionar com tela cheia de [WIDTH] x [HEIGHT]. -Executando em janela. - - - Erro de desligamento ao destruir janela (DestroyWindow() failed) - - - Erro de desligamento - - - Não é possível fazer contexto do dispositivo GL - - - Não é possível encontrar um formato de pixel adequado - - - Não é possível encontrar descrição de formato de pixel - - - [APP_NAME] requer True Color (32-bit) para ser executado. -Por favor, vá para as configurações de vídeo do computador e defina o modo de cores para 32-bit. - - - [APP_NAME] é incapaz de executar porque ele não consegue obter um canal alpha de 8 bits. Geralmente isso ocorre devido a problemas de drivers da placa de vídeo. -Por favor, certifique-se que os últimos drivers da placa de vídeo estão instalados. -Também não se esqueça de definir seu monitor para True Color (32-bit), em painéis de controle Configurações> Display>. -Se você continuar a receber esta mensagem, contate o [SUPPORT_SITE]. - - - Não é possível definir o formato de pixel - - - Não é possível criar o contexto de renderização GL - - - Não é possível ativar o contexto de renderização GL - - - [APP_NAME] é incapaz de funcionar por causa do seu driver de video não ter sido instalado corretamente, estão desatualizados, ou não são suportados pelo hardware. Por favor certifique-se que você possui os drivers de placa de vídeo mais recente e mesmo assim, tente reinstalá-los. - -If you continue to receive this message, contact the [SUPPORT_SITE]. - - - Barba por fazer - - - Todo branco - - - Olhos de Anime - - - Arqueados - - - Comprimento do braço - - - Anexado - - - Lóbulos da orelha anexados - - - corte traseiro - - - folgado - - - Franja - - - Olhos pequenos - - - Tamanho da barriga - - - Grande - - - Bunda grande - - - Cabelo volumoso: Trás - - - Cabelo volumoso: Frente - - - Cabelo volumoso: Topo - - - cabeça grande - - - Peitorais grandes - - - Pontas grandes - - - Negro - - - Loiro - - - Cabelo loiro - - - Blush - - - Cor do blush - - - Opacidade do blush - - - Definição do corpo - - - Gordura - - - Sardas - - - Corpo cheio - - - Ossatura - - - Corpo magro - - - Pernas arqueadas - - - Caimento dos seios - - - Separação dos seios - - - Tamanho dos seios - - - Largura do nariz - - - Largo - - - Tamanho da sobrancelha - - - Olhos saltados - - - Olhos esbugalhados - - - Bulbos - - - Nariz em bulbo - - - Seios - massa - - - Seios - suavização - - - Seios - gravidade - - - Seios - resistência do ar - - - Efeito máximo - - - Vibração - - - Ganho - - - Duração - - - Efeito máximo - - - Vibração - - - Ganho - - - Duração - - - Efeito máximo - - - Vibração - - - Ganho - - - Duração - - - Barriga - massa - - - Barriga - suavização - - - Barriga - gravidade - - - Barriga - resistência do ar - - - Efeito máximo - - - Vibração - - - Ganho - - - Duração - - - Nádegas - massa - - - Nádegas - suavização - - - Nádegas - gravidade - - - Nádegas - resistência do ar - - - Efeito máximo - - - Vibração - - - Ganho - - - Duração - - - Efeito máximo - - - Vibração - - - Ganho - - - Duração - - - Sobrancelhas grossas - - - Cabelo grosso - - - Tamanho do traseiro - - - Nádegas - gravidade - - - Saia armada - - - Saia reta - - - Mais - - - Chaplin - - - Maçãs do rosto - - - Tamanho do peito - - - Ângulo do queixo - - - Fissura do queixo - - - Barba de contorno - - - Profundidade do queixo - - - Queixo pronunciado - - - Queixo para dentro - - - Queixo para fora - - - Queixo-pescoço - - - Limpar - - - Fenda - - - Fechar conjunto de olhos - - - Fechado - - - Trás fechada - - - Frente fechada - - - Esquerda fechada - - - Direita fechada - - - Pouco volume - - - Colarinho posterior - - - Colarinho anterior - - - Canto para baixo - - - Canto para cima - - - Vincado - - - Nariz torto - - - Bainha larga - - - Escuro - - - Verde escuro - - - Mais escuro - - - Profundidade - - - Salto padrão - - - Densidade - - - Queixo duplo - - - Curvado para baixo - - - Mais volume - - - Ângulo da orelha - - - Tamanho da orelha - - - Pontas das orelhas - - - Cabeça oval - - - Olheiras - - - Cor dos olhos - - - Profundidade dos olhos - - - Luminosidade dos olhos - - - Abertura dos olhos - - - Olho saltado - - - Tamanho dos olhos - - - Espaçamento dos olhos - - - Arco da sobrancelha - - - Densidade da sobrancelha - - - Altura da sobrancelha - - - Pontas da sobrancelha - - - Tamanho da sobrancelha - - - Comprimento das pestanas - - - Delineador - - - Cor do delineador - - - Olhos esbugalhados - - - Face raspada - - - Definição facial - - - Distância entre os olhos - - - Lábios carnudos - - - Feminino - - - Dedos - - - Dedos - - - Punhos largos - - - Chato - - - Traseiro chato - - - Cabeça chata - - - Dedos dos pés chatos - - - Tamanho dos pés - - - Ângulo da testa - - - Testa pronunciada - - - Sardas - - - Franja - - - Trás cheia - - - Delienador cheio - - - Frente cheia - - - Cabelos laterais cheios - - - Lados cheios - - - Brilhante - - - Dedos da luva - - - Comprimento das luvas - - - Cabelo - - - Cabelo: Trás - - - Cabelo: Frente - - - Cabelos: Lateral - - - Cabelo penteado - - - Espessura do cabelo - - - Espessura do cabelo - - - Divisão do cabelo - - - Divistão do cabelo esquerda - - - Divisão do cabelo direita - - - Cabelo: Volume - - - Tamanho das mãos - - - Bigode - - - Comprimento da cabeça - - - Formato da cabeça - - - Tamanho da cabeça - - - Extensão da cabeça - - - Altura do salto - - - Formato do salto - - - Altura - - - Alto - - - Salto alto - - - Maxilar alto - - - Plataformas altas - - - Alto e justo - - - Mais alto - - - Comprimento do quadril - - - Largura do quadril - - - Dentro - - - Cor da sombra interna - - - Opacidade da sombra interna - - - Canto interno dos olhos - - - Sombra interna dos olhos - - - Sombra interna - - - Comprimento da blusa - - - Dobras da jaqueta - - - Ângulo da mandíbula - - - Posição do maxilar - - - Formato do maxilar - - - Juntar - - - Papo - - - Ângulo do joelho - - - Joelhos para dentro - - - Grande - - - Mãos grandes - - - Parte esquerda - - - Comprimento da perna - - - Musculatura da perna - - - Menos - - - Menos gordura - - - Menos barba - - - Menos sardas - - - Menos - - - Menos gravidade - - - Menos excesso - - - Menos músculos - - - Menos musculoso - - - Menos rosado - - - Menos arredondado - - - Menos ancas - - - Menos quadrado - - - Menos volume - - - Menos alma - - - Lighter - - - Fenda dos lábios - - - Profundidade da fenda dos lábios - - - Volume dos lábios - - - Rosado dos lábios - - - Proporção dos lábios - - - Espessura dos lábios - - - Largura dos lábios - - - Brilho dos lábios - - - Batom - - - Cor do batom - - - Longo - - - Cabeça alongada - - - Lábios longos - - - Pernas longas - - - Pescoço longo - - - Chiquinhas longas - - - Rabo de cavalo longo - - - Torso longo - - - Braços longos - - - Pantalonas - - - Camisa folgada - - - Mangas folgadas - - - Pneu - - - Baixo - - - Salto baixo - - - Maxilar baixo - - - Plataformas baixas - - - Baixo e solto - - - Mais baixo - - - Mais baixa - - - Bochechas abaixadas - - - Masculino - - - Parte do meio - - - Mais - - - Mais blush - - - Mais gordura - - - Mais barba - - - Mais sombra dos olhos - - - Mais sardas - - - Mais volume - - - Mais gravidade - - - Mais batom - - - Mais cintura - - - Mais lábio inferior - - - Mais músculos - - - Mais musculoso - - - Mais rosado - - - Mais arredondado - - - Mais ancas - - - Mais inclinado - - - Mais quadrado - - - Mais lábios superiores - - - Mais vertical - - - Mais volume - - - Mais alma - - - Bigode - - - Canto da boca - - - Posição da boca - - - Moicano - - - Muscular - - - Costeletas - - - Esmate das unhas - - - Cor do esmalte das unhas - - - Estreito - - - Costas estreitas - - - Frente estreita - - - Lábios estreitos - - - Natural - - - Comprimento do pescoço - - - Espessura do pescoço - - - Sem blush - - - Sem delineador - - - Sem sombra - - - Sem brilho - - - Sem batom - - - Sem parte - - - Sem esmalte - - - Sem vermelho - - - Sem pontas - - - Sem branco - - - Sem dobras - - - Normal inferior - - - Normal superior - - - Nariz para esquerda - - - Nariz para direita - - - Tamanho do nariz - - - Espessura do nariz - - - Ângulo da ponta do nariz - - - Formato da ponta do nariz - - - Largura do nariz - - - Divisão das narinas - - - Largura das narinas - - - Opaco - - - Abrir - - - Aberto atrás - - - Aberto na frente - - - Aberto esquerdo - - - Aberto direito - - - Laranja - - - Fora - - - Cor da sombra externa - - - Opacidade da sombra externa - - - Canto externo do olho - - - Sombra externa do olho - - - Sombra externa - - - Má oclusão - - - Púbis - - - Unhas pintadas - - - Pálido - - - Cavalo da calça - - - Caimento das calças - - - Comprimento das calças - - - Cintura da calça - - - Dobras das calças - - - Parte - - - Divisão da franja - - - Peitorais - - - Pigmento - - - Chiquinhas - - - Rosa - - - Mais rosado - - - Altura da plataforma - - - Largura da plataforma - - - Pontudo - - - Salto agulha - - - Rabo de cavalo - - - Saia bufante - - - Olho saltado esquerdo - - - Olho saltado direito - - - Inchado - - - Pálpebras inchadas - - - Cor do arco íris - - - Cabelo ruivo - - - Normal - - - Parte direita - - - Rosado da face - - - Arredondado - - - Rubor - - - Corado - - - Cabelo desalinhado - - - Culote - - - Pernas magricelas - - - Separar - - - Raso - - - Trás rente - - - Face raspada - - - Frente rente - - - Esquerda rente para cima - - - Trás rente para cima - - - Rente atrás - - - Rente frente - - - Deslocar p/ esquerda - - - Deslocar boca - - - Deslocar p/ direita - - - Barra da camisa - - - Ajuste da camisa - - - +/- amassada - - - Altura do sapato - - - Curto - - - Braços curtos - - - Pernas curtas - - - Pescoço curto - - - Chiquinhas curtas - - - Rabo de cavalo curto - - - Costeletas curtas - - - Tronco curto - - - Quadril curto - - - Ombros - - - pontas laterais - - - Costeletas - - - Cabelo lateral - - - Cabelo lateral long - - - Cabelo lateral superior - - - Pescoço fino - - - Ajuste de saia - - - Comprimento da saia - - - Testa inclinada - - - Comprimento da manga - - - Folga da manga - - - Abertura : Atrás - - - Abertura: Frente - - - Abertura: Esquerda - - - Abertura: Direita - - - Pequeno - - - Mãos pequenas - - - Cabeça pequena - - - Suavizar - - - Suavizar cabelo - - - Comprimento das meias - - - Cavanhaque - - - Disperso - - - Cabelo espetado - - - Quadrado - - - Dedo quadrado - - - Cabeça de Pera - - - Cabeça esticada - - - Afundar - - - Peito afundado - - - Olhos afundados - - - Pentear para trás - - - Pentear para frente - - - Alto - - - Afinar atrás - - - Afinar a frente - - - Salto grosso - - - Pescoço grosso - - - Dedo grosso - - - Fino - - - Sobrancelhas finas - - - Lábios finos - - - Nariz fino - - - Queixo apertado - - - Punho justo - - - Calça justa - - - Camisa justa - - - Saia justa - - - Tight Sleeves - - - Formato dos dedos - - - Espessura dos dos dedos - - - Comprimento do tronco - - - Músculos do tronco - - - Tronco magricela - - - Desanexado - - - Uncreased - - - Underbite - - - Não natural - - - Parte alta do nariz - - - Bochechas altas - - - fenda do queixo alta - - - Curvatura dos cílios supériores - - - Voltado para cima - - - Bem vermelho - - - Altura da cintura - - - Corpulento - - - Grisalho - - - Amplo - - - Costas largas - - - Testa larga - - - Lábios amplos - - - Selvagem - - - Rugas - - - Adicionar às minhas Landmarks - - - Editar minhas Landmarks - - - Ver mais informações sobre a localização atual - - - Histórico de localizações - - - Região Adulta - - - Região Moderada - - - Região em geral - - - Avatar visíveis e bate-papo permitido fora deste terreno - - - Os objetos que se movem podem não se comportar corretamente nesta região até que ela seja recarregada. - - - O pathfinding dinâmico não está habilitado nesta região. - - - [APP_NAME] Atualização - - - Atualizando agora o [APP_NAME]... - - - Instalando [APP_NAME]... - - - Seu visualizador [APP_NAME] está sendo atualizado para a versão mais recente. Isso pode levar algum tempo, então por favor seja paciente. - - - Fazendo o download da atualização... - - - Fazendo o download da atualização - - - Falha no download da atualização - - - Um erro ocorreu ao atualizar [APP_NAME]. Por favor, faça o download da versão mais recente em www.secondlife.com. - - - Falha ao instalar a atualização - - - Falha ao iniciar o visualizador - - - [APP_NAME]: Entrada de itens rápida demais de [FROM_NAME], visualização automática suspensa por [TIME] segundos - - - [APP_NAME]: Entrada de itens rápida demais, visualização automática suspensa por [TIME] segundos - - - -- Log de mensagem instantânea habilitado -- - - - [NAME] está digitando... - - - (Anônimo) - - - (Moderado: Voz desativado por padrão) - - - Bate-papo de texto não está disponível para esta chamada. - - - Seu bate- papo de texto foi desabilitado por um Moderador do Grupo. - - - Clique aqui para menagem instantânea. - - - Para - - - (Moderador) - - - (Salvo em [LONG_TIMESTAMP]) - - - Para visualizar esta mensagem, você deve desmarcar "Apenas amigos e grupos podem me ligar ou enviar MIs" em Preferências/Privacidade. - - - Ligação atendida - - - Você iniciou uma ligação de voz - - - Você entrou na ligação - - - [NAME] iniciou uma ligação de voz - - - Entrando em ligação de voz... - - - Conectado. Para sair, clique em Desligar - - - Saiu da ligação de voz - - - Conversa com [AGENT_NAME] - - - Oferta de item de inventário - - - (Sessão de MI inexistente) - - - Você é o único usuário desta sessão. - - - [NAME] está offline. - - - Clique no botão [BUTTON NAME] para aceitar/ conectar a este bate-papo em voz. - - - Você bloqueou este residente. Se quiser retirar o bloqueio, basta enviar uma mensagem. - - - Erro de solicitação, tente novamente mais tarde. - - - Erro na requisição, por favor, tente novamente. - - - Você não tem permissões suficientes. - - - A sessão deixou de existir - - - Você não possui esta habilidade. - - - Você não possui esta habilidade. - - - Você não é um moderador de sessão. - - - Bate-papo de texto desativado por um moderador. - - - Um moderador do grupo desabilitou seu bate-papo em texto. - - - Não foi possível adicionar usuários na sessão de bate-papo com [RECIPIENT]. - - - Não foi possível enviar sua mensagem para o bate-papo com [RECIPIENT]. - - - Não foi possível enviar sua mensagem na sessão de bate- papo com [RECIPIENT]. - - - Erro durante a moderação. - - - Você foi tirado do grupo. - - - Você foi removido do grupo. - - - Você não possui mais a habilidade de estar na sessão de bate-papo. - - - [SOURCES] disse alguma coisa - - - [SOURCES] disseram alguma coisa - - - A inicialização da sessão expirou - - - Posição inicial definida. - - - http://secondlife.com/landing/voicemorphing - - - [NAME] lhe pagou L$ [AMOUNT] [REASON]. - - - [NAME] lhe pagou L$ [AMOUNT] - - - Você pagou L$[AMOUNT] por [REASON] a [NAME]. - - - Você acaba de pagar L$[AMOUNT]. - - - Você pagou L$[AMOUNT] a [NAME]. - - - Você pagou L$[AMOUNT] por [REASON]. - - - Você não pagou L$[AMOUNT] a [NAME] referentes a [REASON]. - - - Você não pagou L$[AMOUNT]. - - - Você não pagou L$[AMOUNT] a [NAME]. - - - Você não pagou L$[AMOUNT] referentes a [REASON]. - - - por [ITEM] - - - por uma parcela - - - por um passe de acesso - - - para doar um terreno - - - para criar um grupo - - - para entrar em um grupo - - - para carregar - - - para publicar um anúncio - - - Dando L$ [AMOUNT] - - - O upload custa L$ [AMOUNT] - - - Isso custa L$ [AMOUNT] - - - Comprando terreno selecionado L$ [AMOUNT] - - - Esse objeto custa L$ [AMOUNT] - - - Todos - - - Oficiais - - - Proprietários - - - Conectado - - - Carregando... - -Denunciar abuso - - - Nova forma - - - Nova pele - - - Novo cabelo - - - Novos olhos - - - Nova camisa - - - Novas calças - - - Novos sapatos - - - Novas meias - - - Nova blusa - - - Novas luvas - - - Nova camiseta - - - Novas roupa de baixo - - - Nova saia - - - Novo alpha - - - Nova tatuagem - - - Novo físico - - - Item inválido - - - Novo gesto - - - Novo script - - - Nova nota - - - Nova pasta - - - Conteúdo - - - Gesto - - - Gestos masculinos - - - Gestos femininos - - - Outros gestos - - - Gestos da fala - - - Gestos comuns - - - Perdão - masculino - - - Deixe-me em paz - masculino - - - Mandar beijo - masculino - - - Vaia - masculino - - - Maçante - masculino - - - Ôpa! - masculino - - - Risada - masculino - - - Quero distância! - masculino - - - Encolher de ombros - masculino - - - Mostrar a língua - masculino - - - Wow - masculino - - - Engraçado - Feminino - - - Chorar - Feminino - - - Com vergonha - Feminino - - - Perdão - fem - - - Deixe-me em paz - feminino - - - Mandar beijo - fem - - - Vaia - fem - - - Maçante - feminino - - - Ôpa - feminino - - - E aí, beliza? - Feminino - - - Risada - feminina - - - Que chique - Feminino - - - Acenar - Feminino - - - Por favor - Feminino - - - Quero distância! - feminino - - - Encolher ombros - feminino - - - Mostrar a língua - feminino - - - Wow - feminino - - - /reverência - - - /palmas - - - /contar - - - /apagar - - - /dane_se - - - /músculos - - - /não - - - /não! - - - /papel - - - /apontar_eu - - - /apontar_você - - - /pedra - - - /tesoura - - - /fumar - - - /alongar - - - /assobiar - - - /sim - - - /sim! - - - ldt - - - dança1 - - - dança2 - - - dança3 - - - dança4 - - - dança5 - - - dança6 - - - dança7 - - - dança8 - - - [mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt] - - - nenhum/nehum - - - A imagem excede o limite [WIDTH]*[HEIGHT] - - - - Aconteceu algo inesperado, apesar de termos tentado impedir isso. - - Cheque secondlifegrid.net para saber se foi detectado um problema com o serviço. - Se o problema persistir, cheque a configuração da sua rede e firewall. - - - Domingo:Segunda:Terça:Quarta:Quinta:Sexta:Sábado - - - Dom:Seg:Ter:Qua:Qui:Sex:Sab - - - Janeiro:Fevereiro:Março:Abril:Maio:Junho:Julho:Agosto:Setembro:Outubro:Novembro:Dezembro - - - Jan:Fev:Mar:Abr:Maio:Jun:Jul:Ago:Set:Out:Nov:Dez - - - [MDAY] - - - AM - - - PM - - - US$ [AMOUNT] - - - Plano - - - Cargos - - - Identidade do lote - - - Gestão do lote - - - ID do lote - - - Configurações do lote - - - Poderes do lote - - - Acesso ao lote - - - Conteúdo do lote - - - Gestão de objetos - - - Contabilidade - - - Avisos - - - Bate-papo - - - Excluir itens selecionados? - - - Excluir item selecionado? - - - Este look não possui nenhuma peça - - - Selecione um editor utilizando a configuração ExternalEditor. - - - O editor externo especificado não foi localizado. -Tente colocar o caminho do editor entre aspas. -(ex. "/caminho para/editor" "%s") - - - Error ao analisar o comando do editor externo. - - - Falha de execução do editor externo. - - - Falha na tradução: [REASON] - - - Erro ao analisar resposta de tradução. - - - Esc - - - Space - - - Enter - - - Tab - - - Ins - - - Del - - - Backsp - - - Shift - - - Ctrl - - - Alt - - - CapsLock - - - Início - - - End - - - PgUp - - - PgDn - - - F1 - - - F2 - - - F3 - - - F4 - - - F5 - - - F6 - - - F7 - - - F8 - - - F9 - - - F10 - - - F11 - - - F12 - - - Adicionar - - - Subtrair - - - Multiplicar - - - Dividir - - - PAD_DIVIDE - - - PAD_LEFT - - - PAD_RIGHT - - - PAD_DOWN - - - PAD_UP - - - PAD_HOME - - - PAD_END - - - PAD_PGUP - - - PAD_PGDN - - - PAD_CENTER - - - PAD_INS - - - PAD_DEL - - - PAD_Enter - - - PAD_BUTTON0 - - - PAD_BUTTON1 - - - PAD_BUTTON2 - - - PAD_BUTTON3 - - - PAD_BUTTON4 - - - PAD_BUTTON5 - - - PAD_BUTTON6 - - - PAD_BUTTON7 - - - PAD_BUTTON8 - - - PAD_BUTTON9 - - - PAD_BUTTON10 - - - PAD_BUTTON11 - - - PAD_BUTTON12 - - - PAD_BUTTON13 - - - PAD_BUTTON14 - - - PAD_BUTTON15 - - - - - - - = - - - ` - - - ; - - - [ - - - ] - - - \ - - - 0 - - - 1 - - - 2 - - - 3 - - - 4 - - - 5 - - - 6 - - - 7 - - - 8 - - - 9 - - - A - - - B - - - C - - - D - - - E - - - F - - - G - - - H - - - I - - - J - - - K - - - L - - - M - - - N - - - O - - - P - - - Q - - - R - - - S - - - T - - - U - - - V - - - W - - - X - - - Y - - - Z - - - Vendo balizas de partículas (azul) - - - Vendo balizas de objetos físicos (verde) - - - Vendo balizas de objetos com script (vermelho) - - - Vendo objeto com script com balizas com funcionalidade de toque (vermelho) - - - Vendo balizas de som (amarelo) - - - Vendo balizas de mídia (branco) - - - Ocultar partículas - - - Sobre terrenos - - - Aparência - - - Avatar - - - Construir - - - Bate-papo - - - Bússola - - - Destinos - - - Gestos - - - Como - - - Inventário - - - Mapa - - - Mercado - - - Mini Mapa - - - Andar/correr/voar - - - Caixa de saída do lojista - - - Pessoas - - - Destaques - - - Lugares - - - Preferências - - - Perfil - - - Buscar - - - Foto - - - Falar - - - Controles da câmera - - - Configurações de voz - - - Informações sobre o terreno que você está visitando - - - Mudar seu avatar - - - Escolha um avatar completo - - - Construindo objetos e redimensionando terreno - - - Bater papo com pessoas próximas usando texto - - - Bússola - - - Destinos de interesse - - - Gestos para seu avatar - - - Como executar tarefas comuns - - - Exibir e usar seus pertences - - - Mapa-múndi - - - Faça compras - - - Mostrar quem está aqui - - - Movendo seu avatar - - - Transferir itens para o seu mercado para venda - - - Amigos, grupos e pessoas próximas - - - Lugares mostrados como favoritos em seu perfil - - - Lugares salvos - - - Preferências - - - Edite ou visualize seu perfil - - - Encontre lugares, eventos, pessoas - - - Tirar uma foto - - - Fale com pessoas próximas usando seu microfone - - - Alterar o ângulo da câmera - - - Controles de volume das chamadas e pessoas próximas a você no mundo virtual - - - atualmente na sua barra de ferramentas inferior - - - atualmente na sua barra de ferramentas esquerda - - - atualmente na sua barra de ferramentas direita - - - Reter% - - - Detalhe - - - Detalhamento maior - - - Superfície - - - Sólido - - - Conclusão - - - Visualizar - - - Normal - - - http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer - - - Nenhum - - - Afeta o navmesh - - - Personagem - - - (Múltiplo) - - - Muito baixo - - - Baixo - - - Médio - - - Alto - - - Muito alto - - - O residente não pode visitar a região. - - - [Usuário] - - diff --git a/indra/newview/skins/steam/xui/ru/strings.xml b/indra/newview/skins/steam/xui/ru/strings.xml deleted file mode 100644 index 03b8a153df..0000000000 --- a/indra/newview/skins/steam/xui/ru/strings.xml +++ /dev/null @@ -1,5060 +0,0 @@ - - - - - Second Life - - - Second Life - - - SECOND LIFE - - - Сетка Second Life - - - Портал поддержки Second Life - - - Проверка оборудования... - - - Загружается [APP_NAME]... - - - Очистка кэша... - - - Инициализация кэша текстур... - - - Инициализация виртуальной файловой системы... - - - Ошибка инициализации графики. Обновите графический драйвер! - - - Восстановление... - - - Изменение разрешения... - - - Собственная яркость (устаревший параметр) - - - Вход... Может показаться, что программа [APP_NAME] зависла. Ожидайте. - - - Вход... - - - Аутентификация - - - Идет обслуживание аккаунта... - - - Предыдущая попытка входа была неудачной. Вход: попытка № [NUMBER] - - - Загрузка мира... - - - Инициализация встроенного веб-браузера... - - - Инициализация мультимедиа... - - - Загрузка шрифтов... - - - Проверка файлов кэша (может занять 60-90 с)... - - - Обработка ответа... - - - Инициализация мира... - - - Декодирование изображений... - - - Инициализация QuickTime... - - - QuickTime не найден – ошибка инициализации. - - - Успешная инициализация QuickTime. - - - Запрос возможностей региона... - - - Запрос возможностей региона, попытка [NUMBER]... - - - Устанавливается связь с регионом... - - - Подключение к региону... - - - Загрузка одежды... - - - Сервер возвратил недействительный или поврежденный сертификат. Обратитесь к администратору сетки. - - - Для доступа к серверу использовалось недействительное имя узла. Проверьте URL-адрес SL или имя узла в сетке. - - - Судя по всему, истек срок действия сертификата, возвращенного сеткой. Проверьте время, установленное в системе, или обратитесь к администратору сетки. - - - Не удалось использовать в протоколе SSL сертификат, возвращенный сервером. Обратитесь к администратору сетки. - - - В цепочке сертификатов серверов слишком много сертификатов. Обратитесь к администратору сетки. - - - Не удалось проверить подпись сертификата, возвращенного сервером сетки. Обратитесь к администратору сетки. - - - Ошибка сети: не удалось установить соединение. Проверьте подключение к сети. - - - Ошибка входа. - - - Выйти - - - http://join.secondlife.com/index.php?sourceid=1206_steam&lang=ru-RU - - - У клиента, которым вы пользуетесь, больше нет доступа к игре Second Life. Загрузить новую версию клиента можно по адресу -http://secondlife.com/download - -Дополнительные сведения см. в разделе вопросов и ответов по адресу -http://secondlife.com/viewer-access-faq - - - Доступно необязательное обновление клиента: [VERSION] - - - Необходимо обновить клиент: [VERSION] - - - С этого клиента уже выполнен вход. - - - Извините! Не удается обеспечить ваш вход. -Убедитесь, что вы правильно ввели: - * имя пользователя (например, bobsmith12 или steller.sunshine) - * пароль -Кроме того, убедитесь, что режим Caps Lock отключен. - - - В качестве меры предосторожности ваш пароль изменен. -Перейдите на страницу своего аккаунта по адресу http://secondlife.com/password -и ответьте на контрольный вопрос, чтобы восстановить свой пароль -Приносим извинения за неудобство. - - - В нашу систему внесены изменения, поэтому вам следует восстановить свой пароль. -Перейдите на страницу своего аккаунта по адресу http://secondlife.com/password -и ответьте на контрольный вопрос, чтобы восстановить свой пароль -Приносим извинения за неудобство. - - - Сайт Second Life временно закрыт на техническое обслуживание. -В данное время входить могут только сотрудники. -Обновление состояния см. на веб-странице www.secondlife.com/status. - - - Вход в Second Life временно ограничен, чтобы сохранить наивысшее качество игрового мира для текущих пользователей. - -В это время у пользователей с бесплатными аккаунтами не будет доступа к Second Life, чтобы у тех, кто заплатил, было больше места. - - - Нельзя входить в игру Second Life на этом компьютере. -Если вы считаете, что это ошибка, отправьте сообщение по адресу -support@secondlife.com. - - - Ваш аккаунт не будет доступен до -[TIME] по тихоокеанскому времени. - - - В данное время нам не удается выполнить ваш запрос. -Обратитесь за помощью в службу поддержки Second Life по адресу http://secondlife.com/support. -Если вам не удается изменить свой пароль, позвоните по телефону (866) 476-9763. - - - При входе обнаружена несогласованность данных. -Напишите сообщения по адресу support@secondlife.com. - - - Выполняется небольшое обслуживание вашего аккаунта. -Ваш аккаунт не будет доступен до -[TIME] по тихоокеанскому времени. -Если вы считаете, что это ошибка, отправьте сообщение по адресу support@secondlife.com. - - - В ответ на запрос выхода симулятор возвратил сообщение о сбое. - - - В системе выполняется выход для вашего аккаунта. -Она будет недоступна до -[TIME] по тихоокеанскому времени. - - - Не удается создать допустимый сеанс. - - - Не удается подключиться к симулятору. - - - Вы можете входить в Second Life только -от [START] до [END] по тихоокеанскому времени. -Заходите в это время. -Если вы считаете, что это ошибка, отправьте сообщение по адресу support@secondlife.com. - - - Неправильные параметры. -Если вы считаете, что это ошибка, отправьте сообщение по адресу support@secondlife.com. - - - Имя должно состоять только из букв и цифр. -Если вы считаете, что это ошибка, отправьте сообщение по адресу support@secondlife.com. - - - Фамилия должна состоять только из букв и цифр. -Если вы считаете, что это ошибка, отправьте сообщение по адресу support@secondlife.com. - - - Регион переходит в автономный режим. -Попробуйте повторно войти через минуту. - - - Агент отсутствует в регионе. -Попробуйте повторно войти через минуту. - - - Вход в регион выполнен в другом сеансе. -Попробуйте повторно войти через минуту. - - - Для региона выполнен выход в предыдущем сеансе. -Попробуйте повторно войти через минуту. - - - Для региона все еще выполняется выход в предыдущем сеансе. -Попробуйте повторно войти через минуту. - - - В последнем сеансе для региона выполнен выход. -Попробуйте повторно войти через минуту. - - - Для региона начат процесс выхода. -Попробуйте повторно войти через минуту. - - - Во время вашего последнего сеанса система начала процедуру выхода. -Попробуйте повторно войти через минуту. - - - Возможно, в этом регионе возникли проблемы. Проверьте подключение к Интернету. - - - Сохранение настроек... - - - Выполняется выход... - - - Игра закрывается... - - - Произошло отключение от региона, в котором вы находились. - - - Вы отправлены в недействительный регион. - - - Тестирование отключения клиента - - - Человек - - - (нет имени) - - - Владелец: - - - Общедоступно - - - (группа) - - - Продается: L$[AMOUNT] - - - Стройка в составе группы - - - Стройка запрещена - - - Стройка в составе группы - - - Небезопасно - - - Полеты запрещены - - - Скрипты для группы - - - Скрипты запрещены - - - Земля: - - - Сюда можно перетащить только одну вещь - - - - Нельзя выложить предметы из папке «Торговые исходящие» - - - Часть этих объектов нельзя продать или передать. - - - Ваша папка «Торговые исходящие» может получать вещи только непосредственно из вашего инвентаря - - - Носимые предметы нельзя поместить в папку «Торговые исходящие» - - - Визитки нельзя поместить в папку «Торговые исходящие» - - - Глубина вложения папок превышает 3 - - - В папке верхнего уровня более 20 подпапок - - - В папке верхнего уровня более 200 предметов - - - Папку нельзя переместить в ее подпапку - - - Папку нельзя переместить саму в себя - - - Щелкните, чтобы просмотреть эту веб-страницу - - - Щелкните, чтобы просмотреть информацию об этом месте - - - Щелкните, чтобы просмотреть профиль этого жителя - - - Узнать больше об этом жителе - - - Щелкните, чтобы не слышать этого жителя - - - Щелкните, чтобы слышать этого жителя - - - Щелкните, чтобы открыть личный чат с этим жителем - - - Щелкните, чтобы заплатить жителю - - - Щелкните, чтобы предложить этому жителю телепортацию - - - Щелкните, чтобы предложить этому жителю дружбу - - - Щелкните, чтобы просмотреть описание группы - - - Щелкните, чтобы просмотреть описание события - - - Щелкните, чтобы просмотреть эту рекламу - - - Щелкните, чтобы посмотреть описание участка - - - Щелкните, чтобы телепортироваться в это место - - - Щелкните, открыть описание этого объекта - - - Щелкните, чтобы посмотреть это место на карте - - - Щелкните, чтобы выполнить команду secondlife:// - - - - Телепортироваться в - - - Показать карту для - - - Откл. звук - - - Вкл. звук - - - IM - - - Заплатить - - - Предложить телепортацию в - - - Предложить дружбу - - - Закрыть (⌘W) - - - Закрыть (Ctrl+W) - - - Закрыть - - - Развернуть - - - Свернуть - - - Отделить - - - Присоединить - - - Показать справку - - - Поиск... - - - Ничего не найдено. - - - Получение... - - - Заметки о выпуске - - - Загрузка... - - - (без имени) - - - (ожидание) - - - (несколько) - - - (нет) - - - [ORDER] абонента Avaline - - - Ошибок нет - - - Запрос актива: сбой - - - Запрос актива: файл не существует - - - Запрос актива: актив не найден в базе данных - - - Конец файла - - - Не удается открыть файл - - - Файл не найден - - - Вышло время передачи файла - - - Обрыв в канале - - - Не достигнута договоренность по цене между клиентом и сервером - - - Неизвестный статус - - - текстуру - - - звук - - - визитку - - - закладку - - - старый скрипт - - - одежду - - - объект - - - заметку - - - папку - - - корневой каталог - - - скрипт LSL2 - - - байт-код LSL - - - текстуру TGA - - - часть тела - - - снимок - - - найденные вещи - - - изображение TGA - - - содержимое корзины - - - изображение JPEG - - - анимацию - - - жест - - - состояние симуляции - - - избранное - - - ссылку - - - ссылку на папку - - - сетка - - - (внешний вид редактируется) - - - Нет на месте - - - Не беспокоить - - - В черном списке - - - Страх - - - Гнев - - - Нет на месте - - - Сальто назад - - - Хохот - - - Широкая улыбка - - - Воздушный поцелуй - - - Скука - - - Поклон - - - Хлопок - - - Учтивый поклон - - - Плач - - - Танец 1 - - - Танец 2 - - - Танец 3 - - - Танец 4 - - - Танец 5 - - - Танец 6 - - - Танец 7 - - - Танец 8 - - - Презрение - - - Питьё - - - Смущение - - - Погрозить пальцем - - - Поднимание кулака - - - Парящий Будда - - - Хмурость - - - Нетерпение - - - Прыжок радости - - - Поцелуй в зад - - - Поцелуй - - - Смех - - - Демонстрация мускулов - - - Грустный отказ - - - Отказ - - - Ня-ня-ня - - - Двойка руками - - - Открывание рта - - - Дружелюбие - - - Указывание на кого-то - - - Указывание на себя - - - Удар левой рукой - - - Удар правой рукой - - - Счет в КНБ - - - КНБ – бумага - - - КНБ – камень - - - КНБ – ножницы - - - Отказ - - - «Вертушка» - - - Грусть - - - Приветствие - - - Крик - - - Пожимание плечами - - - Улыбка - - - Курение не в затяжку - - - Курение в затяжку - - - Бросить сигарету - - - Удивление - - - Удар мечом - - - Вспышка гнева - - - Показ языка - - - Приветствие рукой - - - Шепот - - - Свист - - - Подмигивание - - - Подмигивание по-голливудски - - - Беспокойство - - - Радостное согласие - - - Согласие - - - Несколько - - - Загрузка... - - - Не в сети - - - [AREA] м² L$[PRICE] - - - Ничего не найдено. - - - ОК - - - Преждевременный конец файла - - - Не удается найти объект ROOT или JOINT. - - - шепчет: - - - кричит: - - - Подключение к голосовому чату... - - - Подключение установлено - - - В этом месте голосовая связь недоступна - - - Отключение от общего голосового чата - - - Будет установлено подключение к локальному голосовому чату - - - Объекту «[OBJECTNAME]», который принадлежит пользователю «[OWNERNAME]» и находится в [REGIONPOS] в регионе «[REGIONNAME]», предоставлено разрешение: [PERMISSIONS]. - - - Объекту «[OBJECTNAME]», который принадлежит пользователю «[OWNERNAME]» и находится в [REGIONPOS] в регионе «[REGIONNAME]», отказано в разрешении: [PERMISSIONS]. - - - Разрешив доступ к своему аккаунту, вы также разрешите объекту: - - - У вас берут Linden-деньги - - - Действия при активации элементов управления - - - Новое сопоставление элементов управления - - - Анимировать ваш аватар - - - Прикрепить к аватару - - - Отказаться от владения, сделать всеобщим - - - Связать или отменить связь с другими объектами - - - Добавление и удаление связей с другими объектами - - - Изменить разрешения - - - Следить за камерой - - - Управлять камерой - - - Телепортировать вас - - - Нет подключения - - - Общий - - - Умеренный - - - Для взрослых - - - Не в сети - - - Неизвестно - - - (неизвестно) - - - Землевладение/весь регион - - - Землевладение/поместье - - - Материк/поместье - - - Материк/весь регион - - - Все файлы - - - Звуки - - - Анимация - - - Изображения - - - Сохранить - - - Загрузить - - - Изображения TGA - - - Изображения BMP - - - Видео AVI - - - Анимация XAF - - - XML-файл - - - RAW-файл - - - Несжатые изображения - - - Загрузить файлы - - - Выбрать каталог - - - Скрипты - - - Словари - - - На месте - - - Нет на месте - - - Не занят(а) - - - Не беспокоить - - - Фигура - - - Кожа - - - Волосы - - - Глаза - - - Рубашка - - - Брюки - - - Обувь - - - Носки - - - Пиджак - - - Перчатки - - - Майка - - - Трусы - - - Юбка - - - Альфа-маска - - - Тату - - - Физические данные - - - ошибка - - - нет - - - Рубашка не надета - - - Брюки не надеты - - - Обувь не надета - - - Носки не надеты - - - Пиджак не надет - - - Перчатки не надеты - - - Майка не надета - - - Трусы не надеты - - - Юбка не надета - - - Альфа-маска не надета - - - Тату не надето - - - Физика не учитывается - - - ошибка - - - Создать фигуру - - - Создать кожу - - - Создать волосы - - - Создать глаза - - - Создать рубашку - - - Создать брюки - - - Создать обувь - - - Создать носки - - - Создать пиджак - - - Создать перчатки - - - Создать майку - - - Создать трусы - - - Создать юбку - - - Создать альфа-маску - - - Создать тату - - - Создать физику - - - ошибка - - - Создать [WEARABLE_ITEM] - - - Далее - - - ОК - - - Групповое уведомление - - - Групповые уведомления - - - Отправитель - - - Вложение: - - - Здесь можно просмотреть последние уведомления или отказаться от их получения. - - - Открыть вложение - - - Сохранить вложение - - - Предложена телепортация - - - Пока вы отсутствовали, пришли новые уведомления. - - - Других уведомлений: %d - - - Правая рука - - - Голова - - - Левая рука - - - Левая нога - - - Торс - - - Правая нога - - - низкая - - - средняя - - - высокая - - - Нажмите ESC, чтобы вернуться к обычному обзору - - - Не нашли того, что вам нужно? Воспользуйтесь [secondlife:///app/search/all/[SEARCH_TERM] поиском]. - - - Не нашли того, что вам нужно? Воспользуйтесь [secondlife:///app/search/places/[SEARCH_TERM] поиском]. - - - Перетащите сюда закладку, чтобы добавить ее в список избранного. - - - В вашем инвентаре нет копии этой текстуры - - - Здесь будут показаны ваши покупки из торгового центра. Их можно будет перетащить в ваш инвентарь для использования. - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/ - - - http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4 - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/imports - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/learn_more - - - Продавать вещи в торговом центре может кто угодно. - - - - Если вы хотите стать торговцем, [[MARKETPLACE_CREATE_STORE_URL] создайте магазин]. - - - Ваша папка «Исходящие» пуста. - - - - Перетащите папки в эту область и щелкните «Отправить в торговый центр», чтобы выставить их на продажу в [[MARKETPLACE_DASHBOARD_URL] Торговом центре]. - - - Ошибок нет - - - Ошибка. Прежде чем отправлять вещи в магазин, необходимо зарегистрироваться как торговец (бесплатно). - - - Ошибка. В этой папке нет контента. - - - Ошибка. Не удается передать эту вещь, поскольку в вашем торговом аккаунте слишком много вещей, не связанных с продуктами. Чтобы исправить эту ошибку, войдите на веб-сайт торгового центра и уменьшите число своих вещей, которые ни с чем не связаны. - - - Ошибка. Эта вещь содержит слишком много объектов. Исправьте эту ошибку, сложив объекты в коробки и уменьшив их общее число (должно быть меньше 200). - - - Ошибка. Эта вещь содержит слишком много уровней вложенных папок. Измените структуру так, чтобы уровней вложенных папок было не более 3. - - - Ошибка. Эту вещь нельзя продать в магазине. - - - Ошибка. Эта вещь создает проблему. Повторите попытку позже. - - - Открыть закладки - - - - - - - - - Загрузка содержимого... - - - Нет контента - - - - - Да - - - Нет - - - - - - - - - - - - - - - - - - - - - - - - - - Мой инвентарь - - - Библиотека - - - Текстуры - - - Звуки - - - Визитки - - - Закладки - - - Скрипты - - - Одежда - - - Объекты - - - Заметки - - - Новая папка - - - Инвентарь - - - Несжатые изображения - - - Части тела - - - Корзина - - - Фотоальбом - - - Бюро находок - - - Несжатые звуки - - - Анимация - - - Жесты - - - Мое избранное - - - Мое избранное - - - Текущий костюм - - - Начальные костюмы - - - Мои костюмы - - - Аксессуары - - - Меши - - - Друзья - - - Все - - - Нет прикрепленных объектов - - - Присоединения (осталось гнезд: [COUNT]) - - - Купить - - - Купить за L$ - - - Камень - - - Металл - - - Стекло - - - Дерево - - - Плоть - - - Пластик - - - Резина - - - Светлый - - - SHIFT - - - CTRL - - - Грудь - - - Череп - - - Левое плечо - - - Правое плечо - - - Левая кисть - - - Правая кисть - - - Левая ступня - - - Правая ступня - - - Позвоночник - - - Таз - - - Рот - - - Подбородок - - - Левое ухо - - - Правое ухо - - - Левый глаз - - - Правый глаз - - - Нос - - - Правое плечо - - - Правое предплечье - - - Левое плечо - - - Левое предплечье - - - Правое бедро - - - Правое колено - - - Правая голень - - - Левое бедро - - - Левое колено - - - Левая голень - - - Живот - - - Левая грудь - - - Правая грудь - - - Шея - - - Центр аватара - - - Неверная точка присоединения - - - [AGEYEARS] [AGEMONTHS] - - - [AGEYEARS] - - - [AGEMONTHS] - - - [AGEWEEKS] - - - [AGEDAYS] - - - Сегодня - - - [COUNT] год - - - [COUNT] года - - - [COUNT] лет - - - [COUNT] месяц - - - [COUNT] месяца - - - [COUNT] месяцев - - - [COUNT] неделя - - - [COUNT] недели - - - [COUNT] недель - - - [COUNT] день - - - [COUNT] дня - - - [COUNT] дней - - - [COUNT] участник - - - [COUNT] участника - - - [COUNT] участников - - - Житель - - - Гость - - - Учредитель - - - Сотрудник Linden Lab - - - Есть информация о платежах - - - Есть зарегистрир. информация о платежах - - - Нет информации о платежах - - - Возраст проверен - - - Возраст не проверен - - - В центре 2 - - - Вверху справа - - - Вверху - - - Вверху слева - - - В центре - - - Внизу слева - - - Внизу - - - Внизу справа - - - Загружено, компилируется - - - Скрипт не найден на сервере. - - - Проблема при загрузке - - - Недостаточно разрешений для загрузки скрипта. - - - Недостаточно разрешений для - - - Неизвестный сбой загрузки - - - Ход повторной компиляции - - - скомпилировать повторно - - - Ход сброса - - - сброс - - - Ход запуска - - - запустить - - - Ход остановки выполнения - - - прекратить выполнение - - - Компиляция успешно выполнена! - - - Компиляция успешно выполнена, сохраняется... - - - Сохранение завершено. - - - Скрипт (объект вне области) - - - Объект [OBJECT] пользователя [OWNER] - - - нет - - - - (Неизвестно) - - - - - [day,datetime,utc].[mthnum,datetime,utc].[year,datetime,utc] - - - - - Баланс - - - Расход - - - Приход - - - Итого - - - Не найдены данные для группы - - - родовое землевладение - - - материк - - - подростковый - - - все - - - ошибка - - - все землевладения пользователя [OWNER] - - - все ваши землевладения - - - все землевладения пользователя [OWNER], которыми вы управляете - - - Допущенные жители: ([ALLOWEDAGENTS], не более [MAXACCESS]) - - - Допущенные группы: ([ALLOWEDGROUPS], не более [MAXACCESS]) - - - Память под скрипты на участке - - - Участков в списке: [PARCELS] - - - Используется памяти: [COUNT] КБ из [MAX] КБ; доступно: [AVAILABLE] КБ - - - Используется памяти: [COUNT] КБ - - - URL-адреса скрипта участков - - - Используется URL-адресов: [COUNT] из [MAX] (доступно: [AVAILABLE]) - - - Используется URL-адресов: [COUNT] - - - Ошибка при запросе данных - - - Участок не выбран - - - Ошибка. Сведения о скрипте доступны только в текущем регионе - - - Получение данных... - - - У вас нет прав для исследования этого участка - - - Сидит на - - - Грудь - - - Голова - - - Левое плечо - - - Правое плечо - - - Левая кисть - - - Правая кисть - - - Левая ступня - - - Правая ступня - - - Спина - - - Таз - - - Рот - - - Подбородок - - - Левое ухо - - - Правое ухо - - - Левый глаз - - - Правый глаз - - - Нос - - - Правое плечо - - - Правое предплечье - - - Левое плечо - - - Левое предплечье - - - Правое бедро - - - Правое колено - - - Правая голень - - - Левое бедро - - - Левое колено - - - Левая голень - - - Живот - - - Правая грудь - - - Левая грудь - - - Данные в игре в центре 2 - - - Данные в игре вверху справа - - - Данные в игре вверху в центре - - - Данные в игре вверху слева - - - Данные в игре в центре 1 - - - Данные в игре внизу слева - - - Данные в игре внизу - - - Данные в игре внизу справа - - - Строка [LINE], столбец [COLUMN] - - - Найдено: [COUNT] - - - [hour,datetime,slt]:[min,datetime,slt] - - - [day,datetime,slt].[mthnum,datetime,slt] - - - Подключение к объекту - - - Новый скрипт - - - У адресата вашего сообщения задан статус «Не беспокоить». Ваше сообщение все равно будет отображено на панели IM для просмотра позже. - - - (по имени) - - - (для жителя) - - - (для объекта) - - - (для группы) - - - (внешний) - - - Нет соглашения для этого землевладения. - - - Нет соглашения для этого землевладения. Земля в этом землевладении продается его владельцем, а не компанией Linden Lab. Чтобы узнать подробности о продаже, обратитесь к землевладельцу. - - - - - - Собственность группы - - - Общая собственность - - - Локальные настройки - - - Региональные настройки - - - Щелчки: телепорт [TELEPORT], карта [MAP], профиль [PROFILE] - - - (будет обновлено после публикации) - - - Вы не создали подборки или рекламы. Нажмите кнопку со знаком «плюс» ниже, чтобы создать подборку или рекламу - - - У жителя нет подборки или рекламы - - - Загрузка... - - - Предварительный просмотр - - - Свойства - - - Объект с именем - - - принадлежит группе - - - принадлежит известной группе - - - принадлежит - - - принадлежит неизвестному пользователю - - - дал(а) вам - - - Вы не приняли [DESC] от жителя <nolink>[NAME]</nolink>. - - - Итого - - - куплено - - - уплачено вам - - - уплачено в - - - куплен пропуск в - - - уплачено за событие - - - выплачено призовых за событие - - - Баланс - - - Расход - - - Приход - - - [weekday,datetime,utc], [day,datetime,utc] [mth,datetime,utc] [year,datetime,utc] - - - Контент - - - Купленные вещи - - - Отмена - - - Передача [NAME] стоит L$[AMOUNT] - - - Стоимость покупки: L$[AMOUNT] - - - Неизвестное расширение файла .%s -Ожидаются расширения: WAV, TGA, BMP, JPG, JPEG или BVH - - - Заблокировать - - - Заблокировать - - - Разблокировать - - - Разблокировать - - - Добавить в мои закладки... - - - Изменить мою закладку... - - - ⌃ - - - ⌘ - - - ⌥ - - - ⇧ - - - CTRL+ - - - ALT+ - - - SHIFT+ - - - Файл сохранен - - - Получение - - - до полудня - - - после полудня - - - Тихоокеанское время - - - Летнее тихоокеанское время - - - Вперед - - - Влево - - - Вправо - - - Назад - - - Север - - - Юг - - - Запад - - - Восток - - - Вверх - - - Вниз - - - Все категории - - - Покупки - - - Земельная рента - - - Аренда имущества - - - Особое событие - - - Новые продукты - - - Род занятий - - - Хочу найти - - - Услуги - - - Личное сообщение - - - Нет - - - Место Linden - - - Для взрослых - - - Искусство и культура - - - Бизнес - - - Образование - - - Игры - - - Места встреч - - - Для новичков - - - Парки и природа - - - Проживание - - - Стадия - - - Другое - - - Аренда - - - Все - - - Вы - - - : - - - , - - - ... - - - *** - - - ( - - - ) - - - . - - - ' - - - --- - - - Несколько источников мультимедиа - - - Мультимедиа – воспроизведение/пауза - - - Ошибка при анализе командной строки. -См.: http://wiki.secondlife.com/wiki/Client_parameters -Ошибка: - - - Использование командной строки [APP_NAME]: - - - Приложению [APP_NAME] не удается получить доступ к нужному файлу. -Возможно, выполняется несколько копий или в системе неправильно открыт файл. -Если это сообщение по-прежнему будет отображаться, перезагрузите компьютер и повторите попытку. -Если и это не поможет, возможно, придется повторно установить приложение [APP_NAME]. - - - Неустранимая ошибка - - - Для работы [APP_NAME] необходим процессор с поддержкой AltiVec (версии G4 или более поздней). - - - [APP_NAME] уже выполняется. -Поищите значок программы на панели задач. -Если это сообщение по-прежнему будет отображаться, перезагрузите компьютер. - - - По-видимому, при предыдущем запуске приложения [APP_NAME] оно зависло или в нем возник сбой. -Отправить отчет о сбое? - - - Уведомление - - - Приложению [APP_NAME] не удается обнаружить DirectX 9.0b или более поздних версий. -В приложении [APP_NAME] используется DirectX для проверки оборудования и выявления устаревших драйверов, из-за которых может снизиться стабильность работы и быстродействие, а также возникнуть сбои. Настоятельно рекомендуется установить DirectX 9.0b, хотя приложение [APP_NAME] работает и без этого компонента. -Продолжить? - - - Внимание! - - - В ОС Linux автоматическое обновление еще не реализовано. -Загрузите новую версию на сайте www.secondlife.com. - - - Ошибка RegisterClass - - - Ошибка - - - Невозможна работа в полноэкранном режиме на экране [WIDTH] x [HEIGHT]. -Запущено в окне. - - - Ошибка завершения работы при удалении окна (сбой функции DestroyWindow()) - - - Ошибка завершения работы - - - Не удается создать контекст устройства GL - - - Не удается найти подходящий формат пикселей - - - Не удается получить описание формата пикселей - - - Для работы [APP_NAME] необходим режим True Color (32 бита). -Задайте в настройках дисплея 32-битный режим цвета. - - - Не удается запустить [APP_NAME] из-за отсутствия доступа к 8-битному альфа-каналу. Обычно эта проблема возникает из-за неполадок с драйвером видеокарты. -Установите новые драйверы видеокарты. -Также задайте для монитора 32-битный режим True Color (Панель управления > Экран > Параметры). -Если это сообщение продолжает отображаться, обратитесь на сайт [SUPPORT_SITE]. - - - Не удается задать формат пикселей - - - Не удается создать контекст визуализации GL - - - Не удается активировать контекст визуализации GL - - - Не удается запустить приложение [APP_NAME], поскольку драйверы видеокарты неправильно установлены, устарели или предназначены для оборудования, которое не поддерживается. Установите или переустановите последние драйверы видеокарты. -Если это сообщение продолжает отображаться, обратитесь на сайт [SUPPORT_SITE]. - - - Жидкие - - - Полностью белые - - - Глаза как в аниме - - - Дугой - - - Длина рук - - - Прикреплено - - - Приросшие мочки - - - Затылок - - - С мешками - - - Челки - - - Бусинки - - - Размер живота - - - Большой - - - Большой зад - - - Пышные волосы: сзади - - - Пышные волосы: спереди - - - Пышные волосы: сверху - - - Большая голова - - - Выпуклая грудь - - - Большие «шипы» - - - Черный - - - Светлый - - - Светлые волосы - - - Румяна - - - Цвет румян - - - Прозрачность румян - - - Тип тела - - - Жировая прослойка - - - Веснушки - - - Полное тело - - - Полнота - - - Худое тело - - - Ноги колесом - - - Высота груди - - - Ложбинка между грудей - - - Размер груди - - - Ширина переносицы - - - Широкая - - - Размер надбровных дуг - - - Выпученные глаза - - - Выпученные глаза - - - Картошкой - - - Нос картошкой - - - Масса груди - - - Гладкость груди - - - Обвислость груди - - - Аэродинамика груди - - - Верхняя граница - - - Упругость - - - Отклик - - - Затухание - - - Верхняя граница - - - Упругость - - - Отклик - - - Затухание - - - Верхняя граница - - - Упругость - - - Отклик - - - Затухание - - - Масса живота - - - Гладкость живота - - - Обвислость живота - - - Инертность живота - - - Верхняя граница - - - Упругость - - - Отклик - - - Затухание - - - Масса зада - - - Гладкость зада - - - Обвислость зада - - - Инертность зада - - - Верхняя граница - - - Упругость - - - Отклик - - - Затухание - - - Верхняя граница - - - Упругость - - - Отклик - - - Затухание - - - Кустистые брови - - - Пышные - - - Размер зада - - - Обвислость зада - - - Турнюр - - - Без турнюра - - - Большой турнюр - - - «Чарли Чаплин» - - - Скулы - - - Размер грудной клетки - - - Угол подбородка - - - Ямка на подбородке - - - Шкиперская бородка - - - Толщина подбородка - - - Мощный подбородок - - - Подбородок внутрь - - - Подбородок наружу - - - Переход от подбородка к шее - - - Чистый - - - Ямка - - - Близко посаженные - - - Закрыто - - - Закрыто сзади - - - Закрыто спереди - - - Закрыто слева - - - Закрыто справа - - - Кошелек для мелочи - - - Вырез сзади - - - Вырез спереди - - - Уголки опущены - - - Уголки подняты - - - Измятый - - - Искривленный нос - - - Манжеты - - - Темный - - - Темно-зеленый - - - Темнее - - - Глубоко - - - Стандартные каблуки - - - Густые - - - Двойной подбородок - - - Вниз - - - Больше - - - Оттопыренность ушей - - - Размер ушей - - - Кончики ушей - - - Яйцеголовость - - - Мешки под глазами - - - Цвет глаз - - - Глубина глаз - - - Светлость глаз - - - Открытость глаз - - - Вытаращить глаз - - - Размер глаз - - - Расстояние между глазами - - - Линия бровей - - - Густота бровей - - - Высота бровей - - - Кончики бровей - - - Размер бровей - - - Длина ресниц - - - Подводка - - - Цвет подводки - - - Выпученные глаза - - - Перекос лица - - - Черты лица - - - Широко расставленные глаза - - - Толстые губы - - - Женщина - - - Без пальцев - - - С пальцами - - - С манжетами - - - Плоские - - - Плоский зад - - - Плоская голова - - - Плоский носок - - - Размер ступни - - - Наклон лба - - - Мощный лоб - - - Веснушки - - - Челка спереди - - - Полностью назад - - - Подводка полностью - - - Полностью наперед - - - Волосы по бокам - - - По бокам - - - Блестящие - - - Пальцы перчаток - - - Длина перчаток - - - Волосы - - - Волосы: сзади - - - Волосы: спереди - - - Волосы: по бокам - - - Волосы на глаза - - - Толщина волос - - - Толщина волос - - - Зачес - - - Зачес назад - - - Зачес вправо - - - Волосы: объем - - - Размер кисти - - - Длинные усы - - - Длина головы - - - Форма головы - - - Размер головы - - - Вытянутость головы - - - Высота каблука - - - Форма каблука - - - Рост - - - Высокие - - - Высокий каблук - - - Челюсть высоко - - - Высокая платформа - - - Высокий и плотный - - - Выше - - - Длина бедер - - - Ширина бедер - - - Внутрь - - - Цвет внутренних теней - - - Прозрачность внутр. теней - - - Внутренние уголки глаз - - - Тени на внутренних уголках - - - Внутренние тени - - - Длина пиджака - - - Смятый пиджак - - - Угол челюсти - - - Выступание челюсти - - - Форма челюсти - - - Прикрепить - - - Щеки - - - Угол колен - - - Колченогие - - - Больше - - - Большие кисти - - - Левый пробор - - - Длина ног - - - Мышцы на ногах - - - Меньше - - - Меньше жира - - - Меньше борода - - - Меньше веснушек - - - Менее полное - - - Меньше притяжения - - - Меньше - - - Меньше мышц - - - Меньше мышц - - - Меньше румян - - - Меньше округлости - - - Меньше - - - Меньше угловатости - - - Меньше объема - - - Меньше - - - Светлее - - - Ямка между губ - - - Глубина ямки - - - Полнота губ - - - Розоватость губ - - - Пропорция губ - - - Толщина губ - - - Ширина губ - - - Блеск губ - - - Губная помада - - - Цвет помады - - - Длиннее - - - Длинная голова - - - Длинные бедра - - - Длинные ноги - - - Длинная шея - - - Длинные хвосты по бокам - - - Длинный хвост сзади - - - Длинный торс - - - Длинные руки - - - Свободные брюки - - - Свободная рубашка - - - Свободные рукава - - - Отложения на талии - - - Низкие - - - Низкий каблук - - - Челюсть низко - - - Низкая платформа - - - Низкий и свободный - - - Ниже - - - Спинка носа - - - Щеки ниже - - - Мужчина - - - Пробор по центру - - - Больше - - - Больше румян - - - Больше жира - - - Больше борода - - - Больше теней - - - Больше веснушек - - - Более полное - - - Большее притяжение - - - Больше помады - - - Больше - - - Больше нижняя губа - - - Больше мышц - - - Больше мышц - - - Больше румянца - - - Больше округлости - - - Больше - - - Более наклонный - - - Более квадратная - - - Больше верхняя губа - - - Более вертикальный - - - Больше объема - - - Больше - - - Усы - - - Угол рта - - - Положение рта - - - Ирокез - - - Мускулистое - - - Бакенбарды - - - Лак для ногтей - - - Цвет лака - - - Узко - - - Узко сзади - - - Узкий перед - - - Узкие губы - - - Естественный - - - Длина шеи - - - Толщина шеи - - - Без румян - - - Без подводки - - - Без теней - - - Без блеска - - - Без помады - - - Без пробора - - - Без лака - - - Не красные - - - Без «шипов» - - - Нет белого - - - Без морщин - - - Ниже обычного - - - Выше обычного - - - Нос влево - - - Нос вправо - - - Размер носа - - - Толщина носа - - - Загнутость кончика носа - - - Форма кончика носа - - - Ширина носа - - - Перегородка - - - Ширина ноздрей - - - Непрозрачный - - - Открыто - - - Открыто сзади - - - Открыто спереди - - - Открыто слева - - - Открыто справа - - - Оранжевый - - - Наружу - - - Цвет внешних теней - - - Прозрачность внеш. теней - - - Внешние уголки глаз - - - Тени во внешних уголках - - - Внешние тени - - - Глубокий прикус - - - Гульфик - - - Покрашенные - - - Бледный - - - Шаг - - - Облегающие брюки - - - Длина - - - Талия брюк - - - Смятость брюк - - - Пробор - - - Челка с пробором - - - Грудные мышцы - - - Пигментация - - - Хвосты по бокам - - - Розовый - - - Розовее - - - Высота платформы - - - Ширина платформы - - - Острые - - - Острый каблук - - - Хвост сзади - - - Пышная юбка - - - Левый глаз - - - Правый глаз - - - Пухлые - - - Припухлость век - - - Цвета радуги - - - Рыжие волосы - - - Обычное - - - Правый пробор - - - Розовое лицо - - - Круглое - - - Румянец - - - Румяный - - - Взъерошенные - - - Галифе - - - Сухопарая нога - - - Разделить - - - Мелко - - - Скос сзади - - - Перекос лица - - - Скос спереди - - - Скос влево вверх - - - Скос вправо вверх - - - Уменьшено сзади - - - Уменьшено спереди - - - Сдвинуть влево - - - Сдвинуть рот - - - Сдвинуть вправо - - - Низ рубашки - - - Облегание рубашки - - - Помятость рубашки - - - Высота обуви - - - Ниже - - - Короткие руки - - - Короткие ноги - - - Короткая шея - - - Короткие хвосты по бокам - - - Короткий хвост сзади - - - Короткие баки - - - Короткий торс - - - Короткие бедра - - - Плечи - - - Челка набок - - - Бакенбарды - - - Волосы по бокам - - - Волосы по бокам внизу - - - Волосы по бокам вверху - - - Худая шея - - - Облегающая юбка - - - Длина юбки - - - Наклонный лоб - - - Длина рукавов - - - Ширина рукавов - - - Разрез: сзади - - - Разрез: спереди - - - Разрез: слева - - - Разрез: справа - - - Меньше - - - Маленькие кисти - - - Маленькая голова - - - Гладко - - - Приглаженные - - - Длина носков - - - Эспаньолка - - - Жидкие - - - Прическа «шипами» - - - Квадратный - - - Квадратный носок - - - Голова-тыква - - - Вытянутость головы - - - Впалые - - - Впалая грудь - - - Впалые глаза - - - Зачесанные назад - - - Зачесанные вперед - - - Выше - - - Конус сзади - - - Конус спереди - - - Широкий каблук - - - Толстая шея - - - Толстый носок - - - Тонкий - - - Тонкие брови - - - Тонкие губы - - - Тонкий нос - - - Тонкий подбородок - - - Манжеты на резинке - - - Облегающие брюки - - - Облегающая рубашка - - - Облегающая юбка - - - Облегающие рукава - - - Форма носка - - - Толщина носка - - - Длина торса - - - Мускулистость торса - - - Сухопарость торса - - - Не прикреплено - - - Без складок - - - Мезиальный прикус - - - Неестественный - - - Переносица - - - Щеки выше - - - Ямка на подбородке выше - - - Складка верхнего века - - - Вверх - - - Очень красные - - - Высота талии - - - Упитанные - - - Белые волосы - - - Широко - - - Широко сзади - - - Широкий перед - - - Широкие губы - - - Безумный - - - Складки - - - Добавить в закладки - - - Изменить закладку - - - Посмотреть подробную информацию о текущем месте - - - Моя история посещений - - - Купить эту землю - - - Голосовое общение здесь недоступно - - - Полеты запрещены - - - Нельзя толкаться - - - Строительство/выкладывание объектов не разрешено - - - Запускать скрипты запрещено - - - Здоровье - - - Область для взрослых - - - Область умеренной дозволенности - - - Область общей дозволенности - - - Все жители с других участков могут видеть аватары и общаться в чате - - - Возможны неполадки подвижных объектов в этом регионе, пока регион не будет восстановлен. - - - В этом регионе не разрешен динамический поиск пути. - - - Обновление [APP_NAME] - - - Обновляется [APP_NAME]... - - - Устанавливается [APP_NAME]... - - - Клиент [APP_NAME] обновляется до последнего выпуска. Это может занять какое-то время. Проявите терпение. - - - Загрузка обновления... - - - Загружается обновление - - - Не удалось загрузить обновление - - - При обновлении приложения [APP_NAME] возникла ошибка. Загрузите новую версию на сайте www.secondlife.com. - - - Не удалось установить обновление - - - Не удалось запустить клиент - - - [APP_NAME]: Из-за слишком быстрого поступления элементов с [FROM_NAME] автоматический просмотр отключен на [TIME] с - - - [APP_NAME]: из-за слишком быстрого поступления элементов автоматический просмотр отключен на [TIME] с - - - -- Включена регистрация сообщений IM -- - - - [NAME] вводит текст... - - - (Без имени) - - - (Модерируется: голоса по умолчанию отключены) - - - Во время этого звонка текстовый чат недоступен. - - - Ваш текстовый чат отключен модератором группы. - - - Щелкните здесь, чтобы создать IM-сообщение. - - - Кому - - - (Модератор) - - - (Сохранено [LONG_TIMESTAMP]) - - - Для просмотра этого сообщения снимите флажок «Только друзья и группы могут звонить мне и отправлять IM» в окне «Настройки/Приватность». - - - На ваш звонок ответили - - - Вы начали голосовую беседу - - - Вы присоединились к голосовой беседе - - - Житель [NAME] начал голосовую беседу - - - Присоединение к голосовой беседе... - - - Соединение установлено. Выберите команду «Прервать звонок», чтобы повесить трубку - - - Голосовой звонок прерван - - - Соединяется... - - - Спонтанная конференция - - - Конференция с жителем [AGENT_NAME] - - - Предложено пополнить инвентарь - - - Перетаскивайте вещи из инвентаря сюда - - - (Сеанс IM не существует) - - - Вы – единственный пользователь в этом сеансе. - - - [NAME] не в сети. - - - Нажмите кнопку [BUTTON NAME], чтобы участвовать в этом голосовом чате. - - - Вы заблокировали этого жителя. Если отправить ему сообщение, блок автоматически снимется. - - - Ошибка при запросе. Повторите попытку. - - - Ошибка при запросе. Повторите попытку. - - - У вас недостаточно разрешений. - - - Сеанс больше не существует - - - У вас нет этой способности. - - - У вас нет этой способности. - - - Вы – не модератор сеанса. - - - Модератор группы отключил для вас текстовый чат. - - - Модератор группы отключил для вас текстовый чат. - - - Не удается добавить пользователей в сеанс чата с жителем [RECIPIENT]. - - - Не удается отправить ваше сообщение в сеанс чата с жителем [RECIPIENT]. - - - Не удается отправить ваше сообщение в сеанс чата с жителем [RECIPIENT]. - - - Ошибка при модерировании. - - - Вы исключены из группы. - - - Вы исключены из группы. - - - У вас больше нет возможности участвовать в сеансе чата. - - - [SOURCES] сказал что-то новое - - - [SOURCES] сказал что-то новое - - - Истекло время ожидания инициализации сеанса - - - Задано положение дома. - - - http://secondlife.com/landing/voicemorphing - - - Житель [NAME] заплатил вам L$[AMOUNT] за [REASON]. - - - Житель [NAME] заплатил вам L$[AMOUNT]. - - - Вы заплатили жителю [NAME] L$[AMOUNT] за [REASON]. - - - Вы заплатили L$[AMOUNT]. - - - Вы заплатили жителю [NAME] L$[AMOUNT]. - - - Вы заплатили L$[AMOUNT] за [REASON]. - - - Вы не смогли заплатить пользователю [NAME] L$[AMOUNT]: [REASON]. - - - Вы не смогли заплатить L$[AMOUNT] - - - Вы не смогли заплатить пользователю [NAME] L$[AMOUNT] - - - Вы не смогли заплатить L$[AMOUNT]: [REASON]. - - - за [ITEM] - - - за земельный участок - - - за пропуск на землю - - - за передачу земли - - - за создание группы - - - за вступление в группу - - - за передачу по сети - - - за публикацию рекламы - - - Уплата L$[AMOUNT] - - - Передача стоит L$[AMOUNT] - - - Это стоит L$[AMOUNT] - - - Покупка выбранной земли за L$[AMOUNT] - - - Этот объект стоит L$[AMOUNT] - - - Все - - - Должностные лица - - - Владельцы - - - В сети - - - Загружается... - -Жалоба - - - Новая фигура - - - Новая кожа - - - Новые волосы - - - Новые глаза - - - Новая рубашка - - - Новые брюки - - - Новая обувь - - - Новые носки - - - Новый пиджак - - - Новые перчатки - - - Новая майка - - - Новые трусы - - - Новая юбка - - - Новая альфа-маска - - - Новое тату - - - Новая физика - - - Нельзя носить - - - Новый жест - - - Новый скрипт - - - Новая заметка - - - Новая папка - - - Контент - - - Жест - - - Мужские жесты - - - Женские жесты - - - Прочие жесты - - - Жесты в разговорах - - - Стандартные жесты - - - Мужчина – извинение - - - Мужчина – скройтесь - - - Мужчина – воздушный поцелуй - - - Мужчина – фу! - - - Мужчина – скука - - - Мужчина – эй! - - - Мужчина – смех - - - Мужчина – неприятие - - - Мужчина – пожимает плечами - - - Мужчина – показывает язык - - - Мужчина – ух ты! - - - Женщина – смешок - - - Женщина – плач - - - Женщина – смущение - - - Женщина – извинение - - - Женщина – скройтесь - - - Женщина – воздушный поцелуй - - - Женщина – фу! - - - Женщина – скука - - - Женщина – эй! - - - Женщина – эй, бейби! - - - Женщина – смех - - - Женщина – хорошо выглядишь - - - Женщина – сюда! - - - Женщина – просьба - - - Женщина – неприятие - - - Женщина – пожимает плечами - - - Женщина – показывает язык - - - Женщина – ух ты! - - - /поклониться - - - /хлопнуть - - - /счет - - - /затушить - - - /поцелуй меня в зад - - - /силач - - - /нет - - - /нет! - - - /бумага - - - /показать на себя - - - /показать на другого - - - /камень - - - /ножницы - - - /курить - - - /потянуться - - - /свистнуть - - - /да - - - /о да! - - - отошел - - - танец1 - - - танец2 - - - танец3 - - - танец4 - - - танец5 - - - танец6 - - - танец7 - - - танец8 - - - [day,datetime,slt].[mthnum,datetime,slt].[year,datetime,slt] - - - нет/нет - - - Нельзя загружать изображения, размер которых превышает [WIDTH]*[HEIGHT] - - - - Несмотря на наши усилия, что-то неожиданно пошло не так. - - Ознакомьтесь с описанием известных проблем в работе этой службы на сайте status.secondlifegrid.net. - Если проблемы продолжаются, то проверьте подключение к сети и настройки брандмауэра. - - - Воскресенье:Понедельник:Вторник:Среда:Четверг:Пятница:Суббота - - - Вс:Пн:Вт:Ср:Чт:Пт:Сб - - - Январь:Февраль:Март:Апрель:Май:Июнь:Июль:Август:Сентябрь:Октябрь:Ноябрь:Декабрь - - - Янв:Фев:Мар:Апр:Май:Июн:Июл:Авг:Сен:Окт:Ноя:Дек - - - [MDAY] - - - до полудня - - - после полудня - - - [AMOUNT] US$ - - - Членство - - - Роли - - - Удостоверение группы - - - Управление участком - - - Удостоверение участка - - - Параметры участка - - - Способности для участка - - - Доступ к участку - - - Содержимое на участке - - - Управление объектами - - - Бухгалтерия - - - Уведомления - - - Чат - - - Удалить выбранные объекты? - - - Удалить выбранный объект? - - - Для этого костюма нет вещей - - - Выберите редактор, используя параметр ExternalEditor. - - - Не удается найти указанный внешний редактор. -Попробуйте взять путь к редактору в двойные кавычки -(например "/path to my/editor" "%s") - - - Ошибка анализа командной строки для внешнего редактора. - - - Не удалось запустить внешний редактор. - - - Ошибка телепортации: [REASON] - - - Ошибка при анализе ответа переводчика. - - - ESC - - - ПРОБЕЛ - - - ВВОД - - - TAB - - - INS - - - DEL - - - BACKSP - - - SHIFT - - - CTRL - - - ALT - - - CAPSLOCK - - - Стрелка влево - - - Стрелка вправо - - - Стрелка вверх - - - Стрелка вниз - - - HOME - - - END - - - PgUp - - - PgDn - - - F1 - - - F2 - - - F3 - - - F4 - - - F5 - - - F6 - - - F7 - - - F8 - - - F9 - - - F10 - - - F11 - - - F12 - - - + - - - - - - - * - - - / - - - PAD_DIVIDE - - - PAD_LEFT - - - PAD_RIGHT - - - PAD_DOWN - - - PAD_UP - - - PAD_HOME - - - PAD_END - - - PAD_PGUP - - - PAD_PGDN - - - PAD_CENTER - - - PAD_INS - - - PAD_DEL - - - PAD_Enter - - - PAD_BUTTON0 - - - PAD_BUTTON1 - - - PAD_BUTTON2 - - - PAD_BUTTON3 - - - PAD_BUTTON4 - - - PAD_BUTTON5 - - - PAD_BUTTON6 - - - PAD_BUTTON7 - - - PAD_BUTTON8 - - - PAD_BUTTON9 - - - PAD_BUTTON10 - - - PAD_BUTTON11 - - - PAD_BUTTON12 - - - PAD_BUTTON13 - - - PAD_BUTTON14 - - - PAD_BUTTON15 - - - - - - - = - - - ` - - - ; - - - [ - - - ] - - - \ - - - 0 - - - 1 - - - 2 - - - 3 - - - 4 - - - 5 - - - 6 - - - 7 - - - 8 - - - 9 - - - A - - - B - - - C - - - D - - - E - - - F - - - G - - - H - - - I - - - J - - - K - - - L - - - M - - - N - - - O - - - P - - - Q - - - R - - - S - - - T - - - U - - - V - - - W - - - X - - - Y - - - Z - - - Просмотр меток участков (синие) - - - Просмотр меток физических объектов (зеленые) - - - Просмотр меток объектов со скриптами (красные) - - - Просмотр меток объектов со скриптами и функцией касания (красные) - - - Просмотр звуковых меток (желтые) - - - Просмотр медийных меток (белые) - - - Частицы скрыты - - - О земле - - - Внешность - - - Аватар - - - Строительство - - - Чат - - - Компас - - - Пункты - - - Жесты - - - Инструкции - - - Инвентарь - - - Карта - - - Торговый центр - - - Миникарта - - - Ходьба / бег / полет - - - Торговые исходящие - - - Люди - - - Подборка - - - Места - - - Настройки - - - Профиль - - - Поиск - - - Снимок - - - Говорить - - - Управление камерой - - - Настройки голоса - - - Информация о посещаемой вами земле - - - Изменить аватар - - - Выбор аватара - - - Построение объектов и формирование ландшафта - - - Обменивайтесь текстовыми репликами с людьми вокруг вас - - - Компас - - - Интересные места - - - Жесты для аватара - - - Выполнение типичных задач - - - Просмотр и использование вашего имущества - - - Карта мира - - - Покупки - - - Показать людей поблизости - - - Перемещение аватара - - - Перенести предметы в торговый центр для продажи - - - Друзья, группы и люди поблизости - - - Места, которые будут показаны в вашем профиле как избранное - - - Сохраненные вами места - - - Настройки - - - Редактирование или просмотр вашего профиля - - - Поиск мест, событий, людей - - - Сделать снимок - - - Говорите с людьми вокруг вас с помощью микрофона - - - Изменение угла камеры - - - Регулировка громкости вызовов и разговоров с людьми около вас - - - сейчас на нижней панели инструментов - - - сейчас на левой панели инструментов - - - сейчас на правой панели инструментов - - - Остаток% - - - Детализация - - - Более детально - - - Поверхность - - - Сплошной - - - Оболочка - - - Предварительный просмотр - - - Нормальный - - - http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer - - - Нет - - - Влияет на навигационную сетку - - - Персонаж - - - (несколько) - - - Очень низкий - - - Низкий - - - Средний - - - Высокий - - - Очень высокий - - - Житель не может посетить этот регион. - - - [Пользователь] - - diff --git a/indra/newview/skins/steam/xui/tr/strings.xml b/indra/newview/skins/steam/xui/tr/strings.xml deleted file mode 100644 index 4ca10eb6ca..0000000000 --- a/indra/newview/skins/steam/xui/tr/strings.xml +++ /dev/null @@ -1,5061 +0,0 @@ - - - - - Second Life - - - Second Life - - - SECOND LIFE - - - Second Life Ağı - - - Second Life Destek Portalı - - - Donanım saptanıyor... - - - [APP_NAME] yükleniyor... - - - Önbellek temizleniyor... - - - Doku önbelleği başlatılıyor... - - - VFS Başlatılıyor... - - - Grafik başlatma başarılamadı. Lütfen grafik sürücünüzü güncelleştirin! - - - Geri yükleniyor... - - - Çözünürlük değiştiriliyor... - - - Tam parlak (eski) - - - Oturum açılıyor. [APP_NAME] kilitlenmiş görünebilir. Lütfen bekleyin. - - - Oturum açılıyor... - - - Kimlik doğrulaması yapılıyor - - - Hesap bakımı yapılıyor... - - - Önceki oturum açma girişimi başarılamadı. Oturum açılıyor, [NUMBER]. girişim. - - - Dünya yükleniyor... - - - Katıştırılmış web tarayıcısı başlatılıyor... - - - Multimedya başlatılıyor... - - - Fontlar yükleniyor... - - - Önbellek dosyaları doğrulanıyor (60-90 saniye zaman alabilir)... - - - Yanıt işleniyor... - - - Dünya başlatılıyor... - - - Görüntülerin kodu çözülüyor... - - - QuickTime Başlatılıyor... - - - QuickTime bulunamadı - başlatılamadı. - - - QuickTime başarıyla başlatıldı. - - - Bölge özellikleri talep ediliyor... - - - Bölge özellikleri talep ediliyor: [NUMBER]. girişim... - - - Bölge el sıkışması bekleniyor... - - - Bölgeye bağlanılıyor... - - - Giysiler karşıdan yükleniyor... - - - Sunucu geçersiz veya bozuk bir sertifika döndürdü. Lütfen Ağ yöneticisine başvurun. - - - Sunucuya erişmek için geçersiz bir ana bilgisayar adı kullanıldı, lütfen SLURL veya Ağ ana bilgisayar adınızı kontrol edin. - - - Ağ tarafından döndürülen sertifikanın süresi sona ermiş görünüyor. Lütfen sistem saatinizi kontrol edin veya Ağ yöneticinize başvurun. - - - Sunucu tarafından döndürülen sertifika SSL için kullanılamadı. Lütfen Ağ yöneticinize başvurun. - - - Sunucunun Sertifika zincirinde çok fazla sertifika vardı. Lütfen Ağ yöneticinize başvurun. - - - Ağ sunucusu tarafından döndürülen sertifika imzası doğrulanamadı. Lütfen Ağ yöneticinize başvurun. - - - Ağ hatası: Bağlantı kurulamadı, lütfen ağ bağlantınızı kontrol edin. - - - Oturum açılamadı. - - - Çık - - - http://join.secondlife.com/index.php?sourceid=1206_steam&lang=tr-TR - - - Kullandığınız görüntüleyici ile artık Second Life'a erişemezsiniz. Yeni bir görüntüleyiciyi karşıdan yüklemek için lütfen şu sayfayı ziyaret edin: -http://secondlife.com/download - -Daha fazla bilgi edinmek için asağıdaki SSS sayfamızı ziyaret edin: -http://secondlife.com/viewer-access-faq - - - Opsiyonel görüntüleyici güncelleştirmesi mevcut: [VERSION] - - - Gerekli görüntüleyici güncelleştirmesi: [VERSION] - - - Bu aracı zaten oturum açmış durumda. - - - Üzgünüz! Oturumunuzu açamadık. -Lütfen şunları doğru girdiğinizi kontrol edin: - * Kullanıcı adı (mustafayalcin12 veya faruk.gungoren gibi) - * Parola: -Ayrıca lütfen Caps Lock tuşuna basmadığınıza emin olun. - - - Güvenlik önlemi olarak parolanız değiştirildi. -Lütfen hesap sayfanıza gidin: http://secondlife.com/password -ve parolanızı sıfırlamak için güvenlik sorusunu yanıtlayın. -Bu sorun için özür dileriz. - - - Sistemimizde bazı değişiklikler yaptık, parolanızı sıfırlamanız gerekecek. -Lütfen hesap sayfanıza gidin: http://secondlife.com/password -ve parolanızı sıfırlamak için güvenlik sorusunu yanıtlayın. -Bu sorun için özür dileriz. - - - Second Life bakım amacıyla geçici olarak kapatıldı. -Şu anda sadece çalışanlar oturum açabilir. -Güncelleştirmeler için www.secondlife.com/status adresini kontrol edin. - - - Second Life üzerindeki aktif kullanıcıların olası en iyi deneyimi yaşamasını sağlamak için, oturum açılması geçici olarak kısıtlanmıştır. - -Second Life için ödeme yapmış olan kişilere öncelik tanımak amacıyla, ücretsiz hesaplara sahip kişiler bu süre içerisinde Second Life'a erişemeyecekler. - - - Second Life'a bu bilgisayardan erişemezsiniz. -Bunun bir hata olduğunu düşünüyorsanız, lütfen şu adrese başvurun: -support@secondlife.com. - - - Hesabınıza şu zamana kadar erişemeyeceksiniz: -Pasifik Saati ile [TIME]. - - - Talebinizi şu anda tamamlayamıyoruz. -Lütfen yardım almak için Second Life destek bölümüne başvurun: http://secondlife.com/support -Eğer parolanızı değiştiremiyorsanız, lütfen şu numarayı arayın: (866) 476-9763. - - - Oturum açılması sırasında veri tutarsızlığı saptandı. -Lütfen şu adrese başvurun: support@secondlife.com - - - Hesabınızda küçük çaplı bir bakım işlemi sürüyor. -Hesabınıza şu zamana kadar erişemeyeceksiniz: -Pasifik Saati ile [TIME]. -Bunun bir hata olduğunu düşünüyorsanız, lütfen şu adrese başvurun: support@secondlife.com - - - Oturum kapatma talebi simülatörden bir hata yanıtı gelmesine neden oldu. - - - Sistem şu anda oturumunuzu sonlandırıyor. -Hesabınıza şu zamana kadar erişemeyeceksiniz: -Pasifik Saati ile [TIME]. - - - Geçerli bir oturum oluşturulamadı. - - - Bir simülatöre bağlanılamadı. - - - Hesabınız Second Life'a sadece -Pasifik Saati ile [START] ve [END] arasında erişebilir. -Lütfen bu saatler arasında tekrar uğrayın. -Bunun bir hata olduğunu düşünüyorsanız, lütfen şu adrese başvurun: support@secondlife.com - - - Yanlış parametreler. -Bunun bir hata olduğunu düşünüyorsanız, lütfen şu adrese başvurun: support@secondlife.com - - - Ad parametresi alfasayısal olmalıdır. -Bunun bir hata olduğunu düşünüyorsanız, lütfen şu adrese başvurun: support@secondlife.com - - - Soyadı parametresi alfasayısal olmalıdır. -Bunun bir hata olduğunu düşünüyorsanız, lütfen şu adrese başvurun: support@secondlife.com - - - Bölge şu anda çevrimdışı oluyor. -Lütfen bir dakika içerisinde tekrar oturum açmayı deneyin. - - - Aracı bölgede değil. -Lütfen bir dakika içerisinde tekrar oturum açmayı deneyin. - - - Bu bölge başka bir oturum açmaktaydı. -Lütfen bir dakika içerisinde tekrar oturum açmayı deneyin. - - - Bu bölge önceki oturumu sonlandırmaktaydı. -Lütfen bir dakika içerisinde tekrar oturum açmayı deneyin. - - - Bu bölge hala bir önceki oturumu sonlandırma işlemini sürdürüyor. -Lütfen bir dakika içerisinde tekrar oturum açmayı deneyin. - - - Bu bölge son oturumu sonlandırdı. -Lütfen bir dakika içerisinde tekrar oturum açmayı deneyin. - - - Bölge oturumu sonlandırma işlemini başlattı. -Lütfen bir dakika içerisinde tekrar oturum açmayı deneyin. - - - Sistem son oturumunuzu sonlandırma işlemini başlattı. -Lütfen bir dakika içerisinde tekrar oturum açmayı deneyin. - - - Bu bölgede sorun yaşanıyor olabilir. Lütfen İnternet bağlantınızı kontrol edin. - - - Ayarlarınız kaydediliyor... - - - Oturum kapatılıyor... - - - Kapatılıyor... - - - Bulunduğunuz bölgeyle bağlantınız kesildi. - - - Geçersiz bir bölgeye gönderildiniz. - - - Görüntüleyici bağlantısının kesilmesi test ediliyor - - - Kişi - - - (adsız) - - - Sahip: - - - Kamuya Açık - - - (Grup) - - - Satılık: L$[AMOUNT] - - - Grup İnşası - - - İnşa Edilemez - - - Grup İnşası - - - Güvenli Değil - - - Uçamaz - - - Grup Komut Dosyaları - - - Komut Dosyası Yok - - - Arazi: - - - Buraya sadece bir öğe sürüklenebilir. - - - - Satıcı giden kutunuzda öğeler oluşturamazsınız - - - Bu nesnelerden bir veya daha fazlası satılamaz veya aktarılamaz. - - - Satıcı giden kutunuza sadece doğrudan kendi envanterinizden öğeler koyabilirsiniz - - - Giymekte olduğunuz öğeleri Satıcı giden kutunuza koyamazsınız. - - - Satıcı giden kutunuza arama kartları koyamazsınız - - - İç içe geçmiş klasörlerin derinliği üçü geçiyor - - - Üst seviyedeki klasördeki alt klasör sayısı 20'yi geçiyor - - - Üst seviyedeki klasördeki öğe sayısı 200'ü geçiyor - - - Bir klasörü alt klasörüne taşıyamazsınız - - - Bir klasörü kendi içine taşıyamazsınız - - - Bu web sayfasını görmek için tıklayın - - - Bu konumun bilgisini görmek için tıklayın - - - Bu Sakinin profilini görmek için tıklayın - - - Bu Sakin hakkında daha fazla bilgi öğrenin - - - Bu Sakini engellemek için tıklayın - - - Bu Sakinin engellemesini kaldırmak için tıklayın - - - Bu Sakine Aİ göndermek için tıklayın - - - Bu Sakine ödeme yapmak için tıklayın - - - Bu Sakine bir ışınlama talebi teklif etmek için tıklayın - - - Bu Sakine bir arkadaşlık talebi teklif etmek için tıklayın - - - Bu grubun açıklamasını görmek için tıklayın - - - Bu etkinliğin açıklamasını görmek için tıklayın - - - Bu ilanı görmek için tıklayın - - - Bu parselin açıklamasını görmek için tıklayın - - - Bu konuma ışınlama yapmak için tıklayın - - - Bu nesnenin açıklamasını görmek için tıklayın - - - Bu konumu bir haritada görmek için tıklayın - - - secondlife:// komutunu çalıştırmak için tıklayın - - - - Şuraya ışınla: - - - Şunun için Harita göster: - - - Engelle - - - Engellemeyi kaldır - - - Aİ - - - Öde - - - Şuraya Işınlama Teklif Et: - - - Arkadaşlık Talebi - - - Kapat (⌘W) - - - Kapat (Ctrl+W) - - - Kapat - - - Geri Yükle - - - Simge Durumuna Küçült - - - Böl - - - Yerleştir - - - Yardımı Göster - - - Arıyor... - - - Hiçbiri bulunamadı. - - - Alınıyor... - - - Sürüm Notları - - - Yükleniyor... - - - (hiçbiri) - - - (bekliyor) - - - (birden çok) - - - (hiçbiri) - - - Avaline Arayanı [ORDER] - - - Hata yok - - - Varlık talebi: başarısız oldu - - - Varlık talebi: var olmayan dosya - - - Varlık talebi: veri tabanında varlık bulunamadı - - - Dosya sonu - - - Dosya açılamadı - - - Dosya bulunamadı - - - Dosya aktarımı zaman aşımı - - - Devre yok - - - Görüntüleyici ile sunucu fiyatta anlaşmadı - - - Bilinmeyen durum - - - doku - - - ses - - - arama kartı - - - yer imi - - - eski komut dosyası - - - giysi - - - nesne - - - not kartı - - - klasör - - - kök - - - LSL2 komut dosyası - - - LSL bayt kodu - - - tga dokusu - - - vücut bölümü - - - anlık görüntü - - - Kaybedip Bulduklarım - - - targa görüntüsü - - - Çöp - - - jpeg görüntüsü - - - animasyon - - - mimik - - - sim durumu - - - favori - - - bağlantı - - - klasör bağlantısı - - - örgü - - - (Görünümü Düzenliyor) - - - Uzakta - - - Meşgul - - - Engellenmiş - - - Korkmuş - - - Kızgın - - - Uzakta - - - Geriye salto - - - İçten Kahkaha - - - Büyük Gülümseme - - - Öpücük Atma - - - Canı Sıkılmış - - - Selamlama - - - Alkış - - - Reverans - - - Ağlama - - - Dans 1 - - - Dans 2 - - - Dans 3 - - - Dans 4 - - - Dans 5 - - - Dans 6 - - - Dans 7 - - - Dans 8 - - - Dudak Bükme - - - İçme - - - Utanmış - - - İşaret Etme - - - Yumruk Sallama - - - Uçan Yoga - - - Kaş Çatma - - - Sabırsız - - - Sevinçten Zıplama - - - Kıçımı Öp - - - Öpücük - - - Gülme - - - Muscle Beach - - - Hayır (Mutsuz) - - - Hayır - - - Ha-ha-ha - - - Peşpeşe İki Yumruk - - - Ağız Açık - - - Barış - - - Diğerini Göster - - - Kendini Göster - - - Sola Yumruk At - - - Sağa Yumruk At - - - RPS sayımı - - - RPS kağıdı - - - RPS kayası - - - RPS makası - - - Tiksinmiş - - - Döner Tekme - - - Üzgün - - - Selam - - - Bağırma - - - Omuz Silkme - - - Gülümseme - - - Duman Tüttürme - - - Duman Çekme - - - Yere İzmarit Atma - - - Sürpriz - - - Kılıç Darbesi - - - Öfke Nöbeti - - - Dil Çıkarma - - - El Sallama - - - Fısıldama - - - Islık Çalma - - - Göz Kırpma - - - Göz Kırpma (Hollywood) - - - Endişelenme - - - Evet (Mutlu) - - - Evet - - - Birden Çok - - - Yükleniyor... - - - Çevrimdışı - - - [AREA] m² L$[PRICE] - - - Hiçbiri bulunamadı. - - - Tamam - - - Dosyanın zamanından önce sonu - - - KÖK veya EKLEM bulunamıyor. - - - fısıldar: - - - bağırır: - - - SL dünyası içindeki Sesli Sohbete bağlanılıyor... - - - Bağlı - - - Geçerli konumunuzda ses mevcut değil - - - SL dünyası içindeki Sesli Sohbet ile bağlantı kesildi - - - Şimdi Yakındaki bir Sesli Sohbete yeniden bağlanılacaksınız. - - - '[OWNERNAME]' adlı kişiye ait, [REGIONPOS] üzerinde [REGIONNAME] içerisinde bulunan '[OBJECTNAME]' nesnesine şunu yapma izni verildi: [PERMISSIONS]. - - - '[OWNERNAME]' adlı kişiye ait, [REGIONPOS] üzerinde [REGIONNAME] içerisinde bulunan '[OBJECTNAME]' nesnesine şunu yapma izni verilmedi: [PERMISSIONS]. - - - Eğer hesabınıza erişime izin verirseniz, bu nesneye aynı zamanda şunun için izin vermiş olacaksınız: - - - Sizden Linden dolar (L$) almak - - - Denetim girişlerinizle ilgili eylem gerçekleştirmek - - - Denetim girişleriniz için yeniden eşleme yapmak - - - Avatarınızı canlandırmak - - - Avatarınıza eklemek - - - Mülkiyeti bırakmak ve kamuya açık hale gelmek - - - Başka nesnelerle bağlantı kurmak veya koparmak - - - Başka nesnelerle eklem eklemek ve kaldırmak - - - İzinlerini değiştirmek - - - Kameranızı takip etmek - - - Kameranızı kontrol etmek - - - Sizi ışınlama - - - Bağlı Değil - - - Genel - - - Orta - - - Yetişkin - - - Çevrimdışı - - - Bilinmiyor - - - (bilinmiyor) - - - Gayrimenkul / Tam Bölge - - - Gayrimenkul / Banliyö - - - Anakara / Banliyö - - - Anakara / Tam Bölge - - - Tüm Dosyalar - - - Sesler - - - Animasyonlar - - - Görüntüler - - - Kaydet - - - Yükle - - - Targa Görüntüleri - - - Bitmap Görüntüler - - - AVI Film Dosyası - - - XAF Animasyon Dosyası - - - XML Dosyası - - - Ham Dosya - - - Sıkıştırılmış Görüntüler - - - Dosyalar Yükle - - - Dizin Seç - - - Komut Dosyaları - - - Sözlükler - - - Uzakta Değil - - - Uzakta - - - Meşgul Değil - - - Meşgul - - - Şekil - - - Dış Katman - - - Saç - - - Gözler - - - Gömlek - - - Pantolon - - - Ayakkabılar - - - Çoraplar - - - Ceket - - - Eldivenler - - - Fanila - - - Külot - - - Etek - - - Alfa - - - Dövme - - - Fizik - - - geçersiz - - - hiçbiri - - - Giyilmemiş gömlek - - - Giyilmemiş pantolon - - - Giyilmemiş ayakkabılar - - - Giyilmemiş çoraplar - - - Giyilmemiş ceket - - - Giyilmemiş eldivenler - - - Giyilmemiş fanila - - - Giyilmemiş külot - - - Giyilmemiş etek - - - Giyilmemiş alfa - - - Giyilmemiş dövme - - - Giyilmemiş fizik - - - geçersiz - - - Yeni şekil oluştur - - - Yeni dış katman oluştur - - - Yeni saç oluştur - - - Yeni gözler oluştur - - - Yeni gömlek oluştur - - - Yeni pantolon oluştur - - - Yeni ayakkabılar oluştur - - - Yeni çoraplar oluştur - - - Yeni ceket oluştur - - - Yeni eldivenler oluştur - - - Yeni fanila oluştur - - - Yeni külot oluştur - - - Yeni etek oluştur - - - Yeni alfa oluştur - - - Yeni dövme oluştur - - - Yeni fizik oluştur - - - geçersiz - - - Yeni [WEARABLE_ITEM] - - - Sonraki - - - Tamam - - - Grup Bildirimi - - - Grup Bildirimleri - - - Gönderen: - - - Eklenmiş: - - - Burada eski bildirimleri görüntüleyin veya bu iletilerin alınmasını iptal edin. - - - Aksesuarı Aç - - - Aksesuarı Kaydet - - - Işınlama teklifi - - - Siz yokken yeni bildirimler geldi. - - - %d ilave bildiriminiz var - - - Sağ Kol - - - Baş - - - Sol Kol - - - Sol Bacak - - - Gövde - - - Sağ Bacak - - - Düşük - - - Orta - - - Yüksek - - - Dünya Görünümüne dönmek için ESC'e basın - - - Aradığınızı bulamadınız mı? [secondlife:///app/search/all/[SEARCH_TERM] Arama] ile bulmayı deneyin. - - - Aradığınızı bulamadınız mı? [secondlife:///app/search/places/[SEARCH_TERM] Arama] ile bulmayı deneyin. - - - Bir yer imini favorilerinize eklemek için buraya sürükleyin. - - - Envanterinizde bu dokunun kopyası yok - - - Pazaryerinda satın aldıklarınız burada görünecektir. Bunları kullanmak için envanterinize sürükleyebilirsiniz. - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/ - - - http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4 - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/imports - - - https://marketplace.[MARKETPLACE_DOMAIN_NAME]/learn_more - - - Pazaryerinde herkes öğe satabilir. - - - - Eğer bir satıcı olmak istiyorsanız, [Pazaryerinde [MARKETPLACE_CREATE_STORE_URL] bir mağaza açmanız gerekir]. - - - Giden kutunuz boş. - - - - Bu alana klasörleri sürükleyin ve bunları [[MARKETPLACE_DASHBOARD_URL] Pazaryerinde] satılık olarak duyurmak için "Pazaryerine Gönder" üzerine tıklayın. - - - Hata yok - - - Hata: Öğeleri Pazaryerine göndermeden önce kendinizi bir satıcı olarak belirlemelisiniz (ücretsizdir). - - - Hata: Bu klasörün içeriği boş. - - - Hata: Bu öğe karşıya yüklenemedi çünkü satıcı hesabınızda ürünlerle ilişkisiz çok fazla öğe mevcut. Bu hatayı düzeltmek için Pazaryeri web sitesine oturum açın ve ilişkisiz öğe sayınızı azaltın. - - - Hata: Bu öğe çok fazla nesne içeriyor. Bu hatayı düzeltmek için nesneleri birlikte kutulara yerleştirerek, toplam nesne sayısını 200'ün altına düşürün. - - - Hata: Bu öğede çok fazla iç içe geçmiş klasör seviyesi var. Bunu tekrar düzenleyerek maksimum 3 iç içe geçmiş klasör seviyesine indirin. - - - Hata: Bu öğe Pazaryerinde satılamaz. - - - Hata: Bu öğede bir sorun var. Daha sonra tekrar deneyin. - - - Açık yer imleri - - - - - - - - - İçerik yükleniyor... - - - İçerik yok - - - - - Evet - - - Hayır - - - - - - - - - - - - - - - - - - - - - - - - - - Envanterim - - - Kütüphane - - - Dokular - - - Sesler - - - Arama Kartları - - - Yer İmleri - - - Komut Dosyaları - - - Giysiler - - - Nesneler - - - Not Kartları - - - Yeni Klasör - - - Envanter - - - Sıkıştırılmamış Görüntüler - - - Vücut Bölümleri - - - Çöp - - - Fotoğraf Albümü - - - Kaybedip Bulduklarım - - - Sıkıştırılmamış Sesler - - - Animasyonlar - - - Mimikler - - - Favorilerim - - - Favorilerim - - - Mevcut Dış Görünüm - - - Başlangıçtakı Dış Görünümler - - - Benim Dış Görünümlerim - - - Aksesuarlar - - - Örgüler - - - Arkadaşlar - - - Tümü - - - Giyilen aksesuar yok - - - Aksesuarlar ([COUNT] yuva mevcut) - - - Satın Al - - - L$'a Satın Al - - - Taş - - - Metal - - - Cam - - - Ahşap - - - Et - - - Plastik - - - Lastik - - - Işık - - - Shift - - - Ctrl - - - Göğüs - - - Kafatası - - - Sol Omuz - - - Sağ Omuz - - - Sol El - - - Sağ El - - - Sol Ayak - - - Sağ Ayak - - - Omurga - - - Leğen Kemiği - - - Ağız - - - Çene - - - Sol Kulak - - - Sağ Kulak - - - Sol Göz Küresi - - - Sağ Göz Küresi - - - Burun - - - Sağ Üst Kol - - - Sağ Ön Kol - - - Sol Üst Kol - - - Sol Ön Kol - - - Sağ Kalça - - - Sağ Üst Bacak - - - Sağ Alt Bacak - - - Sol Kalça - - - Sol Üst Bacak - - - Sol Alt Bacak - - - Karın - - - Sol Göğüs - - - Sağ Göğüs - - - Boyun - - - Avatar Merkezi - - - Geçersiz Aksesuar Noktası - - - [AGEYEARS] [AGEMONTHS]'lık - - - [AGEYEARS] yaşında - - - [AGEMONTHS]'lık - - - [AGEWEEKS]'lık - - - [AGEDAYS]'lük - - - Bugün katıldı - - - [COUNT] yıl - - - [COUNT] yıl - - - [COUNT] yıl - - - [COUNT] ay - - - [COUNT] ay - - - [COUNT] ay - - - [COUNT] hafta - - - [COUNT] hafta - - - [COUNT] hafta - - - [COUNT] gün - - - [COUNT] gün - - - [COUNT] gün - - - [COUNT] üye - - - [COUNT] üye - - - [COUNT] üye - - - Sakin - - - Deneme - - - Ayrıcalıklı Üye - - - Linden Lab Çalışanı - - - Kullanılan Ödeme Bilgisi - - - Dosyadaki Ödeme Bilgisi - - - Dosyada Ödeme Bilgisi Yok - - - Yaşı Doğrulanmış - - - Yaşı Doğrulanmamış - - - 2. Merkez - - - Sağ Üst - - - Üst - - - Sol Üst - - - Merkez - - - Sol Alt - - - Alt - - - Sağ Alt - - - Karşıdan yüklendi, şimdi derleniyor - - - Komut dosyası sunucuda bulunamadı. - - - Karşıdan yüklenirken sorun oluştu - - - Komut dosyasını karşıdan yüklemek için yeterli izin yok. - - - Şunun için yeterli izin yok: - - - Karşıdan yüklerken bilinmeyen hata - - - Tekrar Derleme İlerlemesi - - - tekrar derle - - - Sıfırlama İlerlemesi - - - sıfırla - - - Çalışan Süreçlerin İlerlemesini Ayarla - - - çalıştırmayı ayarla - - - Çalışmayan Süreçlerin İlerlemesini Ayarla - - - çalıştırmamayı ayarla - - - Derleme başarılı oldu! - - - Derleme başarılı, kaydediliyor... - - - Kaydetme tamamlandı. - - - Komut dosyası (nesne kapsam dışı) - - - [OWNER] mülkiyetindeki [OBJECT] nesnesi - - - hiçbiri - - - - (Bilinmiyor) - - - - - [mthnum,datetime,utc]/[day,datetime,utc]/[year,datetime,utc] - - - - - Bakiye - - - Katkıda Bulunanlar - - - Borçlar - - - Toplam - - - Bu grup için grup verisi bulunamadı - - - ana gayrimenkul - - - anakara - - - on sekiz yaş altı - - - herkes - - - hata - - - [OWNER] mülkiyetindeki tüm gayrimenkuller - - - sahip olduğunuz tüm gayrimenkuller - - - [OWNER] adına yönettiğiniz tüm gayrimenkuller - - - İzin verilen Sakinler: ([ALLOWEDAGENTS], maks [MAXACCESS]) - - - İzin verilen gruplar: ([ALLOWEDGROUPS], maks [MAXACCESS]) - - - Parsel Komut Dosyası Belleği - - - Listelenen Parseller: [PARCELS] - - - Kullanılan bellek: [COUNT] kb / [MAX] kb içerisinden; [AVAILABLE] kb serbest - - - Kullanılan bellek: [COUNT] kb - - - Parsel Komut Dosyası URL'leri - - - Kullanılan URL'ler: [COUNT] / [MAX] içerisinden; [AVAILABLE] serbest - - - Kullanılan URL'ler: [COUNT] - - - Bilgi talep edilirken hata oluştu - - - Seçili Parsel Yok - - - Hata: Komut dosyası bilgisi sadece mevcut bölgenizde geçerli - - - Bilgiler alınıyor... - - - Bu parseli inceleme izniniz yok - - - Üzerinde Oturuyor - - - Göğüs - - - Baş - - - Sol Omuz - - - Sağ Omuz - - - Sol El - - - Sağ El - - - Sol Ayak - - - Sağ Ayak - - - Geri - - - Leğen Kemiği - - - Ağız - - - Çene - - - Sol Kulak - - - Sağ Kulak - - - Sol Göz - - - Sağ Göz - - - Burun - - - Sol Üst Kol - - - Sağ Alt Kol - - - Sol Üst Kol - - - Sol Alt Kol - - - Sağ Kalça - - - Sağ Üst Bacak - - - Sağ Alt Bacak - - - Sol Kalça - - - Sol Üst Bacak - - - Sol Alt Bacak - - - Göbek - - - Sağ Göğüs - - - Sol Göğüs - - - BÜG 2. Merkez - - - BÜG Sağ Üst - - - BÜG Merkez Üst - - - BÜG Sol Üst - - - BÜG 1. Merkez - - - BÜG Sol Alt - - - BÜG Alt - - - BÜG Sağ Alt - - - Satır [LINE], Sütun [COLUMN] - - - [COUNT] bulundu - - - [hour,datetime,slt]:[min,datetime,slt] - - - [mthnum,datetime,slt]/[day,datetime,slt] - - - Nesnenin içeriği - - - Yeni Komut Dosyası - - - İleti gönderdiğiniz Sakin 'meşgul modu'nda, bu da rahatsız edilmek istemediği anlamına geliyor. İletiniz daha sonra incelenmesi için kendisine ait Aİ panelinde gösterilecektir. - - - (Adına göre) - - - (Sakin) - - - (Nesne) - - - (Grup) - - - (Harici) - - - Bu Gayrimenkul için Sözleşmesi yok. - - - Bu Gayrimenkul için Sözleşmesi yok. Bu gayrimenkul üzerindeki arazi Linden Lab. değil, Gayrimenkul sahibi tarafından satılmaktadır. Satış ayrıntılarını öğrenmek için lütfen Gayrimenkul Sahibiyle bağlantıya geçin. - - - - - - Sahibi Olunan Grup - - - Kamuya Açık - - - Yerel Ayarlar - - - Bölge Ayarları - - - Tıklamalar: [TELEPORT] ışınlama, [MAP] harita, [PROFILE] profil - - - (yayınlandıktan sonra güncelleştirilir) - - - Herhangi bir Seçme veya İlan oluşturmadınız. Bir Seçme veya İlan oluşturmak için aşağıdaki Artı düğmesine tıklayın. - - - Kullanıcının herhangi bir seçmesi veya ilanı yok - - - Yükleniyor... - - - Önizleme - - - Özellikler - - - Şu ada sahip bir nesne: - - - grubun sahip olduğu: - - - bilinmeyen grubun sahip olduğu: - - - sahibi: - - - bilinmeyen bir kullanıcının sahip olduğu: - - - size verdi: - - - <nolink>[NAME]</nolink> tarafından gönderilen [DESC]'i reddettiniz. - - - Toplam - - - alınan: - - - size ödenen: - - - şuraya ödenen: - - - şuraya geçiş hakkı alınan: - - - etkinlik için ödenen ücret: - - - etkinlik için verilen ödül: - - - Bakiye - - - Katkıda Bulunanlar - - - Borçlar - - - [weekday,datetime,utc] [mth,datetime,utc] [day,datetime,utc], [year,datetime,utc] - - - İçerik - - - Alınan Öğeler - - - İptal - - - [NAME] için karşıya yükleme maliyeti: L$ [AMOUNT] - - - Bunu satın almanın maliyeti: L$ [AMOUNT] - - - Bilinmeyen dosya uzantısı .%s -.wav, .tga, .bmp, .jpg, .jpeg veya .bvh bekleniyordu - - - Engelle - - - Engelle - - - Engellemeyi Kaldır - - - Engellemeyi Kaldır - - - Yer İmlerime Ekle... - - - Yer İmimi Düzenle... - - - ⌃ - - - ⌘ - - - ⌥ - - - ⇧ - - - Ctrl+ - - - Alt+ - - - Shift+ - - - Dosya Kaydedildi - - - Alınıyor - - - ÖÖ - - - ÖS - - - PST - - - PDT - - - İleri - - - Sol - - - Sağ - - - Geri - - - Kuzey - - - Güney - - - Batı - - - Doğu - - - Yukarı - - - Aşağı - - - Herh. Bir Kategori - - - Alışveriş - - - Arazi Kiralama - - - Mülk Kiralama - - - Özel Atraksiyon - - - Yeni Ürünler - - - İstihdam - - - Arananlar - - - Hizmet - - - Kişisel - - - Renksiz - - - Linden Konumu - - - Yetişkin - - - Sanat ve Kültür - - - İş - - - Eğitim - - - Oyun - - - Uğrak Mekan - - - Yeni Gelenlere Yardım Sunan - - - Park ve Doğa - - - Yerleşim - - - Sahne - - - Diğer - - - Kiralık - - - Herhangi - - - Siz - - - : - - - , - - - ... - - - *** - - - ( - - - ) - - - . - - - ' - - - --- - - - Birden Çok Ortam - - - Ortamı Oynat/Durdur - - - Komut satırı ayrıştırılırken bir hata oluştu. -Lütfen bakınız: http://wiki.secondlife.com/wiki/Client_parameters -Hata: - - - [APP_NAME] Komut satırı kullanımı: - - - [APP_NAME] ihtiyaç duyduğu bir dosyaya erişemiyor. - -Bunun nedeni bir şekilde birden fazla kopyanın çalışıyor olması veya sisteminizin, bir dosyanın açık olduğunu sanması olabilir. -Bu iletiyi görmeye devam ederseniz, bilgisayarınızı yeniden başlatın ve tekrar deneyin. -Sorun devam ederse [APP_NAME] uygulamasını tümüyle kaldırmanız ve tekrar yüklemeniz gerekebilir. - - - Önemli Hata - - - [APP_NAME], AltiVec (G4 veya üzeri) bir işlemciye ihtiyaç duyuyor. - - - [APP_NAME] zaten çalışıyor. -Programın simge durumuna küçültülmüş bir kopyası için görev çubuğunuza bakın. -Bu iletiyi görmeye devam ederseniz, bilgisayarınızı tekrar başlatın. - - - [APP_NAME] uygulaması bir önceki çalıştırmada kilitlenmiş görünüyor. -Bir kilitlenme raporu göndermek ister misiniz? - - - Bildirim - - - [APP_NAME], DirectX 9.0b veya üzerini saptayamıyor. -[APP_NAME], kararlılık problemleri, kötü performans ve çökmelere neden olabilecek donanım ve/veya süresi geçmiş sürücüleri saptamak için DirectX kullanır. [APP_NAME] uygulamasını bu olmadan da çalıştırmanız mümkündür, ancak DirectX 9.0b ile çalıştırmanızı kuvvetle tavsiye ederiz. - -Devam etmek istiyor musunuz? - - - Uyarı - - - Linux için henüz otomatik güncelleştirme uygulanmamıştır. -Lütfen www.secondlife.com adresinden son sürümü karşıdan yükleyin. - - - RegisterClass başarısız oldu - - - Hata - - - [WIDTH] x [HEIGHT] çözünürlüğünde tam ekran çalıştırma yapılamıyor. -Pencerede çalışıyor. - - - Pencere yok edilirken Kapatma Hatası oluştu (DestroyWindow() başarısız oldu) - - - Kapatma Hatası - - - GL cihazı içeriği oluşturulamıyor - - - Uygun piksel formatı bulunamadı - - - Piksel formatı açıklaması alınamıyor - - - [APP_NAME] uygulamasının çalışması için Gerçek Renk (32 bit) gerekiyor. -Lütfen bilgisayarınızın ekran ayarlarına gidin ve renk modunu 32 bit olarak ayarlayın. - - - [APP_NAME] çalışamıyor çünkü 8 bit bir alfa kanalı alamıyor. Bunun nedeni genellikle video kartı sürücü sorunlarıdır. -Lütfen en yeni video sürücülerinin yüklü olduğuna emin olun. -Ayrıca Denetim Masaları > Ekran > Ayarlar içerisinde ekranınız için Gerçek Renk (32 bit) ayarı yapıldığına emin olun. -Bu iletiyi almaya devam ederseniz, lütfen [SUPPORT_SITE] bölümüne başvurun. - - - Piksel formatı ayarlanamıyor - - - GL işlemi bağlamı oluşturulamıyor - - - GL işlemi bağlamı etkinleştirilemiyor - - - [APP_NAME] çalışamıyor çünkü video kartı sürücüleriniz düzgün yüklenemedi, süresi geçmiş durumda veya desteklenmeyen donanımlar için geliştirilmiş. Lütfen en yeni video kartı sürücülerine sahip olduğunuza emin olun; en yeni sürücüler mevcutsa da bunları tekrar yüklemeyi deneyin. - -Bu iletiyi almaya devam ederseniz, lütfen [SUPPORT_SITE] bölümüne başvurun. - - - Bir Günlük Sakal - - - Tümü Beyaz - - - Anime Gözler - - - Yay Şeklinde - - - Kol Uzunluğu - - - Bitişik - - - Kafaya Bitişik Kulak Memeleri - - - Arka Perçem - - - Torbalı - - - Kahküller - - - Boncuk Gözler - - - Göbek Büyüklüğü - - - Büyük - - - Büyük Kıç - - - Kabarık Saç: Arkada - - - Kabarık Saç: Önde - - - Kabarık Saç: Tepede - - - Büyük Kafa - - - Büyük Göğüs Kasları - - - Büyük Dik Kısımlar - - - Siyah - - - Sarışın - - - Sarı Saç - - - Allık - - - Allık Rengi - - - Allık Geçirgenliği - - - Vücut Tanımı - - - Vücut Yağı - - - Vücut Çilleri - - - Kalın Vücut - - - Vücut Kalınlığı - - - İnce Vücut - - - Çarpık Bacaklı - - - Göğüs Kalkıklığı - - - Göğüs Çatalı - - - Göğüs Büyüklüğü - - - Burun Kemiği Genişliği - - - Geniş - - - Alın Genişliği - - - Patlak Gözlü - - - Patlak Gözlü - - - Patates Burunlu - - - Patates Burunlu - - - Göğüs Kütlesi - - - Göğüs Düzleştirme - - - Göğüs Yerçekimi - - - Göğüs Direnci - - - Maks Etki - - - Yaylanma - - - Kazanç - - - Sönüm - - - Maks Etki - - - Yaylanma - - - Kazanç - - - Sönüm - - - Maks Etki - - - Yaylanma - - - Kazanç - - - Sönüm - - - Göbek Kütlesi - - - Göbek Düzleştirme - - - Göbek Yerçekimi - - - Göbek Direnci - - - Maks Etki - - - Yaylanma - - - Kazanç - - - Sönüm - - - Kıç Kütlesi - - - Kıç Düzleştirme - - - Kıç Yerçekimi - - - Kıç Direnci - - - Maks Etki - - - Yaylanma - - - Kazanç - - - Sönüm - - - Maks Etki - - - Yaylanma - - - Kazanç - - - Sönüm - - - Gür Kaşlar - - - Gür Saç - - - Kıç Büyüklüğü - - - Kıç Yerçekimi - - - Tarlatanlı Etek - - - Tarlatansız - - - Çok Tarlatanlı - - - Chaplin pantalon - - - Elmacık Kemikleri - - - Göğüs Büyüklüğü - - - Çene Açısı - - - Çene Çukuru - - - Lincoln Sakal - - - Çene Derinliği - - - Geniş Çene - - - Çene İçeri - - - Çene Dışarı - - - Çene-Boyun - - - Temizle - - - Çukur - - - Yakın Gözler - - - Kapalı - - - Arkası Kapalı - - - Önü Kapalı - - - Solu Kapalı - - - Sağı Kapalı - - - Para Cüzdanı - - - Yaka Arkası - - - Yaka Önü - - - Köşesi Aşağıda - - - Köşesi Yukarıda - - - Kırışık - - - Yamuk Burun - - - Geniş Paça - - - Karanlık - - - Koyu Yeşil - - - Daha Koyu - - - Derin - - - Varsayılan Topuklar - - - Yoğun - - - Çift Çene - - - Aşağı Dönük - - - Spor Çanta - - - Kulak Açısı - - - Kulak Büyüklüğü - - - Kulak Uçları - - - Yumurta Kafa - - - Göz Altı Torbaları - - - Göz Rengi - - - Gözün İçeri Çöküklüğü - - - Göz Parlaklığı - - - Göz Açıklığı - - - Gözlerin Dışarı Çıkıklığı - - - Göz Büyüklüğü - - - Gözlerin Aralığı - - - Kaş Kavisi - - - Kaş Kalınlığı - - - Kaş Yüksekliği - - - Kaş Yapısı - - - Kaş Büyüklüğü - - - Kirpik Uzunluğu - - - Göz Kalemi - - - Göz Kalemi Rengi - - - Patlak Gözler - - - Dikey Yüz Kaydırma - - - Yüz İfadesi - - - Ayrık Gözler - - - Kalın Dudaklar - - - Kadın - - - Parmaksız - - - Parmaklar - - - Geniş Paçalar - - - Düz - - - Düz Kıç - - - Düz Kafa - - - Düz Ayak Ucu - - - Ayak Büyüklüğü - - - Alın Açısı - - - Belirgin Alın - - - Çiller - - - Ön Perçem - - - Arkası Düz - - - Çift Taraflı Göz Kalemi - - - Önü Düz - - - Saçlar Yanda - - - Saçlar Yanda - - - Parlak - - - Eldiven Parmakları - - - Eldiven Uzunluğu - - - Saç - - - Saç: Arka - - - Saç: Ön - - - Saç: Yanlar - - - Saçı Yana Tarama - - - Saç Kalınlığı - - - Saç Kalınlığı - - - Saç Eğimi - - - Sola Eğimli Saç - - - Sağa Eğimli Saç - - - Saç: Hacim - - - El Büyüklüğü - - - Gidon tipi - - - Kafa Uzunluğu - - - Kafa Şekli - - - Kafa Büyüklüğü - - - Kafayı Uzatma - - - Topuk Yüksekliği - - - Topuk Şekli - - - Yükseklik - - - Yüksek - - - Yüksek Topuklar - - - Ayrık - - - Yüksek Topuklu - - - Düşük - - - Daha Yüksek - - - Kalça Uzunluğu - - - Kalça Genişliği - - - İçeri - - - İç Gölge Rengi - - - İç Gölge Opaklığı - - - Gözün İç Köşesi - - - İç Göz Gölgesi - - - İç Gölge - - - Ceket Uzunluğu - - - Ceket Kırışıklıkları - - - Çenenin Boyuna Uzaklığı - - - Alt Çene Uzunluğu - - - Çene Kemiği Genişliği - - - Birleşik - - - Avurtlar - - - Diz Açısı - - - Çarpık Bacaklı - - - Büyük - - - Büyük Eller - - - Sola Ayırma - - - Bacak Uzunluğu - - - Bacak Kasları - - - Daha Az - - - Daha Az Vücut Yağı - - - Daha Az Lincoln Sakal - - - Daha Az Çil - - - Daha Az Dolgun - - - Dik Göğüs - - - Daha İnce Bel - - - Daha Az Kas - - - Daha Az Kaslı - - - Daha Az Pembe - - - Daha Az Yuvarlak - - - Basensiz - - - Daha Az Küt - - - Daha Az Hacim - - - Daha az ruh - - - Daha Hafif - - - Dudak Çukuru - - - Dudak Çukuru Derinliği - - - Dudak Dolgunluğu - - - Dudak Pembeliği - - - Dudak Oranı - - - Dudak Kalınlığı - - - Dudak Genişliği - - - Dudak Parlatıcısı - - - Ruj - - - Ruj Rengi - - - Uzun - - - Uzun Kafa - - - Yüksek kalça - - - Uzun Bacaklar - - - Uzun Boyun - - - Yandan Uzun Kuyruk - - - Uzun Atkuyruğu - - - Uzun Gövde - - - Uzun kollar - - - Bol Pantolon - - - Bol Gömlek - - - Bol Kollu - - - Bel Kalınlığı - - - Düşük - - - Alçak Topuklar - - - Bitişik - - - Alçak Topuklu - - - Yüksek - - - Daha Alçak - - - Alt Burun Kemiği - - - Daha Alçak Yanaklar - - - Erkek - - - Ortadan Ayırma - - - Daha Fazla - - - Daha Fazla Allık - - - Daha Fazla Vücut Yağı - - - Daha Çok Lincoln Sakal - - - Daha Fazla Göz Farı - - - Daha Çok Çil - - - Daha Dolgun - - - Sarkık Göğüs - - - Daha Çok Ruj - - - Daha Kalın Bel - - - Daha Dolgun Alt Dudak - - - Daha Çok Kas - - - Daha Kaslı - - - Daha Pembe - - - Daha Yuvarlak - - - Basenli - - - Daha Eğimli - - - Daha Küt - - - Daha Dolgun Üst Dudak - - - Daha Dikey - - - Daha Hacimli - - - Daha çok ruh - - - Bıyık - - - Ağız Köşesi - - - Ağzın Konumu - - - Mowhawk Saçı - - - Kaslı - - - Geniş Favori - - - Tırnak Cilası - - - Tırnak Cilası Rengi - - - Dar - - - Arkası Dar - - - Önü Dar - - - Küçük Dudaklar - - - Doğal - - - Boyun Uzunluğu - - - Boyun Kalınlığı - - - Allık Yok - - - Göz Kalemi Yok - - - Göz Farı Yok - - - Dudak Parlatıcısı Yok - - - Ruj Yok - - - Ayırma Yok - - - Cila Yok - - - Kırmızı Yok - - - Dikleştirme Yok - - - Beyaz Yok - - - Kırışıklık Yok - - - Normal Alt - - - Normal Üst - - - Sola Eğimli Burun - - - Sağa Eğimli Burun - - - Burun Büyüklüğü - - - Burun Kalınlığı - - - Burun Ucu Açısı - - - Burun Ucu Şekli - - - Burun Genişliği - - - Burun Deliği Ayrımı - - - Burun Deliği Genişliği - - - Opak - - - Aç - - - Arkayı Aç - - - Önü Aç - - - Solu Aç - - - Sağı Aç - - - Turuncu - - - Dışarı - - - Dış Gölge Rengi - - - Dış Gölge Opaklığı - - - Gözün Dış Köşesi - - - Dış Göz Gölgesi - - - Dış Gölge - - - Öne Doğru - - - Apış Arası Şişkinliği - - - Ojeli Tırnaklar - - - Soluk - - - Pantolon Ağı - - - Pantolon Oturması - - - Pantolon Uzunluğu - - - Pantolon Bel Ölçüsü - - - Pantolon Kırışıklıkları - - - Ayırma - - - Ayrılmış Kahküller - - - Göğüs Kasları - - - Pigment - - - Yan Kuyruklar - - - Pembe - - - Daha Pembe - - - Topuk Yüksekliği - - - Topuk Genişliği - - - Dar - - - Sivri Topuklar - - - Atkuyruğu - - - Kabarık Etek - - - Sol Gözü Dışarı Çıkar - - - Sağ Gözü Dışarı Çıkar - - - Şişkin - - - Şişkin Göz Kapakları - - - Gökkuşağı Rengi - - - Kırmızı Saç - - - Normal - - - Sağa Ayırma - - - Kırmızı Yanaklı - - - Yuvarlak - - - Kırmızılık - - - Kırmızı - - - Dağınık Saç - - - Basen Genişliği - - - Sıska Bacak - - - Ayrık - - - Sığ - - - Arka Dolgunluğu - - - Yüzü Dikey Kaydır - - - Ön Dolgunluğu - - - Solu Yukarı Kaydır - - - Sağı Yukarı Kaydır - - - Arkası Dolgun - - - Önü Dolgun - - - Sola Kaydır - - - Ağzı Kaydırma - - - Sağa Kaydır - - - Gömlek Eteği - - - Gömlek Boyu - - - Gömlek Kırışıklıkları - - - Ayakkabı Yüksekliği - - - Kısa - - - Kısa Kollar - - - Kısa Bacaklar - - - Kısa Boyun - - - Yandan Kısa Kuyruk - - - Kısa Atkuyruğu - - - Kısa Favoriler - - - Kısa Gövde - - - Düşük kalça - - - Omuzlar - - - Yan Perçem - - - Favoriler - - - Yan Saçlar - - - Aşağı Doğru Yan Saç - - - Yukarı Doğru Yan Saç - - - İnce Boyun - - - Etek Boyu - - - Etek Uzunluğu - - - Eğimli Alın - - - Kol Uzunluğu - - - Kol Bolluğu - - - Yırtmaç: Geri - - - Yırtmaç: Ön - - - Yırtmaç: Sol - - - Yırtmaç: Sağ - - - Küçük - - - Küçük Eller - - - Küçük Kafa - - - Düz - - - Düz Saç - - - Çorap Uzunluğu - - - Dudak Altı Sakal - - - Seyrek - - - Dikleştirilmiş Saç - - - Geniş - - - Küt Burunlu - - - Kafayı Bastır - - - Kafayı Uzat - - - Çökük - - - Çökük Göğüs - - - Çökük Gözler - - - Arkaya Tarama - - - Öne Tarama - - - Uzun - - - Arkası Kısa - - - Önü Kısa - - - Kalın Topuklar - - - Kalın Boyun - - - Kalın Ayak Ucu - - - İnce - - - İnce Kaşlar - - - İnce Dudaklar - - - İnce Burun - - - Çift Çene - - - Dar Paçalar - - - Dar Pantolon - - - Dar Gömlek - - - Dar Etek - - - Dar Kollu - - - Ayakkabu Burnu Şekli - - - Ayakkabu Burnu Kalınlığı - - - Gövde Uzunluğu - - - Gövde Kasları - - - Sıska Gövde - - - Ayrık - - - Buruşuk olmayan - - - Geriye Doğru - - - Doğal Olmayan - - - Üst Burun Kemiği - - - Üst Yanaklar - - - Üst Çene Çukuru - - - Üst Göz Kapağı Kıvrımı - - - Yukarı dönük - - - Çok Kırmızı - - - Bel Yüksekliği - - - Dolgun - - - Beyaz Saç - - - Geniş - - - Geniş Arka - - - Geniş Ön - - - Geniş Dudaklar - - - Çılgın - - - Kırışıklıklar - - - Yer İmlerime Ekle - - - Yer İmimi Düzenle - - - Geçerli konum hakkında daha fazla bilgi gör - - - Konum geçmişim - - - Bu araziyi satın al - - - Ses burada kullanılamaz - - - Uçmaya izin verilmiyor - - - İtme yok - - - Nesne inşa etmeye/düşürmeye izin verilmiyor - - - Komut dosyalarına izin verilmiyor - - - Sağlık - - - Yetişkin Bölgesi - - - Orta Bölge - - - Genel Bölge - - - Bu parselin dışında avatarlar görünür durumda ve sohbete izin veriliyor - - - Bölge yeniden kaydedilinceye kadar hareket eden nesneler bu bölgede doğru davranmayabilir. - - - Bu bölgede dinamik yol bulma etkin değil. - - - [APP_NAME] Güncelleştirme - - - [APP_NAME] güncelleştiriliyor... - - - [APP_NAME] yükleniyor... - - - [APP_NAME] Görüntüleyiciniz en son sürüme güncelleştiriliyor. Bu biraz zaman alabilir, bu nedenle sabırlı olun. - - - Güncelleştirme karşıdan yükleniyor... - - - Güncelleştirme karşıdan yükleniyor - - - Güncelleştirmenin karşıdan yüklenmesi başarılamadı - - - [APP_NAME] güncellenirken bir hata oluştu. Lütfen www.secondlife.com adresinden son sürümü karşıdan yükleyin. - - - Güncelleştirmenin yüklenmesi başarılamadı - - - Görüntüleyici başlatılamadı - - - [APP_NAME]: [FROM_NAME]'den öğeler çok hızlı geliyor, [TIME] saniye boyunca otomatik ön izleme devre dışı bırakıldı - - - [APP_NAME]: Öğeler çok hızlı geliyor, [TIME] saniye boyunca otomatik ön izleme devre dışı bırakıldı - - - -- Anlık ileti günlük tutulması etkin -- - - - [NAME] yazıyor... - - - (İsimsiz) - - - (Yönetilen: Varsayılan durumda sesler kapalı) - - - Bu aramada metin sohbeti kullanılamaz. - - - Bir Grup Moderatörü tarafından metin sohbetiniz devre dışı bırakıldı. - - - Anlık ileti göndermek için buraya tıklayın. - - - Kime - - - (Moderatör) - - - (Kaydedildi [LONG_TIMESTAMP]) - - - Bu mesajı görmek için Tercihler/Gizlilik'de 'Sadece arkadaşlar ve gruplar beni arasın veya Aİ göndersin' seçeneğinin işaretini kaldırmalısınız. - - - Aramanız yanıtlandı - - - Bir sesli arama başlattınız - - - Sesli aramaya katıldınız - - - [NAME] bir sesli arama başlattı - - - Sesli aramaya katılınılıyor... - - - Bağlandı, kapatmak için Aramadan Çık üzerine tıklayın - - - Sesli aramadan çıkıldı - - - Bağlanıyor... - - - Özel Konferans - - - [AGENT_NAME] ile konferans - - - Teklif edilen envanter öğesi: - - - Envanterinizden buraya öğeler sürükleyin - - - (Aİ Oturumu Mevcut Değil) - - - Bu oturumdaki tek kullanıcısınız. - - - [NAME] çevrim dışı. - - - Bu sesli sohbeti kabul etmek/bağlanmak için [BUTTON NAME] düğmesine tıklayın. - - - Bu Sakini engellediniz. Bir ileti gönderdiğinizde engelleme otomatik olarak kaldırılır. - - - Talep gönderilirken hata oluştu, lütfen daha sonra tekrar deneyin. - - - Talep gönderilirken hata oluştu, lütfen daha sonra tekrar deneyin. - - - Yeterli izne sahip değilsiniz. - - - Bu oturum artık mevcut değil. - - - Bu yeteneğe sahip değilsiniz. - - - Bu yeteneğe sahip değilsiniz. - - - Bir oturum moderatörü değilsiniz. - - - Bir grup moderatörü metin sohbetinizi devre dışı bıraktı. - - - Bir grup moderatörü metin sohbetinizi devre dışı bıraktı. - - - [RECIPIENT] ile sohbet oturumuna kullanıcı eklenemiyor. - - - İletiniz [RECIPIENT] ile sohbet oturumuna gönderilemedi. - - - İletiniz [RECIPIENT] ile sohbet oturumuna gönderilemedi. - - - Yönetme sırasında hata oluştu. - - - Gruptan çıkarıldınız. - - - Gruptan çıkarıldınız. - - - Sohbet oturumunda bulunma yeteneğine artık sahip değilsiniz. - - - [SOURCES] yeni bir şey söyledi - - - [SOURCES] yeni bir şey söyledi - - - Oturum başlatılması zaman aşımına uğradı. - - - Ana konum ayarlandı. - - - http://secondlife.com/landing/voicemorphing - - - [NAME] size [REASON] L$[AMOUNT] ödedi. - - - [NAME] size L$[AMOUNT] ödedi. - - - [NAME]'e [REASON] L$[AMOUNT] ödediniz. - - - L$[AMOUNT] ödediniz. - - - [NAME]'e L$[AMOUNT] ödediniz. - - - [REASON] L$[AMOUNT] ödediniz. - - - [REASON] [NAME]'e L$[AMOUNT] ödeyemediniz. - - - L$[AMOUNT] ödeyemediniz. - - - [NAME]'e L$[AMOUNT] ödeyemediniz. - - - [REASON] L$[AMOUNT] ödeyemediniz. - - - [ITEM] için. - - - bir arazi parseli için. - - - bir arazi erişim hakkı için - - - arazi devretmek için - - - bir grup oluşturmak için - - - bir grupa katılmak için - - - karşıya yüklemek için - - - bir ilan yayınlamak için - - - L$ [AMOUNT] veriliyor - - - Karşıya yüklemenin maliyeti: L$ [AMOUNT] - - - Bunun maliyeti: L$ [AMOUNT] - - - L$ [AMOUNT] karşılığında seçilen arazi satın alınıyor - - - Bu nesnenin maliyeti: L$ [AMOUNT] - - - Herkes - - - Yetkililer - - - Sahipler - - - Çevrimiçi - - - Kötüye Kullanım Bildirimi Karşıya Yükleniyor... - - - Yeni Şekil - - - Yeni Dış Katman - - - Yeni Saç - - - Yeni Gözler - - - Yeni Gömlek - - - Yeni Pantolon - - - Yeni Ayakkabılar - - - Yeni Çoraplar - - - Yeni Ceket - - - Yeni Eldivenler - - - Yeni Fanila - - - Yeni Külot - - - Yeni Etek - - - Yeni Alfa - - - Yeni Dövme - - - Yeni Fizik - - - Geçersiz Giyilebilir - - - Yeni Mimik - - - Yeni Komut Dosyası - - - Yeni Not - - - Yeni Klasör - - - İçerik - - - Mimik - - - Erkek Mimikleri - - - Kadın Mimikleri - - - Diğer Mimikler - - - Konuşma Mimikleri - - - Favori Mimikler - - - Erkek - Afedersiniz - - - Erkek - İşine bak - - - Erkek - Öpücük gönderme - - - Erkek - Yuh çekme - - - Erkek - Canı sıkılmış - - - Erkek - Hey - - - Erkek - Gülme - - - Erkek - Tiksinmiş - - - Erkek - Omuz Silkme - - - Erkek - Dil çıkarma - - - Erkek - Vay be - - - Kadın - Kıkırdama - - - Kadın - Ağlama - - - Kadın - Utanmış - - - Kadın - Afedersiniz - - - Kadın - İşine bak - - - Kadın - Öpücük gönderme - - - Kadın - Yuh çekme - - - Kadın - Canı sıkılmış - - - Kadın - Hey - - - Kadın - Hey bebek - - - Kadın - Gülme - - - Kadın - Yakışıklı - - - Kadın - Buraya baksana - - - Kadın - Lütfen - - - Kadın - Tiksinmiş - - - Kadın - Omuz Silkme - - - Kadın - Dil çıkarma - - - Kadın - Vay be - - - /selamlama - - - /alkış - - - /sayım - - - /söndürme - - - /hib - - - /kas - - - /hayır - - - /hayır! - - - /kağıt - - - /beni göster - - - /seni göster - - - /kaya - - - /makas - - - /duman - - - /uzatma - - - /ıslık - - - /evet - - - /evet! - - - kbd - - - dans1 - - - dans2 - - - dans3 - - - dans4 - - - dans5 - - - dans6 - - - dans7 - - - dans8 - - - [mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt] - - - hiçbiri/hiçbiri - - - [WIDTH]*[HEIGHT] çözünürlüğünden büyük görüntüler yüklenemez - - - - Tüm çabalarımıza rağmen beklenmeyen bir hata meydana geldi. - - Hizmetle ilişkili bilinen bir sorun olup olmadığını görmek için lütfen status.secondlifegrid.net adresine bakın. - Sorun yaşamaya devam ederseniz lütfen ağınızın ve güvenlik duvarınızın ayarlarına bakın. - - - Pazar:Pazartesi:Salı:Çarşamba:Perşembe:Cuma:Cumartesi - - - Paz:Pzt:Sal:Çar:Per:Cum:Cmt - - - Ocak:Şubat:Mart:Nisan:Mayıs:Haziran:Temmuz:Ağustos:Eylül:Ekim:Kasım:Aralık - - - Oca:Şub:Mar:Nis:May:Haz:Tem:Ağu:Eyl:Eki:Kas:Ara - - - [MDAY] - - - ÖÖ - - - ÖS - - - US$ [AMOUNT] - - - Üyelik - - - Roller - - - Grup Kimliği - - - Parsel Yönetimi - - - Parsel Kimliği - - - Parsel Ayarları - - - Parsel Güçleri - - - Parsel Erişimi - - - Parsel İçeriği - - - Nesne Yönetimi - - - Muhasebe - - - Bildirimler - - - Sohbet - - - Seçili öğeler silinsin mi? - - - Seçili öğe silinsin mi? - - - Bu dış görünümde herhangi bir öğe yok - - - ExternalEditor ayarını kullanarak bir düzenleyici seçin. - - - Belirttiğiniz harici düzenleyici bulunamadı. -Düzenleyici yolunu çift tırnakla çevrelemeyi deneyin. -(örn. "/yolum/duzenleyici" "%s") - - - Harici düzenleyici komutu ayrıştırılırken hata oluştu. - - - Harici düzenleyici çalışmadı. - - - Çeviri başarılamadı: [REASON] - - - Çeviri yanıtı ayrıştırılırken hata meydana geldi. - - - Esc - - - Boşluk - - - Enter - - - Sekme - - - Ins - - - Del - - - Geri tuşu - - - Shift - - - Ctrl - - - Alt - - - CapsLock - - - Sol - - - Sağ - - - Yukarı - - - Aşağı - - - Home - - - End - - - PgUp - - - PgDn - - - F1 - - - F2 - - - F3 - - - F4 - - - F5 - - - F6 - - - F7 - - - F8 - - - F9 - - - F10 - - - F11 - - - F12 - - - Ekle - - - Çıkar - - - Çarp - - - Böl - - - PAD_DIVIDE - - - PAD_LEFT - - - PAD_RIGHT - - - PAD_DOWN - - - PAD_UP - - - PAD_HOME - - - PAD_END - - - PAD_PGUP - - - PAD_PGDN - - - PAD_CENTER - - - PAD_INS - - - PAD_DEL - - - PAD_Enter - - - PAD_BUTTON0 - - - PAD_BUTTON1 - - - PAD_BUTTON2 - - - PAD_BUTTON3 - - - PAD_BUTTON4 - - - PAD_BUTTON5 - - - PAD_BUTTON6 - - - PAD_BUTTON7 - - - PAD_BUTTON8 - - - PAD_BUTTON9 - - - PAD_BUTTON10 - - - PAD_BUTTON11 - - - PAD_BUTTON12 - - - PAD_BUTTON13 - - - PAD_BUTTON14 - - - PAD_BUTTON15 - - - - - - - = - - - ` - - - ; - - - [ - - - ] - - - \ - - - 0 - - - 1 - - - 2 - - - 3 - - - 4 - - - 5 - - - 6 - - - 7 - - - 8 - - - 9 - - - A - - - B - - - C - - - D - - - E - - - F - - - G - - - H - - - I - - - J - - - K - - - L - - - M - - - N - - - O - - - P - - - Q - - - R - - - S - - - T - - - U - - - V - - - W - - - X - - - Y - - - Z - - - Parçacık işaretleri gösteriliyor (mavi) - - - Fiziksel nesne işaretleri gösteriliyor (yeşil) - - - Komut dosyalı nesne işaretleri gösteriliyor (kırmızı) - - - Dokunma işlevli komut dosyalı nesne işaretleri gösteriliyor (kırmızı) - - - Ses işaretleri gösteriliyor (sarı) - - - Ortam işaretleri gösteriliyor (beyaz) - - - Parçacıklar Gizleniyor - - - Arazi hakkında - - - Görünüm - - - Avatar - - - İnşa Et - - - Sohbet - - - Pusula - - - Hedef Konum - - - Mimikler - - - Nasıl yapılır - - - Envanter - - - Harita - - - Pazaryeri - - - Mini-harita - - - Yürü / koş / uç - - - Satıcı giden kutusu - - - Kişiler - - - Seçmeler - - - Yerler - - - Tercihler - - - Profil - - - Ara - - - Anlık görüntü - - - Konuş - - - Kamera denetimleri - - - Ses ayarları - - - Ziyaret ettiğiniz araziyle ilgili bilgi - - - Avatarınızı değiştirin - - - Eksiksiz bir avatar seçin - - - Nesneler oluşturma ve yüzeyi şekillendirme - - - Metin kullanarak yakındaki kişilerle sohbet etmek - - - Pusula - - - İlgilendiğiniz hedef konumlar - - - Avatarınız için mimikler - - - Genel görevleri nasıl yapacağınız - - - Eşyalarınızı görüntüleyin ve kullanın - - - Dünya haritası - - - Alışveriş yap - - - Yakındaki kişileri göster - - - Avatarınızı hareket ettirmek - - - Satmak amacıyla Pazaryerinize öğeler taşıyın - - - Arkadaşlar, gruplar ve yakındaki kişiler - - - Profilinizde favori olarak gösterilecek yerler - - - Kaydettiğiniz yerler - - - Tercihler - - - Profilinizi düzenleyin veya görüntüleyin - - - Yerler, etkinlikler ve kişiler bulmak - - - Resim çekin - - - Mikrofonunuzu kullanarak yakındaki kişilerle konuşun - - - Kamera açısını değiştirmek - - - Aramalar ve SL dünyası içinde size yakın kişiler için ses denetimleri - - - şu anda alt araç çubuğunuzda - - - şu anda sol araç çubuğunuzda - - - şu anda sağ araç çubuğunuzda - - - Koru % - - - Ayrıntı - - - Daha İyi Ayrıntı - - - Yüzey - - - Katı - - - Sar - - - Önizleme - - - Normal - - - http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer - - - Hiçbiri - - - Navigasyon örgüsünü etkiler - - - Karakter - - - (Birden çok) - - - Çok Düşük - - - Düşük - - - Orta - - - Yüksek - - - Çok Yüksek - - - Sakin bu bölgeyi ziyaret edemez. - - - [User] - - diff --git a/indra/newview/skins/steam/xui/zh/strings.xml b/indra/newview/skins/steam/xui/zh/strings.xml deleted file mode 100644 index 718d6805f1..0000000000 --- a/indra/newview/skins/steam/xui/zh/strings.xml +++ /dev/null @@ -1,4591 +0,0 @@ - - - - - 第二人生 - - - 第二人生 - - - 第二人生 - - - 第二人生網格 - - - 第二人生支援入口 - - - 硬體偵測中... - - - [APP_NAME] 載入中... - - - 快取清除中... - - - 材質快取初始化中... - - - VFS 初始化中... - - - 回存中... - - - 變更解析度... - - - 全亮(舊版) - - - 登入中。[APP_NAME] 可能看似凍結,請耐心稍等。 請稍候。 - - - 登入中... - - - 身份驗證中 - - - 進行帳戶維護... - - - 前一次登入失敗。 登入中,第 [NUMBER] 次嘗試 - - - 世界載入中... - - - 內嵌式網頁瀏覽器初始化中... - - - 多媒體初始化中... - - - 字型載入中... - - - 驗證快取檔案(約需 60-90 秒左右)... - - - 回應處理中... - - - 世界初始化中... - - - 圖像解碼中... - - - QuickTime 初始化中... - - - QuickTime 未發現 - 無法進行初始化。 - - - QuickTime 已成功初始化。 - - - 地區交握等待中... - - - 地區聯結中... - - - 服裝下載中... - - - 伺服器回傳一個無效或損壞的憑證。 請聯絡網格管理員。 - - - 用了無效的主機名來聯絡伺服器,請檢查你的 SLURL 或網格主機名。 - - - 網格傳回的憑證似乎已過期。 請檢查系統時鐘,或聯絡網格管理員。 - - - 伺服器傳回的憑證無法用於 SSL。 請聯絡網格管理員。 - - - 伺服器憑證鍊中的憑證數目太多。 請聯絡網格管理員。 - - - 無法檢驗通過網格伺服器傳回的憑證簽名。 請聯絡網格管理員。 - - - 網路出錯:無法建立連線,請檢查網路連線是否正常。 - - - 登入失敗。 - - - 結束退出 - - - http://join.secondlife.com/?sourceid=1206_steam - - - 你目前所用的 Viewer 已經無法再進入第二人生。 請到這個頁面下載最新 Viewer: -http://secondlife.com/download - -欲知詳情,請參閱下面的常見問題集: -http://secondlife.com/viewer-access-faq - - - 有可以選擇性安裝的更新版:[VERSION] - - - 你必須更新為這個版本:[VERSION] - - - 此用戶已經登入。 - - - 抱歉! 我們無法讓你登入。 -請檢查確定你輸入了正確的 - * 使用者名稱(例:bobsmith123 或 steller.sunshine) - * 密碼 -並請確定鍵盤沒有鎖定大寫鍵。 - - - 為了安全起見,已經變更你的密碼。 -請到位於 http://secondlife.com/password 的帳號頁面 -回答安全驗證問題後,重設密碼。 -如有造成不便請多包涵。 - - - 我們系統有所變更,你必須重設密碼。 -請到位於 http://secondlife.com/password 的帳號頁面 -回答安全驗證問題後,重設密碼。 -如有造成不便請多包涵。 - - - 第二人生目前暫時關閉進行維護。 -目前僅允許林登員工登入。 -請到 www.secondlife.com/status 察看最新公告。 - - - 第二人生此時暫時限制登入,以確保不影響效能,讓目前虛擬世界裡的用戶享受最佳的體驗。 - -免費帳戶的用戶此時暫時無法進入第二人生,因為我們必須優先容納付費用戶。 - - - 無法從這部電腦進入第二人生。 -如你認為這是我們弄錯,請聯絡 support@secondlife.com。 - - - 你的帳號要等到 [TIME] (太平洋時間)才可使用。 - - - 此時無法完成你的請求。 -請到 http://secondlife.com/support 聯絡支援人員獲得幫助。 -如果你無法變更密碼,請致電 (866) 476-9763 (美國)。 - - - 登入時的資料不一致。 -請聯絡 support@secondlife.com。 - - - 你的帳號目前正在進行小規模的維護。 -你的帳號要等到 [TIME] (太平洋時間)才可使用。 -如你認為這是我們弄錯,請聯絡 support@secondlife.com。 - - - 模擬器回應:登出請求出錯。 - - - 系統正在處理你的登出。 -你的帳號要等到 [TIME] (太平洋時間)才可使用。 - - - 無法建立有效的時域。 - - - 無法連接到模擬器。 - - - 你的帳號僅能在 [START] 到 [END] (太平洋時間)時段進入第二人生。 -請耐心等到該時段再回來。 -如你認為這是我們弄錯,請聯絡 support@secondlife.com。 - - - 錯誤的參數。 -如你認為這是我們弄錯,請聯絡 support@secondlife.com。 - - - 全名的第一個字(first name)須為英文字母或數字。 -如你認為這是我們弄錯,請聯絡 support@secondlife.com。 - - - 全名的第二個字(last name)須為英文字母或數字。 -如你認為這是我們弄錯,請聯絡 support@secondlife.com。 - - - 地區即將離線。 -請稍待一分鐘再試。 - - - 用戶不在地區裡。 -請稍待一分鐘再試。 - - - 地區正在登入另一個時域。 -請稍待一分鐘再試。 - - - 地區正在登入上一個時域。 -請稍待一分鐘再試。 - - - 地區正在登出上一個時域。 -請稍待一分鐘再試。 - - - 地區剛剛登出上一個時域。 -請稍待一分鐘再試。 - - - 地區已經開始登出程序。 -請稍待一分鐘再試。 - - - 系統已經開始登出你的上一個時域。 -請稍待一分鐘再試。 - - - 本區域可能正發生問題。 請檢查你的網際網路連線是否正常。 - - - 你的設定儲存中... - - - 登出中... - - - 關閉中... - - - 你已經被所在的地區中斷聯結。 - - - 你被傳送到一個無效的地區。 - - - 測試瀏覽器斷線 - - - 人 - - - (無名稱) - - - 所有人: - - - 公開 - - - (群組) - - - 出售: L$[AMOUNT] - - - 群組建造 - - - 禁止建造 - - - 群組建造 - - - 非安全 - - - 禁止飛行 - - - 群組腳本 - - - 禁止腳本 - - - 土地: - - - 只有一個物品可以被拖曳到此處 - - - - 點按以察看這個網頁 - - - 點按以察看這個位置資訊 - - - 點按以察看這個居民檔案 - - - 瞭解更多有關這個居民 - - - 點按以封鎖這位居民 - - - 點按以解除封鎖這位居民 - - - 點按開始 IM 這位居民 - - - 點按以支付這位居民 - - - 點按以送出瞬間傳送邀請給這位居民 - - - 點按以送出交友邀請給這位居民 - - - 點按以察看這個群組的描述 - - - 點按以察看這個活動的描述 - - - 點按察看這個個人廣告 - - - 點按以察看這個地段的描述 - - - 點按以傳送到這個位置 - - - 點按以察看這個物件的描述 - - - 點按以察看此處在地圖上的位置 - - - 點按以執行 secondlife:// 指令 - - - - 瞬間傳送到 - - - 顯示地圖為了 - - - 封鎖 - - - 解除封鎖 - - - IM - - - 支付 - - - 發給瞬間傳送請求到 - - - 交友要求 - - - 關閉(⌘W) - - - 關閉(Ctrl+W) - - - 關閉 - - - 還原 - - - 最小化 - - - 脫下 - - - 停泊固定 - - - 顯示幫助 - - - 搜尋中... - - - 查無結果。 - - - 檢索... - - - 釋出版本說明 - - - 載入中... - - - (沒有人) - - - (等待中) - - - (多個) - - - (無) - - - Avaline 通話者 [ORDER] - - - 無錯誤 - - - 資產請求:失敗 - - - 資產要求:非已存在檔案 - - - 資產要求:資產並未發現於資料庫中 - - - 檔案結尾 - - - 無法開啟檔案 - - - 檔案未發現 - - - 檔案傳輸逾時 - - - 失去線路 - - - Viewer 和伺服器無法同意價格 - - - 未知狀態 - - - 材質 - - - 聲音 - - - 名片 - - - 地標 - - - 舊版腳本 - - - 服裝 - - - 物件 - - - 記事卡 - - - 資料夾 - - - 根目錄 - - - LSL2 腳本 - - - LSL 位元組碼 - - - tga 材質 - - - 身體部位 - - - 快照 - - - 失物招領 - - - targa 圖像 - - - 垃圾桶 - - - jpeg 圖像 - - - 動作 - - - 姿勢 - - - 模擬器狀態 - - - 我的最愛 - - - 聯結 - - - 資料夾聯結 - - - 網面 - - - (外觀編輯中) - - - 離開 - - - 忙碌 - - - 封鎖的 - - - 害怕 - - - 生氣 - - - 離開 - - - 後空翻 - - - 捧腹大笑 - - - 大微笑 - - - 飛吻 - - - 無聊 - - - 彎腰點頭 - - - 拍手 - - - 宮廷鞠躬 - - - 哭泣 - - - 跳舞1 - - - 跳舞2 - - - 跳舞3 - - - 跳舞4 - - - 跳舞5 - - - 跳舞6 - - - 跳舞7 - - - 跳舞8 - - - 鄙視 - - - 喝一口 - - - 尷尬 - - - 揮動食指 - - - 高舉右拳 - - - 漂浮瑜伽 - - - 皺眉 - - - 不耐煩 - - - 雀躍 - - - 親我屁股! - - - 親吻 - - - 笑 - - - 秀健美肌肉 - - - 不(不快樂) - - - 否 - - - Nya-nya-nya - - - 連續左右出拳 - - - 張口 - - - 和平手勢 - - - 指著別人 - - - 指著自己 - - - 左出拳 - - - 右出拳 - - - 剪刀石頭布預備動作 - - - 剪刀石頭布:布 - - - 剪刀石頭布:石頭 - - - 剪刀石頭布:剪刀 - - - 作噁 - - - 旋踢 - - - 傷心 - - - 敬禮 - - - 吶喊 - - - 聳聳肩 - - - 微笑 - - - 悠閒抽菸 - - - 吸一口菸 - - - 甩菸蒂 - - - 驚喜 - - - 劍擊 - - - 鬧脾氣 - - - 吐舌頭 - - - 揮手 - - - 耳語 - - - 吹口哨 - - - 眨眼 - - - 眨眼(好萊塢) - - - 擔心 - - - 是(快樂) - - - 是 - - - 載入中... - - - 離線 - - - [AREA] 平方公尺 L$[PRICE] - - - 查無結果。 - - - 確定 - - - 檔案異常中止 - - - 找不到 ROOT 或旋軸。 - - - 低語: - - - 吶喊: - - - 聯接到虛擬世界的語音功能中... - - - 已聯接 - - - 語音聊天功能於你目前所在的位置無法使用 - - - 虛擬世界中的語音聊天中斷 - - - 現在你將重新聯接到附近的語音聊天 - - - 物件「[OBJECTNAME]'」(所有人「[OWNERNAME]」,位於「[REGIONNAME]」,方位「[REGIONPOS]」)已獲得下列權限:[PERMISSIONS]。 - - - 物件「[OBJECTNAME]'」(所有人「[OWNERNAME]」,位於「[REGIONNAME]」,方位「[REGIONPOS]」)已被撤除下列權限:[PERMISSIONS]。 - - - 由你身上拿走林登幣(L$) - - - 按你的控制輸入行動 - - - 重新規劃你的控制輸入 - - - 使化身動起來 - - - 附加到化身 - - - 釋出所有權,開放給所有人 - - - 連結其他物件或去除連結 - - - 和其他物件建立或移除旋軸 - - - 變更它的權限 - - - 追蹤你的攝影機 - - - 控制你的攝影機 - - - 未聯接 - - - 一般普級 - - - 適度成人 - - - 完全成人 - - - 離線 - - - 未知 - - - (未知) - - - 領地 / 完整地區 - - - 領地 / 家園 - - - 大陸 / 家園 - - - 大陸 / 完整地區 - - - 全部檔案 - - - 聲音 - - - 動作 - - - 圖像 - - - 儲存 - - - 載入 - - - Targa 圖像 - - - Bitmap 圖像 - - - AVI 視頻檔案 - - - XAF 動畫檔案 - - - XML 檔案 - - - RAW 檔案 - - - 壓縮的圖像 - - - 載入檔案 - - - 選擇目錄 - - - 非離開 - - - 離開 - - - 非忙碌 - - - 忙碌 - - - 體形 - - - 皮膚 - - - 頭髮 - - - 眼睛 - - - 襯衫 - - - 褲子 - - - 鞋子 - - - 襪子 - - - 夾克 - - - 手套 - - - 內衣 - - - 內褲 - - - 裙子 - - - 半透明 - - - 刺青 - - - 身體物理 - - - 無效 - - - 無 - - - 襯衫未穿 - - - 褲子未穿 - - - 鞋子未穿 - - - 襪子未穿 - - - 夾克未穿 - - - 手套未穿 - - - 內衣未穿 - - - 內褲未穿 - - - 裙子未穿 - - - 半透明未穿 - - - 刺青未穿 - - - 身體物理未穿 - - - 無效 - - - 創造新體形 - - - 創造新皮膚 - - - 創造新頭髮 - - - 創造新眼睛 - - - 創造新襯衫 - - - 創造新褲子 - - - 創造新鞋子 - - - 創造新襪子 - - - 創造新夾克 - - - 創造新手套 - - - 創造新內衣 - - - 創造新內褲 - - - 創造新裙子 - - - 創造新半透明 - - - 創造新刺青 - - - 創造新身體物理 - - - 無效 - - - 新 [WEARABLE_ITEM] - - - 下一個 - - - 確定 - - - 群組通知 - - - 群組通知 - - - 送出由 - - - 附件: - - - 在這裡察看舊通知或選擇不接收訊息。 - - - 開啟附件 - - - 儲存附件 - - - 發出「瞬間傳送」邀請 - - - 當你離開時有新的通知送達。 - - - 你有約 %d 通知 - - - 右臂 - - - 頭部 - - - 左臂 - - - 左腿 - - - 軀幹 - - - 右腿 - - - 低 - - - 中 - - - 高 - - - 按下 ESC 鍵回復到世界的視角 - - - 找不到你要找的嗎? 請試試 [secondlife:///app/search/places/ 搜尋]。 - - - 找不到你要找的嗎? 請試試 [secondlife:///app/search/places/[SEARCH_TERM] 搜尋]。 - - - 將一個地標拖曳到這裡,加進「我的最愛」。 - - - 你的收納區裡沒有這個材質的副本 - - - - - - - - - 內容載入中... - - - 無內容 - - - - - 是 - - - 否 - - - - - - - - - - - - - - - - - - - - - - - - - - 我的收納區 - - - 我的最愛 - - - 資源庫 - - - 材質 - - - 聲音 - - - 名片 - - - 地標 - - - 腳本 - - - 服裝 - - - 物件 - - - 記事卡 - - - 新資料夾 - - - 收納區 - - - 未壓縮圖像 - - - 身體部位 - - - 垃圾桶 - - - 相簿 - - - 失物招領 - - - 未壓縮聲音 - - - 動作 - - - 姿勢 - - - 我的最愛 - - - 我的最愛 - - - 目前裝扮 - - - 初始裝扮 - - - 我的裝扮 - - - 配件 - - - 網面 - - - 朋友 - - - 全部 - - - 購買 - - - 出價購買:L$ - - - 石頭 - - - 金屬 - - - 玻璃 - - - 木頭 - - - 肌肉 - - - 塑膠 - - - 橡膠 - - - 光源 - - - Shift 鍵 - - - Ctrl 鍵 - - - 胸部 - - - 頭顱 - - - 左肩 - - - 右肩 - - - 左手 - - - 右手 - - - 左腳 - - - 右腳 - - - 脊椎 - - - 骨盆 - - - 嘴巴 - - - 下巴 - - - 左耳 - - - 右耳 - - - 左眼球 - - - 右眼球 - - - 鼻子 - - - 右上臂 - - - 右前臂 - - - 左上臂 - - - 左前臂 - - - 右臀 - - - 右大腿 - - - 右小腿 - - - 左臀 - - - 左大腿 - - - 左小腿 - - - 腹肌 - - - 左胸肌 - - - 右胸肌 - - - 無效的附接點 - - - 年齡:[AGEYEARS] 年 [AGEMONTHS] 月 - - - 年齡:[AGEYEARS] 年 - - - [AGEMONTHS] 月 - - - [AGEWEEKS] 週 - - - [AGEDAYS] 天 - - - 今日剛加入 - - - [COUNT] 年 - - - [COUNT] 年 - - - [COUNT] 年 - - - [COUNT] 月 - - - [COUNT] 月 - - - [COUNT] 月 - - - [COUNT] 週 - - - [COUNT] 週 - - - [COUNT] 週 - - - [COUNT] 天 - - - [COUNT] 天 - - - [COUNT] 天 - - - [COUNT] 位成員 - - - [COUNT] 位成員 - - - [COUNT] 位成員 - - - 居民 - - - 試用 - - - 老牌 Charter 成員 - - - 林登實驗室員工 - - - 使用的付款資料 - - - 預留付款資料 - - - 未預留付款資料 - - - 通過年齡驗證 - - - 未通過年齡驗證 - - - 中央 2 - - - 右上 - - - 上方 - - - 左上 - - - 中央 - - - 左下 - - - 下方 - - - 右下 - - - 已下載,正在編譯中 - - - 伺服器上未發現腳本。 - - - 下載時出問題 - - - 下載腳本的權限不足。 - - - 權限不足: - - - 下載失敗,原因不明 - - - 重新編譯進度 - - - 重新編譯 - - - 重設進度 - - - 重設 - - - 設定「執行中」進度 - - - 設為「執行中」 - - - 設定「非執行中」進度 - - - 設為「非執行中」 - - - 編譯成功! - - - 編譯成功,儲存中... - - - 儲存完畢。 - - - 腳本(物件超出範圍) - - - 物件 [OBJECT] 為 [OWNER] 所擁有 - - - 無 - - - - (未知) - - - - - - - 餘額 - - - 貸記 - - - 借記 - - - 總額 - - - 查無群組資料 - - - 母領地 - - - 大陸 - - - 青少年 - - - 錯誤 - - - [OWNER] 所擁有的的全部領地 - - - 你所擁有的全部領地 - - - 你為 [OWNER] 管理的全部領地 - - - 允許居民:([ALLOWEDAGENTS],最多 [MAXACCESS]) - - - 允許的群組:([ALLOWEDGROUPS],最多 [MAXACCESS]) - - - 地段腳本記憶體 - - - 地段清單:[PARCELS] - - - 計憶體用量:使用 [MAX] kb 中的 [COUNT] kb ;剩餘 [AVAILABLE] kb 可用 - - - 記憶體用量:[COUNT] kb - - - 地段腳本的 URL - - - URL 使用狀況:最多可用 [MAX],已用 [COUNT],剩餘 [AVAILABLE] - - - 已用 URL:[COUNT] - - - 調資料時出錯 - - - 未選擇地段 - - - 錯誤:只能在你目前所處區域取得腳本資訊 - - - 正在調閱資料... - - - 你無權審視此地段 - - - 坐在 - - - 胸部 - - - 頭部 - - - 左肩 - - - 右肩 - - - 左手 - - - 右手 - - - 左腳 - - - 右腳 - - - 背部 - - - 骨盆 - - - 嘴巴 - - - 下巴 - - - 左耳 - - - 右耳 - - - 左眼 - - - 右眼 - - - 鼻子 - - - 右上臂 - - - 右前臂 - - - 左上臂 - - - 左前臂 - - - 右臀 - - - 右大腿 - - - 右小腿 - - - 左臀 - - - 左大腿 - - - 左小腿 - - - 腹部 - - - 右胸肌 - - - 左胸肌 - - - 擡頭顯示中央 2 - - - 擡頭顯示右上 - - - 擡頭顯示中央上方 - - - 擡頭顯示左上 - - - 擡頭顯示中央 1 - - - 擡頭顯示左下 - - - 擡頭顯示下方 - - - 擡頭顯示右下 - - - 橫行 [LINE],縱列 [COLUMN] - - - 找到 [COUNT] - - - [hour12,datetime,slt]:[min,datetime,slt] [ampm,datetime,slt] - - - [mthnum,datetime,slt]/[day,datetime,slt] - - - 物件內容 - - - 新腳本 - - - 你傳訊過去的居民目前處於忙碌狀態,這意味著他要求不被打擾。 你的訊息仍將留存並顯示於對���的 IM 面板上供稍後查閱。 - - - (按名稱) - - - (居民) - - - (物件) - - - (群組) - - - (外部) - - - 此領地沒有任何契約要求。 - - - 此領地沒有任何契約要求。 本領地土地的出售人是領地所有人,不是林登實驗室。 請聯絡領地所有人獲知售地詳情。 - - - - - - 群組所擁有 - - - 公開 - - - 點按:[TELEPORT] 瞬間傳送,[MAP] 地圖,[PROFILE] 檔案 - - - (將於發布後更新) - - - 你尚未建立任何精選地點或個人廣告。 點按下面的 + 按鈕建立精選地點或個人廣告。 - - - 使用者無精選地點或個人廣告 - - - 載入中... - - - 預覽 - - - 屬性 - - - 一個物件,名為 - - - 屬於群組 - - - 屬於一個未知群組 - - - 所有人: - - - 屬於某未知使用者 - - - 給予你 - - - 你拒絕了來自 <nolink>[NAME]</nolink> 的 [DESC]。 - - - 總額 - - - 買了 - - - 支付給你 - - - 支付給 - - - 買了通行權: - - - 付了費用參加活動 - - - 付了賞金參加活動 - - - 餘額 - - - 貸記 - - - 借記 - - - 內容 - - - 取得物品 - - - 取消 - - - 上傳 [NAME] 費用 L$ [AMOUNT] - - - 購買這個需要 L$ [AMOUNT] - - - 未知的副檔名 .%s -應該是 .wav、.tga、.bmp、.jpg、.jpeg 或 .bvh - - - 封鎖 - - - 封鎖 - - - 解除封鎖 - - - 解除封鎖 - - - 添加到我的地標... - - - 編輯我的地標... - - - ⌃ - - - ⌘ - - - ⌥ - - - ⇧ - - - Ctrl+ - - - Alt+ - - - Shift+ - - - 檔案已儲存 - - - 接收中 - - - 上午 - - - 下午 - - - 太平洋時間 - - - 太平洋日光節約時間 - - - 向前 - - - 左移鍵 - - - 右移鍵 - - - 向後 - - - 北 - - - 南 - - - 西 - - - 東 - - - 上移鍵 - - - 下移鍵 - - - 任何類別 - - - 購物 - - - 土地租賃 - - - 房產租賃 - - - 熱門地點 - - - 新產品 - - - 徵人 - - - 徵求 - - - 服務 - - - 交友 - - - 無 - - - 林登位置 - - - 完全成人 - - - 藝術與文化 - - - 商業 - - - 教育 - - - 遊戲 - - - 聚會所 - - - 歡迎新手光臨 - - - 公園與自然景觀 - - - 住宅 - - - 舞臺 - - - 其他 - - - 出租 - - - 任何 - - - 你 - - - : - - - , - - - ... - - - *** - - - ( - - - ) - - - . - - - ' - - - --- - - - 多媒體 - - - 播放/暫停媒體 - - - 解析命令列時發現錯誤。 -請參閱: http://wiki.secondlife.com/wiki/Client_parameters -錯誤: - - - [APP_NAME] 命令列用法: - - - [APP_NAME] 無法存取它所需的檔案。 - -你可能有多重實例執行中,或你的系統誤以為某檔案已經開啟。 -如果這個訊息持續出現,請重新啟動你的電腦。 -如果問題仍然存在,你可能需要徹底卸除 [APP_NAME] 再重新安裝。 - - - 致命錯誤 - - - [APP_NAME] 需要有 AltiVec(G4 或更新版)的處理器。 - - - [APP_NAME] 已經在執行中。 -請檢查你的工作列裡是否已有最小化的相同程式。 -如果這個訊息持續出現,請重新啟動你的電腦。 - - - [APP_NAME] 上次執行時好像出現凍結或當掉。 -你是否想要報告當機事例? - - - 通知 - - - [APP_NAME] 偵測不到 DirectX 9.0b 或更新版。 -[APP_NAME] 使用 DirectX 來偵測可能導致不穩定、執行效能不佳或當機發生的硬體或老舊驅動器。 你可以選擇不用 DirectX 9.0b 來執行 [APP_NAME],但我們大力推薦你使用。 - -你確定要繼續嗎? - - - 警告 - - - 尚無試用於 Linux 的自動更新功能。 -請到 www.secondlife.com 下載最新版本。 - - - RegisterClass 失敗 - - - 錯誤 - - - [WIDTH] x [HEIGHT] 解析度下無法執行全螢幕。 -在視窗中執行。 - - - 消滅視窗時發生強行關閉錯誤(DestroyWindow()失敗) - - - 強行關閉出錯 - - - 無法建立 GL 裝置環境 - - - 找不到適合的像素格式 - - - 無法取得像素格式描述 - - - [APP_NAME] 需要全彩(32位元)才能執行。 -請到電腦的顯示設定,將色彩模式設為 32 位元。 - - - [APP_NAME] 無法執行,無法取得 8 位元 alpha 頻道。 這通常是因為顯示卡驅動程式出問題。 -請確定你安裝了最新的顯示卡驅動程式。 -請到控制台 > 顯示 > 設定處將螢幕設為全彩(32 位元)。 -如果你繼續看到此訊息,請聯絡 [SUPPORT_SITE]。 - - - 無法設定像素格式 - - - 無法建立 GL 呈像環境 - - - 無法啟動 GL 呈像環境 - - - [APP_NAME] 無法執行,這可能因為你的顯示卡驅動程式安裝不當、版本過舊,或者你的硬體不受支援。 請確定你安裝了最新的顯示卡驅動程式。如果已裝了最新驅動程式,請再重新安裝。 - -如果你繼續看到此訊息,請聯絡 [SUPPORT_SITE]。 - - - 下午五點的新鬍渣 - - - 全白 - - - 日式動漫眼 - - - 彎拱的 - - - 臂長 - - - 貼附的 - - - 附著耳垂 - - - 後瀏海 - - - 袋型的 - - - 瀏海 - - - 珠圓銳光眼 - - - 腹部大小 - - - 大 - - - 大臀部 - - - 大型頭髮:後面 - - - 大型頭髮:前面 - - - 大型頭髮:頂部 - - - 大頭 - - - 大胸肌 - - - 大尖直髮 - - - 黑色 - - - 金色 - - - 金髮 - - - 腮紅 - - - 淺粉色 - - - 腮紅不透明度: - - - 身體結實度 - - - 體脂肪 - - - 身體雀斑 - - - 寬厚體型 - - - 體型厚度 - - - 窄瘦體型 - - - 弓形腿 - - - 乳房彈性 - - - 乳溝深淺 - - - 乳房尺寸 - - - 兩眼間距 - - - 寬 - - - 眉毛大小 - - - 突眼 - - - 突眼 - - - 球狀 - - - 球狀鼻 - - - 乳房質量 - - - 乳房增圓滑 - - - 乳房重力特性 - - - 乳房阻力 - - - 最大效果 - - - 彈跳 - - - 增益 - - - 阻尼 - - - 最大效果 - - - 彈跳 - - - 增益 - - - 阻尼 - - - 最大效果 - - - 彈跳 - - - 增益 - - - 阻尼 - - - 腹部質量 - - - 腹部增圓滑 - - - 腹部重力特性 - - - 腹部阻力 - - - 最大效果 - - - 彈跳 - - - 增益 - - - 阻尼 - - - 臀部質量 - - - 臀部增圓滑 - - - 臀部重力特性 - - - 臀部阻力 - - - 最大效果 - - - 彈跳 - - - 增益 - - - 阻尼 - - - 最大效果 - - - 彈跳 - - - 增益 - - - 阻尼 - - - 濃眉 - - - 濃密頭髮 - - - 臀部大小 - - - 臀部重力特性 - - - 側皺過膝長裙 - - - 無側皺 - - - 增加側皺 - - - Chaplin - - - 顴骨 - - - 胸部大小 - - - 下巴角度 - - - 下巴裂度 - - - 絡腮鬍 - - - 下巴深度 - - - 下巴厚重 - - - 下巴後縮 - - - 下巴突出 - - - 下巴-頸部 - - - 清除 - - - 分裂 - - - 雙眼靠近 - - - 閉合 - - - 後閉合 - - - 前閉合 - - - 左閉合 - - - 右閉合 - - - 小錢包 - - - 後 Collar - - - 前 Collar - - - 角落朝下 - - - 角落朝上 - - - 皺褶 - - - 彎曲鼻 - - - 袖口裝飾 - - - 深暗 - - - 深綠 - - - 更深暗 - - - 深 - - - 預設高跟鞋 - - - 稠密 - - - 雙下巴 - - - 嘴角下垂 - - - 旅行袋 - - - 耳朵角度 - - - 耳朵大小 - - - 耳端 - - - 蛋形頭 - - - 眼袋 - - - 眼睛顏色 - - - 眼睛深度 - - - 眼睛亮度 - - - 眼睛垂直大小 - - - 眼睛 Pop - - - 眼睛大小 - - - 雙眼間距 - - - 眉毛弧度 - - - 眉毛密度 - - - 眉毛高度 - - - 眉毛點 - - - 眉毛尺寸 - - - 睫毛長度 - - - 眼線筆 - - - 眼影筆顏色 - - - 突眼 - - - 臉部偏移 - - - 臉部結實度 - - - 雙眼距離遠 - - - 厚脣 - - - 女性 - - - 無手指 - - - 手指 - - - 帶裝飾袖口 - - - 平 - - - 臀部扁平 - - - 頭部扁平 - - - 腳趾扁平 - - - 腳部大小 - - - 前額角度 - - - 前額寬厚 - - - 雀斑 - - - 前瀏海 - - - 後部飽滿 - - - 厚重眼線 - - - 前面飽滿 - - - 側髮飽滿 - - - 兩側飽滿 - - - 光亮 - - - 手套手指 - - - 手套長度 - - - 頭髮 - - - 頭髮:後面 - - - 頭髮:前面 - - - 頭髮:側面 - - - 頭髮垂擺 - - - 頭髮厚度 - - - 頭髮厚度 - - - 頭髮傾度 - - - 頭髮傾左 - - - 頭髮傾右 - - - 頭髮:髮量 - - - 手部大小 - - - 把手 - - - 頭部長度 - - - 頭型 - - - 頭部大小 - - - 頭部延展度 - - - 腳踵高度 - - - 腳踵形狀 - - - 高度 - - - 高 - - - 高跟鞋 - - - 高顎 - - - 高平臺 - - - 高而緊緻 - - - 更高 - - - 腰長 - - - 腰寬 - - - 向內 - - - 內陰影顏色 - - - 內陰影不透明度 - - - 眼內角 - - - 內眼陰影 - - - 內陰影 - - - 外套長度 - - - 外套皺褶 - - - 顎角度 - - - 顎突出 - - - 顎形 - - - 加入 - - - 下頜 - - - 膝部角度 - - - 八字腿 - - - 大 - - - 大手掌 - - - 左邊分 - - - 腿長 - - - 腿肌肉 - - - 更少 - - - 體脂肪較少 - - - 絡腮鬍較短 - - - 雀斑較少 - - - 較不飽滿 - - - 較少重力 - - - 少一點愛 - - - 少一點肌肉 - - - 少一點肌壯感 - - - 少一點紅潤 - - - 少一點圓度 - - - 腿靠攏一點 - - - 少一點方形 - - - 少一點量 - - - 少一點靈魂 - - - 亮一點 - - - 脣裂度 - - - 脣裂深度 - - - 脣豐度 - - - 脣色粉紅度 - - - 脣比率 - - - 脣厚度 - - - 脣寬度 - - - 脣蜜 - - - 脣膏 - - - 脣膏顏色 - - - 長 - - - 長臉 - - - 高腰 - - - 長腿 - - - 長頸 - - - 長辮子 - - - 長馬尾辮 - - - 長軀幹 - - - 長臂 - - - 寬鬆褲子 - - - 寬鬆襯衫 - - - 寬鬆袖子 - - - 腰間贅肉 - - - 低 - - - 低跟鞋 - - - 低顎 - - - 低平臺 - - - 低而寬鬆 - - - 降低 - - - 鼻樑低一點 - - - 雙頰低一點 - - - 男性 - - - 中間邊分 - - - 更多 - - - 增加腮紅 - - - 增加體脂肪 - - - 增加絡腮鬍 - - - 增加眼影 - - - 增加雀斑 - - - 更飽滿 - - - 增加重力 - - - 多一點脣膏 - - - 多一點愛 - - - 下脣更豐滿 - - - 多一點肌肉 - - - 增加肌壯感 - - - 更加紅潤 - - - 增加圓度 - - - 腿更加張開 - - - 更加傾斜 - - - 增加方形 - - - 上脣更豐滿 - - - 更加垂直 - - - 多一點量 - - - 多一點靈魂 - - - 髭鬍 - - - 嘴角 - - - 嘴巴位置 - - - 莫霍克髮型 - - - 肌肉發達 - - - 羊排式絡腮鬍 - - - 指甲油 - - - 指甲油顏色 - - - 窄 - - - 後窄 - - - 前窄 - - - 窄脣 - - - 自然 - - - 頸長 - - - 頸部厚度 - - - 無腮紅 - - - 無眼線 - - - 無眼影 - - - 無脣蜜 - - - 無脣膏 - - - 無邊分 - - - 無指甲油 - - - 去紅色 - - - 無尖直形 - - - 去白色 - - - 無皺紋 - - - 正常下半 - - - 正常上半 - - - 左鼻 - - - 右鼻 - - - 鼻部大小 - - - 鼻子厚度 - - - 鼻尖角度 - - - 鼻尖形狀 - - - 鼻寬 - - - 鼻孔分開度 - - - 鼻孔寬 - - - 不透明 - - - 打開 - - - 後開 - - - 前開 - - - 左開 - - - 右開 - - - 橘色 - - - 外 - - - 外陰影顏色 - - - 外陰影不透明度 - - - 眼外角 - - - 外眼陰影 - - - 外陰影 - - - 齙牙 - - - 配套 - - - 美甲 - - - 蒼白 - - - 褲襠 - - - 褲子合身度 - - - 褲長 - - - 褲腰 - - - 褲子皺褶 - - - 邊分 - - - 邊分瀏海 - - - 胸肌 - - - 色素 - - - 辮子 - - - 粉紅 - - - 增加粉紅 - - - 平臺高度 - - - 平臺寬度 - - - 尖狀 - - - 尖狀高跟鞋 - - - 馬尾辮 - - - 蓬蓬裙 - - - 左眼 Pop - - - 右眼 Pop - - - 眼腫 - - - 眼瞼腫 - - - 虹彩 - - - 紅髮 - - - 一般 - - - 右邊分 - - - 臉色紅潤 - - - 圓 - - - 紅潤度 - - - 紅潤 - - - 亂髮 - - - 馬鞍型工具袋 - - - 削瘦的腿 - - - 分開 - - - 淺 - - - 後偏移 - - - 臉部偏移 - - - 前偏移 - - - 左上偏移 - - - 右上偏移 - - - 後偏移 - - - 前偏移 - - - 左移 - - - 嘴部偏移 - - - 右移 - - - 襯衫底 - - - 襯衫合身度 - - - 襯衫皺褶 - - - 鞋高 - - - 短 - - - 短臂 - - - 短腿 - - - 短頸 - - - 短辮子 - - - 短馬尾辮 - - - 短鬢鬚 - - - 短軀幹 - - - 短腰 - - - 肩膀 - - - 側瀏海 - - - 鬢鬚 - - - 側面頭髮 - - - 側面頭髮下部 - - - 側面頭髮上部 - - - 削瘦頸部 - - - 裙子合身度 - - - 裙長 - - - 斜前額 - - - 袖長 - - - 袖子寬鬆度 - - - 後分 - - - 前分 - - - 左分 - - - 右分 - - - 小 - - - 小手 - - - 小頭 - - - 平滑 - - - 平滑頭髮 - - - 襪長 - - - 脣下撮鬍 - - - 稀疏 - - - 尖直頭髮 - - - 方形 - - - 平頭鞋 - - - 長瓜形臉 - - - 延展頭部 - - - 深陷 - - - 胸部內陷 - - - 眼睛深陷 - - - 後垂擺 - - - 前垂擺 - - - 高 - - - 後漸細 - - - 前漸細 - - - 粗腳跟 - - - 粗頸 - - - 粗腳趾 - - - 瘦 - - - 細眉 - - - 細脣 - - - 細鼻 - - - 緊緻下巴 - - - 緊緻袖口 - - - 緊緻褲子 - - - 緊緻襯衫 - - - 緊緻裙子 - - - 緊緻袖子 - - - 腳趾形狀 - - - 腳趾粗細 - - - 軀幹長度 - - - 軀幹肌肉 - - - 軀幹削瘦 - - - 未附著 - - - 無皺痕 - - - 戽斗 - - - 不自然 - - - 上鼻梁 - - - 上臉頰 - - - 上部顎裂 - - - 上眼瞼皺褶 - - - 嘴角上翹 - - - 大紅 - - - 腰高 - - - 飽食的 - - - 白髮 - - - 寬 - - - 後寬 - - - 前寬 - - - 寬脣 - - - 狂野 - - - 皺紋 - - - 添加到我的地標 - - - 編輯我的地標 - - - 察看更多關於目前位置的資訊 - - - 我的位置歷史紀錄 - - - 購買這塊土地 - - - 此地並不允許語音 - - - 不允許飛行 - - - 禁止推撞 - - - 建造/丟棄 物件不被允許 - - - 腳本不被允許 - - - 健康 - - - 完全成人地區 - - - 適度成人地區 - - - 一般普級地區 - - - [APP_NAME] 更新 - - - [APP_NAME] 現更新中... - - - [APP_NAME] 安裝中... - - - 你的 [APP_NAME] Viewer 正在更新為最新發行版。 可能要等一會兒,請耐心稍候。 - - - 更新下載中... - - - 更新下載 - - - 夏載更新失敗 - - - [APP_NAME] 更新時出錯。 請到 www.secondlife.com 下載最新版本。 - - - 安裝更新失敗 - - - 啟動瀏覽器失敗 - - - [APP_NAME]:來自 [FROM_NAME] 的項目速度過快,因此自動預覽暫停 [TIME] 秒 - - - [APP_NAME]:進來的項目速度過快,因此自動預覽暫停 [TIME] 秒 - - - -- 已開啟及時訊息記錄 -- - - - [NAME] 正在輸入... - - - (未命名) - - - (有人主持:預設關閉語音) - - - 這次通話無法使用文字聊天。 - - - 群組主持人已禁止你進行文字聊天。 - - - 點按此處以傳送即時訊息。 - - - 至 - - - (主持人) - - - (於 [LONG_TIMESTAMP] 儲存) - - - 你的通話已經接通 - - - 你發起了語音通話 - - - 你發起了語音通話 - - - [NAME] 發起了語音通話 - - - 加入語音通話... - - - 已接通,要掛斷請按「離開通話」 - - - 離開語音通話 - - - 聯接中... - - - 臨時多方通話 - - - 和 [AGENT_NAME] 多方通話 - - - 收納區物品已發送 - - - 將收納區物品拖曳到這裡 - - - (IM 會話不存在) - - - 這次會話只有你一人。 - - - [NAME] 離線。 - - - 點按 [BUTTON NAME] 按鈕接受並連通到這個語音聊天。 - - - 你已經封鎖這位居民。 發送訊息將會自動解除封鎖。 - - - 送出請求時出錯,請稍候再試。 - - - 送出請求時出錯,請稍候再試。 - - - 你權限不足。 - - - 此會話不再存在 - - - 你並不具有這個能力。 - - - 你並不具有這個能力。 - - - 你不具會話主持人身份。 - - - 某群組主持人禁止了你的文字聊天。 - - - 某群組主持人禁止了你的文字聊天。 - - - 無法新增使用者加入與 [RECIPIENT] 的聊天會話。 - - - 你的訊息無法傳送給與 [RECIPIENT] 的聊天會話。 - - - 你的訊息無法傳送給與 [RECIPIENT] 的聊天會話。 - - - 主持期間發生錯誤。 - - - 你已經由群組中被移除。 - - - 你已經由群組中被移除。 - - - 你已無法進入這個聊天會話。 - - - [SOURCES] 又說了一些話 - - - [SOURCES] 又說了一些話 - - - 會話初始化逾時,無法完成 - - - 我的家位置已定。 - - - http://secondlife.com/landing/voicemorphing - - - [NAME] 支付你 L$[AMOUNT]([REASON])。 - - - [NAME] 支付你 L$[AMOUNT]。 - - - 你支付 L$[AMOUNT] 給 [NAME]([REASON])。 - - - 你支付了 L$[AMOUNT]。 - - - 你支付 L$[AMOUNT] 給 [NAME]。 - - - 你支付了 L$[AMOUNT]([REASON])。 - - - 購買 [ITEM] - - - 購買一土地地段 - - - 購買土地出入通行權 - - - 讓渡土地 - - - 以創造群組 - - - 以加入群組 - - - 以上傳 - - - 發布一則個人廣告 - - - 捐贈 L$ [AMOUNT] - - - 上傳花費 L$ [AMOUNT] - - - 本次花費 L$ [AMOUNT] - - - 花費 L$ [AMOUNT] 購買所選土地 - - - 本物件價格 L$ [AMOUNT] - - - 任何人 - - - 職員 - - - 所有人 - - - 上線 - - - 上傳中... - -違規舉報 - - - 新體形 - - - 新皮膚 - - - 新頭髮 - - - 新眼睛 - - - 新襯衫 - - - 新褲子 - - - 新鞋子 - - - 新襪子 - - - 新外套 - - - 新手套 - - - 新內衣 - - - 新內褲 - - - 新裙子 - - - 新半透明 - - - 新刺青 - - - 新身體物理 - - - 無效的可穿裝扮 - - - 新姿勢 - - - 新腳本 - - - 新記事卡 - - - 新資料夾 - - - 內容 - - - 姿勢 - - - 男性姿勢 - - - 女性姿勢 - - - 其他���勢 - - - 演說姿勢 - - - 一般姿勢 - - - 男性 - Excuse me - - - Male - 給我滾蛋! - - - 男性 - 飛吻 - - - 男性 - 我噓你 - - - 男性 - 無聊狀 - - - 男性 - 嘿! - - - 男性 - 笑 - - - 男性 - 作噁 - - - 男性 - 聳聳肩 - - - 男性 - 吐舌頭 - - - 男性 - 哇塞 - - - 女性 - 咯咯笑 - - - 女性 - 哭 - - - 女性 - 尷尬 - - - 男性 - Excuse me - - - 女性 - 給我滾蛋! - - - 女性 - 飛吻 - - - 女性 - 我噓你 - - - 女性 - 無聊狀 - - - 女性 - 嘿! - - - 女性 - 嘿,寶貝 - - - 女性 - 笑 - - - 女性 - 看我美不美 - - - 女性 - 我在這兒 - - - 女性 - 請 - - - 女性 - 作噁 - - - 女性 - 聳聳肩 - - - 女性 - 吐舌頭 - - - 女性 - 哇塞 - - - [mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt] - - - 無/無 - - - 無法載入圖像大於 [WIDTH]*[HEIGHT] - - - - 儘管我們努力避免,還是發生意料外的錯誤。 - - 請察訪 status.secondlifegrid.net 看是否發生了已知狀況。 - 如果文體繼續發生,請檢查你的網路和防火牆設定。 - - - 星期日:星期一:星期二:星期三:星期四:星期五:星期六 - - - 星期日:星期一:星期二:星期三:星期四:星期五:星期六 - - - 一月:二月:三月:四月:五月:六月:七月:八月:九月:十月:十一月:十二月 - - - 一月:二月:三月:四月:五月:六月:七月:八月:九月:十月:十一月:十二月 - - - [MDAY] - - - 上午 - - - 下午 - - - $ [AMOUNT] 美元 - - - 成員資格 - - - 角色 - - - 群組身份 - - - 地段管理 - - - 地段名 - - - 地段設定 - - - 地段權利 - - - 地段出入 - - - 地段內容 - - - 物件管理 - - - 會計 - - - 通知 - - - 聊天 - - - 刪除所選取的物品? - - - 刪除所選取的物���? - - - 沒有任何物品在這個裝扮內 - - - 選擇一個編輯器使用 ExternalEditor 設定。 - - - 找不到你指定的外部編輯器。 -請嘗試在編輯器路經前後加上英文雙括號。 -(例:"/path to my/editor" "%s") - - - 解析外部編輯器指令時出錯。 - - - 執行外部編輯器失敗。 - - - Esc 鍵 - - - 空間鍵 - - - Enter 鍵 - - - Tab 鍵 - - - Ins 鍵 - - - Del 鍵 - - - Backspace 鍵 - - - Shift 鍵 - - - Ctrl 鍵 - - - Alt 鍵 - - - CapsLock 鍵 - - - 左移鍵 - - - 右移鍵 - - - 上移鍵 - - - 下移鍵 - - - Home 鍵 - - - End 鍵 - - - PgUp 鍵 - - - PgDn 鍵 - - - F1 - - - F2 - - - F3 - - - F4 - - - F5 - - - F6 - - - F7 - - - F8 - - - F9 - - - F10 - - - F11 - - - F12 - - - 添加 - - - 減 - - - 乘 - - - 除 - - - PAD_DIVIDE - - - PAD_LEFT - - - PAD_RIGHT - - - PAD_DOWN - - - PAD_UP - - - PAD_HOME - - - PAD_END - - - PAD_PGUP - - - PAD_PGDN - - - PAD_CENTER - - - PAD_INS - - - PAD_DEL - - - PAD_Enter - - - PAD_BUTTON0 - - - PAD_BUTTON1 - - - PAD_BUTTON2 - - - PAD_BUTTON3 - - - PAD_BUTTON4 - - - PAD_BUTTON5 - - - PAD_BUTTON6 - - - PAD_BUTTON7 - - - PAD_BUTTON8 - - - PAD_BUTTON9 - - - PAD_BUTTON10 - - - PAD_BUTTON11 - - - PAD_BUTTON12 - - - PAD_BUTTON13 - - - PAD_BUTTON14 - - - PAD_BUTTON15 - - - - - - - = - - - ` - - - ; - - - [ - - - ] - - - \ - - - 0 - - - 1 - - - 2 - - - 3 - - - 4 - - - 5 - - - 6 - - - 7 - - - 8 - - - 9 - - - A - - - B - - - C - - - D - - - E - - - F - - - G - - - H - - - I - - - J - - - K - - - L - - - M - - - 北 - - - O - - - P - - - Q - - - R - - - 南 - - - T - - - U - - - V - - - 西 - - - X - - - Y - - - Z - - - 檢視粒子效果的導引(藍色) - - - 檢視具體物件的導引(綠色) - - - 檢視腳本物件的導引(紅色) - - - 檢視帶有觸摸函式腳本物件的導引(紅色) - - - 檢視聲音的導引(黃色) - - - 檢視媒體的導引(白色) - - - 隱藏粒子效果 - - -- cgit v1.2.3 From 50c633bf3502c64ec486f62e9b3f55b411dee4b4 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 16 Oct 2012 21:10:54 -0400 Subject: Reference new "[sourceid]" substitution in "create_account_url". join.secondlife.com needs to know the sourceid as well as the user's language of choice. Ensure that sourceid gets passed with the URL. --- indra/newview/skins/default/xui/de/strings.xml | 2 +- indra/newview/skins/default/xui/en/strings.xml | 2 +- indra/newview/skins/default/xui/es/strings.xml | 2 +- indra/newview/skins/default/xui/fr/strings.xml | 2 +- indra/newview/skins/default/xui/it/strings.xml | 2 +- indra/newview/skins/default/xui/ja/strings.xml | 2 +- indra/newview/skins/default/xui/pt/strings.xml | 2 +- indra/newview/skins/default/xui/ru/strings.xml | 2 +- indra/newview/skins/default/xui/tr/strings.xml | 2 +- indra/newview/skins/default/xui/zh/strings.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml index 206b631874..07988a5129 100644 --- a/indra/newview/skins/default/xui/de/strings.xml +++ b/indra/newview/skins/default/xui/de/strings.xml @@ -137,7 +137,7 @@ Beenden - http://join.secondlife.com/index.php?lang=de-DE + http://join.secondlife.com/index.php?lang=de-DE&sourceid=[sourceid] Mit dem von Ihnen verwendeten Viewer ist der Zugriff auf Second Life nicht mehr möglich. Laden Sie von den folgenden Seite einen neuen Viewer herunter: diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 15eb46c348..2157a05bbf 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -59,7 +59,7 @@ Network error: Could not establish connection, please check your network connection. Login failed. Quit - http://join.secondlife.com/ + http://join.secondlife.com/?sourceid=[sourceid] The viewer you are using can no longer access Second Life. Please visit the following page to download a new viewer: diff --git a/indra/newview/skins/default/xui/es/strings.xml b/indra/newview/skins/default/xui/es/strings.xml index d4698b8cb3..f1eecdeba8 100644 --- a/indra/newview/skins/default/xui/es/strings.xml +++ b/indra/newview/skins/default/xui/es/strings.xml @@ -128,7 +128,7 @@ Salir - http://join.secondlife.com/index.php?lang=es-ES + http://join.secondlife.com/index.php?lang=es-ES&sourceid=[sourceid] Ya no puedes acceder a Second Life con el visor que estás utilizando. Visita la siguiente página para descargar un nuevo visor: diff --git a/indra/newview/skins/default/xui/fr/strings.xml b/indra/newview/skins/default/xui/fr/strings.xml index 6dc1439593..3b0a7c52ba 100644 --- a/indra/newview/skins/default/xui/fr/strings.xml +++ b/indra/newview/skins/default/xui/fr/strings.xml @@ -137,7 +137,7 @@ Quitter - http://join.secondlife.com/index.php?lang=fr-FR + http://join.secondlife.com/index.php?lang=fr-FR&sourceid=[sourceid] Le client que vous utilisez ne permet plus d'accéder à Second Life. Téléchargez un nouveau client à la page suivante : diff --git a/indra/newview/skins/default/xui/it/strings.xml b/indra/newview/skins/default/xui/it/strings.xml index f2afcafbea..e030df0ff4 100644 --- a/indra/newview/skins/default/xui/it/strings.xml +++ b/indra/newview/skins/default/xui/it/strings.xml @@ -134,7 +134,7 @@ Esci - http://join.secondlife.com/index.php?lang=it-IT + http://join.secondlife.com/index.php?lang=it-IT&sourceid=[sourceid] Il viewer utilizzato non è più in grado di accedere a Second Life. Visita la parina seguente per scaricare un nuovo viewer: diff --git a/indra/newview/skins/default/xui/ja/strings.xml b/indra/newview/skins/default/xui/ja/strings.xml index 7c3f9489da..e1efa4f7d5 100644 --- a/indra/newview/skins/default/xui/ja/strings.xml +++ b/indra/newview/skins/default/xui/ja/strings.xml @@ -137,7 +137,7 @@ 終了 - http://join.secondlife.com/index.php?lang=ja-JP + http://join.secondlife.com/index.php?lang=ja-JP&sourceid=[sourceid] お使いの古いビューワでは Second Life にアクセスできません。以下のページから新しいビューワをダウンロードしてください: diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml index a7234bdc14..262483d2c0 100644 --- a/indra/newview/skins/default/xui/pt/strings.xml +++ b/indra/newview/skins/default/xui/pt/strings.xml @@ -128,7 +128,7 @@ Sair - http://join.secondlife.com/index.php?lang=pt-BR + http://join.secondlife.com/index.php?lang=pt-BR&sourceid=[sourceid] O visualizador utilizado já não é compatível com o Second Life. Visite a página abaixo para baixar uma versão atual: http://secondlife.com/download diff --git a/indra/newview/skins/default/xui/ru/strings.xml b/indra/newview/skins/default/xui/ru/strings.xml index 3745aff8fd..7e254ec1a2 100644 --- a/indra/newview/skins/default/xui/ru/strings.xml +++ b/indra/newview/skins/default/xui/ru/strings.xml @@ -137,7 +137,7 @@ Выйти - http://join.secondlife.com/index.php?lang=ru-RU + http://join.secondlife.com/index.php?lang=ru-RU&sourceid=[sourceid] У клиента, которым вы пользуетесь, больше нет доступа к игре Second Life. Загрузить новую версию клиента можно по адресу diff --git a/indra/newview/skins/default/xui/tr/strings.xml b/indra/newview/skins/default/xui/tr/strings.xml index eed65f8c75..06e7e1ca26 100644 --- a/indra/newview/skins/default/xui/tr/strings.xml +++ b/indra/newview/skins/default/xui/tr/strings.xml @@ -137,7 +137,7 @@ Çık - http://join.secondlife.com/index.php?lang=tr-TR + http://join.secondlife.com/index.php?lang=tr-TR&sourceid=[sourceid] Kullandığınız görüntüleyici ile artık Second Life'a erişemezsiniz. Yeni bir görüntüleyiciyi karşıdan yüklemek için lütfen şu sayfayı ziyaret edin: diff --git a/indra/newview/skins/default/xui/zh/strings.xml b/indra/newview/skins/default/xui/zh/strings.xml index 9114cc079d..ff6f3935f7 100644 --- a/indra/newview/skins/default/xui/zh/strings.xml +++ b/indra/newview/skins/default/xui/zh/strings.xml @@ -128,7 +128,7 @@ 結束退出 - http://join.secondlife.com/ + http://join.secondlife.com/?sourceid=[sourceid] 你目前所用的 Viewer 已經無法再進入第二人生。 請到這個頁面下載最新 Viewer: -- cgit v1.2.3 From ad4102db1b0b6730d6d1fe7119cab2149bb48e2c Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 16 Oct 2012 21:13:25 -0400 Subject: LLPanelLogin::loadLoginPage() can now use "sourceid" settings var. Previously we borrowed the sourceid= param value from create_account_url, which we "happened to know" was overridden with the sourceid of interest. Now that we have a settings variable that directly captures sourceid, though, much more straightforward to use that. --- indra/newview/llpanellogin.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index c6bcaeab07..58e9a6b342 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -771,13 +771,8 @@ void LLPanelLogin::loadLoginPage() // add OS info params["os"] = LLAppViewer::instance()->getOSInfo().getOSStringSimple(); - // sourceid: create_account_url's sourceid= varies by skin - LLURI create_account_url(LLTrans::getString("create_account_url")); - LLSD create_account_params(create_account_url.queryMap()); - if (create_account_params.has("sourceid")) - { - params["sourceid"] = create_account_params["sourceid"]; - } + // sourceid + params["sourceid"] = gSavedSettings.getString("sourceid"); // Make an LLURI with this augmented info LLURI login_uri(LLURI::buildHTTP(login_page.authority(), -- cgit v1.2.3 From 8dbd1f5f48d5af605f73558091b2bab554ba49c2 Mon Sep 17 00:00:00 2001 From: William Todd Stinson Date: Tue, 16 Oct 2012 18:45:37 -0700 Subject: MAINT-1737: Adding a menu option to the Pathfinding menu to allow rebuilding of the region's navmesh. --- indra/newview/llagent.cpp | 3 - .../llmenuoptionpathfindingrebakenavmesh.cpp | 227 +++++++++------------ .../newview/llmenuoptionpathfindingrebakenavmesh.h | 49 ++--- indra/newview/llviewermenu.cpp | 51 +++++ indra/newview/llviewerwindow.cpp | 7 +- .../skins/default/xui/de/panel_navmesh_rebake.xml | 6 - indra/newview/skins/default/xui/en/menu_viewer.xml | 10 + .../newview/skins/default/xui/en/notifications.xml | 20 -- .../skins/default/xui/en/panel_navmesh_rebake.xml | 45 ---- .../skins/default/xui/es/panel_navmesh_rebake.xml | 6 - .../skins/default/xui/fr/panel_navmesh_rebake.xml | 6 - .../skins/default/xui/it/panel_navmesh_rebake.xml | 6 - .../skins/default/xui/ja/panel_navmesh_rebake.xml | 6 - .../skins/default/xui/pt/panel_navmesh_rebake.xml | 6 - .../skins/default/xui/ru/panel_navmesh_rebake.xml | 6 - .../skins/default/xui/tr/panel_navmesh_rebake.xml | 6 - 16 files changed, 182 insertions(+), 278 deletions(-) delete mode 100644 indra/newview/skins/default/xui/de/panel_navmesh_rebake.xml delete mode 100644 indra/newview/skins/default/xui/en/panel_navmesh_rebake.xml delete mode 100644 indra/newview/skins/default/xui/es/panel_navmesh_rebake.xml delete mode 100644 indra/newview/skins/default/xui/fr/panel_navmesh_rebake.xml delete mode 100644 indra/newview/skins/default/xui/it/panel_navmesh_rebake.xml delete mode 100644 indra/newview/skins/default/xui/ja/panel_navmesh_rebake.xml delete mode 100644 indra/newview/skins/default/xui/pt/panel_navmesh_rebake.xml delete mode 100644 indra/newview/skins/default/xui/ru/panel_navmesh_rebake.xml delete mode 100644 indra/newview/skins/default/xui/tr/panel_navmesh_rebake.xml diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 6785803f03..4782a71ba8 100755 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -51,7 +51,6 @@ #include "llhomelocationresponder.h" #include "llhudmanager.h" #include "lljoystickbutton.h" -#include "llmenuoptionpathfindingrebakenavmesh.h" #include "llmorphview.h" #include "llmoveview.h" #include "llnavigationbar.h" // to show/hide navigation bar when changing mouse look state @@ -2002,7 +2001,6 @@ void LLAgent::endAnimationUpdateUI() LLChicletBar::getInstance()->setVisible(TRUE); LLPanelStandStopFlying::getInstance()->setVisible(TRUE); - LLPanelPathfindingRebakeNavmesh::getInstance()->setVisible(TRUE); LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset); @@ -2112,7 +2110,6 @@ void LLAgent::endAnimationUpdateUI() LLChicletBar::getInstance()->setVisible(FALSE); LLPanelStandStopFlying::getInstance()->setVisible(FALSE); - LLPanelPathfindingRebakeNavmesh::getInstance()->setVisible(FALSE); // clear out camera lag effect gAgentCamera.clearCameraLag(); diff --git a/indra/newview/llmenuoptionpathfindingrebakenavmesh.cpp b/indra/newview/llmenuoptionpathfindingrebakenavmesh.cpp index 3c89958f7e..71062154f7 100644 --- a/indra/newview/llmenuoptionpathfindingrebakenavmesh.cpp +++ b/indra/newview/llmenuoptionpathfindingrebakenavmesh.cpp @@ -34,148 +34,147 @@ #include #include "llagent.h" -#include "llbutton.h" #include "llenvmanager.h" -#include "llhints.h" #include "llnotificationsutil.h" -#include "llpanel.h" #include "llpathfindingmanager.h" #include "llpathfindingnavmesh.h" #include "llpathfindingnavmeshstatus.h" -#include "lltoolbar.h" -#include "lltoolbarview.h" -#include "lltooltip.h" #include "llviewerregion.h" -LLPanelPathfindingRebakeNavmesh* LLPanelPathfindingRebakeNavmesh::getInstance() +LLMenuOptionPathfindingRebakeNavmesh::LLMenuOptionPathfindingRebakeNavmesh() + : LLSingleton(), + mIsInitialized(false), + mCanRebakeRegion(false), + mRebakeNavMeshMode(kRebakeNavMesh_Default), + mNavMeshSlot(), + mRegionCrossingSlot(), + mAgentStateSlot() { - static LLPanelPathfindingRebakeNavmesh* panel = getPanel(); - return panel; } -BOOL LLPanelPathfindingRebakeNavmesh::postBuild() +LLMenuOptionPathfindingRebakeNavmesh::~LLMenuOptionPathfindingRebakeNavmesh() { - //Rebake button - mNavMeshRebakeButton = findChild("navmesh_btn"); - llassert(mNavMeshRebakeButton != NULL); - mNavMeshRebakeButton->setCommitCallback(boost::bind(&LLPanelPathfindingRebakeNavmesh::onNavMeshRebakeClick, this)); - LLHints::registerHintTarget("navmesh_btn", mNavMeshRebakeButton->getHandle()); - - //Sending rebake request - mNavMeshSendingButton = findChild("navmesh_btn_sending"); - llassert(mNavMeshSendingButton != NULL); - LLHints::registerHintTarget("navmesh_btn_sending", mNavMeshSendingButton->getHandle()); - - //rebaking... - mNavMeshBakingButton = findChild("navmesh_btn_baking"); - llassert(mNavMeshBakingButton != NULL); - LLHints::registerHintTarget("navmesh_btn_baking", mNavMeshBakingButton->getHandle()); - - setMode(kRebakeNavMesh_Default); - - createNavMeshStatusListenerForCurrentRegion(); - - if ( !mRegionCrossingSlot.connected() ) + if (getMode() == kRebakeNavMesh_RequestSent) { - mRegionCrossingSlot = LLEnvManagerNew::getInstance()->setRegionChangeCallback(boost::bind(&LLPanelPathfindingRebakeNavmesh::handleRegionBoundaryCrossed, this)); + LL_WARNS("navmeshRebaking") << "During destruction of the LLMenuOptionPathfindingRebakeNavmesh " + << "singleton, the mode indicates that a request has been sent for which a response has yet " + << "to be received. This could contribute to a crash on exit." << LL_ENDL; } - if (!mAgentStateSlot.connected()) + llassert(!mIsInitialized); + if (mIsInitialized) { - mAgentStateSlot = LLPathfindingManager::getInstance()->registerAgentStateListener(boost::bind(&LLPanelPathfindingRebakeNavmesh::handleAgentState, this, _1)); + quit(); } - LLPathfindingManager::getInstance()->requestGetAgentState(); - - return LLPanel::postBuild(); } -void LLPanelPathfindingRebakeNavmesh::draw() +void LLMenuOptionPathfindingRebakeNavmesh::initialize() { - if (doDraw()) + llassert(!mIsInitialized); + if (!mIsInitialized) { - updatePosition(); - LLPanel::draw(); + mIsInitialized = true; + + setMode(kRebakeNavMesh_Default); + + createNavMeshStatusListenerForCurrentRegion(); + + if ( !mRegionCrossingSlot.connected() ) + { + mRegionCrossingSlot = LLEnvManagerNew::getInstance()->setRegionChangeCallback(boost::bind(&LLMenuOptionPathfindingRebakeNavmesh::handleRegionBoundaryCrossed, this)); + } + + if (!mAgentStateSlot.connected()) + { + mAgentStateSlot = LLPathfindingManager::getInstance()->registerAgentStateListener(boost::bind(&LLMenuOptionPathfindingRebakeNavmesh::handleAgentState, this, _1)); + } + LLPathfindingManager::getInstance()->requestGetAgentState(); } } -BOOL LLPanelPathfindingRebakeNavmesh::handleToolTip( S32 x, S32 y, MASK mask ) +void LLMenuOptionPathfindingRebakeNavmesh::quit() { - LLToolTipMgr::instance().unblockToolTips(); - - if (mNavMeshRebakeButton->getVisible()) + llassert(mIsInitialized); + if (mIsInitialized) { - LLToolTipMgr::instance().show(mNavMeshRebakeButton->getToolTip()); - } - else if (mNavMeshSendingButton->getVisible()) - { - LLToolTipMgr::instance().show(mNavMeshSendingButton->getToolTip()); - } - else if (mNavMeshBakingButton->getVisible()) - { - LLToolTipMgr::instance().show(mNavMeshBakingButton->getToolTip()); - } + if (mNavMeshSlot.connected()) + { + mNavMeshSlot.disconnect(); + } - return LLPanel::handleToolTip(x, y, mask); -} + if (mRegionCrossingSlot.connected()) + { + mRegionCrossingSlot.disconnect(); + } -LLPanelPathfindingRebakeNavmesh::LLPanelPathfindingRebakeNavmesh() - : LLPanel(), - mCanRebakeRegion(FALSE), - mRebakeNavMeshMode(kRebakeNavMesh_Default), - mNavMeshRebakeButton(NULL), - mNavMeshSendingButton(NULL), - mNavMeshBakingButton(NULL), - mNavMeshSlot(), - mRegionCrossingSlot(), - mAgentStateSlot() -{ - // make sure we have the only instance of this class - static bool b = true; - llassert_always(b); - b=false; -} + if (mAgentStateSlot.connected()) + { + mAgentStateSlot.disconnect(); + } -LLPanelPathfindingRebakeNavmesh::~LLPanelPathfindingRebakeNavmesh() -{ + mIsInitialized = false; + } } -LLPanelPathfindingRebakeNavmesh* LLPanelPathfindingRebakeNavmesh::getPanel() +bool LLMenuOptionPathfindingRebakeNavmesh::canRebakeRegion() const { - LLPanelPathfindingRebakeNavmesh* panel = new LLPanelPathfindingRebakeNavmesh(); - panel->buildFromFile("panel_navmesh_rebake.xml"); - return panel; + if (!mIsInitialized) + { + LL_ERRS("navmeshRebaking") << "LLMenuOptionPathfindingRebakeNavmesh class has not been initialized " + << "when the ability to rebake navmesh is being requested." << LL_ENDL; + } + return mCanRebakeRegion; } -void LLPanelPathfindingRebakeNavmesh::setMode(ERebakeNavMeshMode pRebakeNavMeshMode) +LLMenuOptionPathfindingRebakeNavmesh::ERebakeNavMeshMode LLMenuOptionPathfindingRebakeNavmesh::getMode() const { - if (pRebakeNavMeshMode == kRebakeNavMesh_Available) + if (!mIsInitialized) { - LLNotificationsUtil::add("PathfindingRebakeNavmesh"); + LL_ERRS("navmeshRebaking") << "LLMenuOptionPathfindingRebakeNavmesh class has not been initialized " + << "when the mode is being requested." << LL_ENDL; } - mNavMeshRebakeButton->setVisible(pRebakeNavMeshMode == kRebakeNavMesh_Available); - mNavMeshSendingButton->setVisible(pRebakeNavMeshMode == kRebakeNavMesh_RequestSent); - mNavMeshBakingButton->setVisible(pRebakeNavMeshMode == kRebakeNavMesh_InProgress); - mRebakeNavMeshMode = pRebakeNavMeshMode; + return mRebakeNavMeshMode; } -LLPanelPathfindingRebakeNavmesh::ERebakeNavMeshMode LLPanelPathfindingRebakeNavmesh::getMode() const +void LLMenuOptionPathfindingRebakeNavmesh::sendRequestRebakeNavmesh() { - return mRebakeNavMeshMode; + if (!mIsInitialized) + { + LL_ERRS("navmeshRebaking") << "LLMenuOptionPathfindingRebakeNavmesh class has not been initialized " + << "when the request is being made to rebake the navmesh." << LL_ENDL; + } + else + { + if (!canRebakeRegion()) + { + LL_WARNS("navmeshRebaking") << "attempting to rebake navmesh when user does not have permissions " + << "on this region" << LL_ENDL; + } + if (getMode() != kRebakeNavMesh_Available) + { + LL_WARNS("navmeshRebaking") << "attempting to rebake navmesh when mode is not available" + << LL_ENDL; + } + + setMode(kRebakeNavMesh_RequestSent); + LLPathfindingManager::getInstance()->requestRebakeNavMesh(boost::bind(&LLMenuOptionPathfindingRebakeNavmesh::handleRebakeNavMeshResponse, this, _1)); + } } -void LLPanelPathfindingRebakeNavmesh::onNavMeshRebakeClick() +void LLMenuOptionPathfindingRebakeNavmesh::setMode(ERebakeNavMeshMode pRebakeNavMeshMode) { - setMode(kRebakeNavMesh_RequestSent); - LLPathfindingManager::getInstance()->requestRebakeNavMesh(boost::bind(&LLPanelPathfindingRebakeNavmesh::handleRebakeNavMeshResponse, this, _1)); + mRebakeNavMeshMode = pRebakeNavMeshMode; } -void LLPanelPathfindingRebakeNavmesh::handleAgentState(BOOL pCanRebakeRegion) +void LLMenuOptionPathfindingRebakeNavmesh::handleAgentState(BOOL pCanRebakeRegion) { + llassert(mIsInitialized); mCanRebakeRegion = pCanRebakeRegion; } -void LLPanelPathfindingRebakeNavmesh::handleRebakeNavMeshResponse(bool pResponseStatus) +void LLMenuOptionPathfindingRebakeNavmesh::handleRebakeNavMeshResponse(bool pResponseStatus) { + llassert(mIsInitialized); if (getMode() == kRebakeNavMesh_RequestSent) { setMode(pResponseStatus ? kRebakeNavMesh_InProgress : kRebakeNavMesh_Default); @@ -187,8 +186,9 @@ void LLPanelPathfindingRebakeNavmesh::handleRebakeNavMeshResponse(bool pResponse } } -void LLPanelPathfindingRebakeNavmesh::handleNavMeshStatus(const LLPathfindingNavMeshStatus &pNavMeshStatus) +void LLMenuOptionPathfindingRebakeNavmesh::handleNavMeshStatus(const LLPathfindingNavMeshStatus &pNavMeshStatus) { + llassert(mIsInitialized); ERebakeNavMeshMode rebakeNavMeshMode = kRebakeNavMesh_Default; if (pNavMeshStatus.isValid()) { @@ -214,14 +214,15 @@ void LLPanelPathfindingRebakeNavmesh::handleNavMeshStatus(const LLPathfindingNav setMode(rebakeNavMeshMode); } -void LLPanelPathfindingRebakeNavmesh::handleRegionBoundaryCrossed() +void LLMenuOptionPathfindingRebakeNavmesh::handleRegionBoundaryCrossed() { + llassert(mIsInitialized); createNavMeshStatusListenerForCurrentRegion(); mCanRebakeRegion = FALSE; LLPathfindingManager::getInstance()->requestGetAgentState(); } -void LLPanelPathfindingRebakeNavmesh::createNavMeshStatusListenerForCurrentRegion() +void LLMenuOptionPathfindingRebakeNavmesh::createNavMeshStatusListenerForCurrentRegion() { if (mNavMeshSlot.connected()) { @@ -231,39 +232,7 @@ void LLPanelPathfindingRebakeNavmesh::createNavMeshStatusListenerForCurrentRegio LLViewerRegion *currentRegion = gAgent.getRegion(); if (currentRegion != NULL) { - mNavMeshSlot = LLPathfindingManager::getInstance()->registerNavMeshListenerForRegion(currentRegion, boost::bind(&LLPanelPathfindingRebakeNavmesh::handleNavMeshStatus, this, _2)); + mNavMeshSlot = LLPathfindingManager::getInstance()->registerNavMeshListenerForRegion(currentRegion, boost::bind(&LLMenuOptionPathfindingRebakeNavmesh::handleNavMeshStatus, this, _2)); LLPathfindingManager::getInstance()->requestGetNavMeshForRegion(currentRegion, true); } } - -bool LLPanelPathfindingRebakeNavmesh::doDraw() const -{ - return (mCanRebakeRegion && (mRebakeNavMeshMode != kRebakeNavMesh_NotAvailable)); -} - -void LLPanelPathfindingRebakeNavmesh::updatePosition() -{ - S32 y_pos = 0; - S32 bottom_tb_center = 0; - - if (LLToolBar* toolbar_bottom = gToolBarView->getChild("toolbar_bottom")) - { - y_pos = toolbar_bottom->getRect().getHeight(); - bottom_tb_center = toolbar_bottom->getRect().getCenterX(); - } - - S32 left_tb_width = 0; - if (LLToolBar* toolbar_left = gToolBarView->getChild("toolbar_left")) - { - left_tb_width = toolbar_left->getRect().getWidth(); - } - - if(LLPanel* panel_ssf_container = getRootView()->getChild("state_management_buttons_container")) - { - panel_ssf_container->setOrigin(0, y_pos); - } - - S32 x_pos = bottom_tb_center-getRect().getWidth()/2 - left_tb_width + 113 /* width of stand/fly button */ + 10 /* margin */; - - setOrigin( x_pos, 0); -} diff --git a/indra/newview/llmenuoptionpathfindingrebakenavmesh.h b/indra/newview/llmenuoptionpathfindingrebakenavmesh.h index e41ae9cae0..7b1d2873ba 100644 --- a/indra/newview/llmenuoptionpathfindingrebakenavmesh.h +++ b/indra/newview/llmenuoptionpathfindingrebakenavmesh.h @@ -24,34 +24,22 @@ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ -#ifndef LL_LLPANELPATHFINDINGREBAKENAVMESH_H -#define LL_LLPANELPATHFINDINGREBAKENAVMESH_H +#ifndef LL_LLMENUOPTIONPATHFINDINGREBAKENAVMESH_H +#define LL_LLMENUOPTIONPATHFINDINGREBAKENAVMESH_H #include -#include "llpanel.h" #include "llpathfindingmanager.h" #include "llpathfindingnavmesh.h" +#include "llsingleton.h" -class LLButton; class LLPathfindingNavMeshStatus; -class LLPanelPathfindingRebakeNavmesh : public LLPanel +class LLMenuOptionPathfindingRebakeNavmesh : public LLSingleton { - - LOG_CLASS(LLPanelPathfindingRebakeNavmesh); + LOG_CLASS(LLMenuOptionPathfindingRebakeNavmesh); public: - static LLPanelPathfindingRebakeNavmesh* getInstance(); - - virtual BOOL postBuild(); - - virtual void draw(); - virtual BOOL handleToolTip( S32 x, S32 y, MASK mask ); - -protected: - -private: typedef enum { kRebakeNavMesh_Available, @@ -61,15 +49,21 @@ private: kRebakeNavMesh_Default = kRebakeNavMesh_NotAvailable } ERebakeNavMeshMode; - LLPanelPathfindingRebakeNavmesh(); - virtual ~LLPanelPathfindingRebakeNavmesh(); + LLMenuOptionPathfindingRebakeNavmesh(); + virtual ~LLMenuOptionPathfindingRebakeNavmesh(); - static LLPanelPathfindingRebakeNavmesh* getPanel(); + void initialize(); + void quit(); - void setMode(ERebakeNavMeshMode pRebakeNavMeshMode); + bool canRebakeRegion() const; ERebakeNavMeshMode getMode() const; - void onNavMeshRebakeClick(); + void sendRequestRebakeNavmesh(); + +protected: + +private: + void setMode(ERebakeNavMeshMode pRebakeNavMeshMode); void handleAgentState(BOOL pCanRebakeRegion); void handleRebakeNavMeshResponse(bool pResponseStatus); @@ -78,19 +72,14 @@ private: void createNavMeshStatusListenerForCurrentRegion(); - bool doDraw() const; - void updatePosition(); + bool mIsInitialized; - BOOL mCanRebakeRegion; + bool mCanRebakeRegion; ERebakeNavMeshMode mRebakeNavMeshMode; - LLButton* mNavMeshRebakeButton; - LLButton* mNavMeshSendingButton; - LLButton* mNavMeshBakingButton; - LLPathfindingNavMesh::navmesh_slot_t mNavMeshSlot; boost::signals2::connection mRegionCrossingSlot; LLPathfindingManager::agent_state_slot_t mAgentStateSlot; }; -#endif // LL_LLPANELPATHFINDINGREBAKENAVMESH_H +#endif // LL_LLMENUOPTIONPATHFINDINGREBAKENAVMESH_H diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index d11e7e32c7..66b09d8eb8 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -83,6 +83,7 @@ #include "llinventoryfunctions.h" #include "llpanellogin.h" #include "llpanelblockedlist.h" +#include "llmenuoptionpathfindingrebakenavmesh.h" #include "llmoveview.h" #include "llparcel.h" #include "llrootview.h" @@ -4876,6 +4877,53 @@ class LLToolsEnablePathfindingView : public view_listener_t } }; +class LLToolsDoPathfindingRebakeRegion : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool hasPathfinding = (LLPathfindingManager::getInstance() != NULL); + + if (hasPathfinding) + { + LLMenuOptionPathfindingRebakeNavmesh::getInstance()->sendRequestRebakeNavmesh(); + } + + return hasPathfinding; + } +}; + +class LLToolsEnablePathfindingRebakeRegion : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool returnValue = false; + + if (LLPathfindingManager::getInstance() != NULL) + { + LLMenuOptionPathfindingRebakeNavmesh *rebakeInstance = LLMenuOptionPathfindingRebakeNavmesh::getInstance(); + returnValue = (rebakeInstance->canRebakeRegion() && + (rebakeInstance->getMode() == LLMenuOptionPathfindingRebakeNavmesh::kRebakeNavMesh_Available)); + } + return returnValue; + } +}; + +class LLToolsVisiblePathfindingRebakeRegion : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool returnValue = false; + + if (LLPathfindingManager::getInstance() != NULL) + { + LLMenuOptionPathfindingRebakeNavmesh *rebakeInstance = LLMenuOptionPathfindingRebakeNavmesh::getInstance(); + returnValue = (rebakeInstance->canRebakeRegion() && + (rebakeInstance->getMode() != LLMenuOptionPathfindingRebakeNavmesh::kRebakeNavMesh_NotAvailable)); + } + return returnValue; + } +}; + // Round the position of all root objects to the grid class LLToolsSnapObjectXY : public view_listener_t { @@ -8359,6 +8407,9 @@ void initialize_menus() view_listener_t::addMenu(new LLToolsEnablePathfinding(), "Tools.EnablePathfinding"); view_listener_t::addMenu(new LLToolsEnablePathfindingView(), "Tools.EnablePathfindingView"); + view_listener_t::addMenu(new LLToolsDoPathfindingRebakeRegion(), "Tools.DoPathfindingRebakeRegion"); + view_listener_t::addMenu(new LLToolsEnablePathfindingRebakeRegion(), "Tools.EnablePathfindingRebakeRegion"); + view_listener_t::addMenu(new LLToolsVisiblePathfindingRebakeRegion(), "Tools.VisiblePathfindingRebakeRegion"); // Help menu // most items use the ShowFloater method diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index ec45a507e8..1605a910c9 100755 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1931,11 +1931,10 @@ void LLViewerWindow::initWorldUI() LLPanelStandStopFlying* panel_stand_stop_flying = LLPanelStandStopFlying::getInstance(); panel_ssf_container->addChild(panel_stand_stop_flying); - LLPanelPathfindingRebakeNavmesh *panel_rebake_navmesh = LLPanelPathfindingRebakeNavmesh::getInstance(); - panel_ssf_container->addChild(panel_rebake_navmesh); - panel_ssf_container->setVisible(TRUE); + LLMenuOptionPathfindingRebakeNavmesh::getInstance()->initialize(); + // Load and make the toolbars visible // Note: we need to load the toolbars only *after* the user is logged in and IW if (gToolBarView) @@ -2005,6 +2004,8 @@ void LLViewerWindow::shutdownViews() mRootView = NULL; llinfos << "RootView deleted." << llendl ; + LLMenuOptionPathfindingRebakeNavmesh::getInstance()->quit(); + // Automatically deleted as children of mRootView. Fix the globals. gStatusBar = NULL; gIMMgr = NULL; diff --git a/indra/newview/skins/default/xui/de/panel_navmesh_rebake.xml b/indra/newview/skins/default/xui/de/panel_navmesh_rebake.xml deleted file mode 100644 index 44be6d67b1..0000000000 --- a/indra/newview/skins/default/xui/de/panel_navmesh_rebake.xml +++ /dev/null @@ -1,6 +0,0 @@ - - -