svn merge -r74808:74832 svn+ssh://svn/svn/linden/branches/qa-dpo-9a

master
Christian Goetze 2007-12-01 01:29:28 +00:00
parent 5c53c187ff
commit 7b7dd4e6f6
16 changed files with 577 additions and 89 deletions

View File

@ -362,7 +362,7 @@
<boolean>false</boolean>
</map>
<key>avatarpickrequest</key>
<key>avatarpicksrequest</key>
<map>
<key>service_name</key>
<string>avatar-pick</string>

View File

@ -212,6 +212,10 @@ class UUID(object):
_int2binstr(v3,4) + \
_int2binstr(v4,4)
# module-level null constant
NULL = UUID()
def printTranslatedMemory(four_hex_uints):
"""
We expect to get the string as four hex units. eg:

View File

@ -97,7 +97,7 @@ class ConnectionPool(Pool):
except (AttributeError, DeadProcess), e:
conn = self.create()
# TODO figure out if we're still connected to the database
if conn:
if conn is not None:
Pool.put(self, conn)
else:
self.current_size -= 1

View File

@ -28,20 +28,33 @@ THE SOFTWARE.
$/LicenseInfo$
"""
import errno
import MySQLdb
import MySQLdb.cursors
import os
import os.path
import re
import time
#import sys # *TODO: remove. only used in testing.
#import pprint # *TODO: remove. only used in testing.
try:
set = set
except NameError:
from sets import Set as set
from indra.base import llsd
from indra.base import config
from indra.ipc import russ
_g_named_manager = None
# this function is entirely intended for testing purposes,
# because it's tricky to control the config from inside a test
def _init_g_named_manager(sql_dir = None):
"""Initializes a global NamedManager object to point at a
specified named queries hierarchy.
This function is intended entirely for testing purposes,
because it's tricky to control the config from inside a test."""
if sql_dir is None:
sql_dir = config.get('named-query-base-dir')
global _g_named_manager
@ -49,14 +62,14 @@ def _init_g_named_manager(sql_dir = None):
os.path.abspath(os.path.realpath(sql_dir)))
def get(name):
"@brief get the named query object to be used to perform queries"
"Get the named query object to be used to perform queries"
if _g_named_manager is None:
_init_g_named_manager()
return _g_named_manager.get(name)
def sql(name, params):
def sql(connection, name, params):
# use module-global NamedQuery object to perform default substitution
return get(name).sql(params)
return get(name).sql(connection, params)
def run(connection, name, params, expect_rows = None):
"""\
@ -72,66 +85,243 @@ Note that this function will fetch ALL rows.
return get(name).run(connection, params, expect_rows)
class ExpectationFailed(Exception):
""" Exception that is raised when an expectation for an sql query
is not met."""
def __init__(self, message):
Exception.__init__(self, message)
self.message = message
class NamedQuery(object):
def __init__(self, name, filename):
self._stat_interval = 5000 # 5 seconds
""" Construct a NamedQuery object. The name argument is an
arbitrary name as a handle for the query, and the filename is
a path to a file containing an llsd named query document."""
self._stat_interval_seconds = 5 # 5 seconds
self._name = name
self._location = filename
self._alternative = dict()
self._last_mod_time = 0
self._last_check_time = 0
self.deleted = False
self.load_contents()
def name(self):
""" The name of the query. """
return self._name
def get_modtime(self):
return os.path.getmtime(self._location)
""" Returns the mtime (last modified time) of the named query
file, if such exists."""
if self._location:
return os.path.getmtime(self._location)
return 0
def load_contents(self):
self._contents = llsd.parse(open(self._location).read())
""" Loads and parses the named query file into self. Does
nothing if self.location is nonexistant."""
if self._location:
self._reference_contents(llsd.parse(open(self._location).read()))
# Check for alternative implementations
try:
for name, alt in self._contents['alternative'].items():
nq = NamedQuery(name, None)
nq._reference_contents(alt)
self._alternative[name] = nq
except KeyError, e:
pass
self._last_mod_time = self.get_modtime()
self._last_check_time = time.time()
def _reference_contents(self, contents):
"Helper method which builds internal structure from parsed contents"
self._contents = contents
self._ttl = int(self._contents.get('ttl', 0))
self._return_as_map = bool(self._contents.get('return_as_map', False))
self._legacy_dbname = self._contents.get('legacy_dbname', None)
self._legacy_query = self._contents.get('legacy_query', None)
self._options = self._contents.get('options', {})
self._base_query = self._contents['base_query']
self._last_mod_time = self.get_modtime()
self._last_check_time = time.time()
# reset these before doing the sql conversion because we will
# read them there. reset these while loading so we pick up
# changes.
self._around = set()
self._append = set()
self._integer = set()
self._options = self._contents.get('dynamic_where', {})
for key in self._options:
if isinstance(self._options[key], basestring):
self._options[key] = self._convert_sql(self._options[key])
elif isinstance(self._options[key], list):
lines = []
for line in self._options[key]:
lines.append(self._convert_sql(line))
self._options[key] = lines
else:
moreopt = {}
for kk in self._options[key]:
moreopt[kk] = self._convert_sql(self._options[key][kk])
self._options[key] = moreopt
self._base_query = self._convert_sql(self._contents['base_query'])
self._query_suffix = self._convert_sql(
self._contents.get('query_suffix', ''))
def _convert_sql(self, sql):
"""convert the parsed sql into a useful internal structure.
This function has to turn the named query format into a pyformat
style. It also has to look for %:name% and :name% and
ready them for use in LIKE statements"""
if sql:
#print >>sys.stderr, "sql:",sql
expr = re.compile("(%?):([a-zA-Z][a-zA-Z0-9_-]*)%")
sql = expr.sub(self._prepare_like, sql)
expr = re.compile("#:([a-zA-Z][a-zA-Z0-9_-]*)")
sql = expr.sub(self._prepare_integer, sql)
expr = re.compile(":([a-zA-Z][a-zA-Z0-9_-]*)")
sql = expr.sub("%(\\1)s", sql)
return sql
def _prepare_like(self, match):
"""This function changes LIKE statement replace behavior
It works by turning %:name% to %(_name_around)s and :name% to
%(_name_append)s. Since a leading '_' is not a valid keyname
input (enforced via unit tests), it will never clash with
existing keys. Then, when building the statement, the query
runner will generate corrected strings."""
if match.group(1) == '%':
# there is a leading % so this is treated as prefix/suffix
self._around.add(match.group(2))
return "%(" + self._build_around_key(match.group(2)) + ")s"
else:
# there is no leading %, so this is suffix only
self._append.add(match.group(2))
return "%(" + self._build_append_key(match.group(2)) + ")s"
def _build_around_key(self, key):
return "_" + key + "_around"
def _build_append_key(self, key):
return "_" + key + "_append"
def _prepare_integer(self, match):
"""This function adjusts the sql for #:name replacements
It works by turning #:name to %(_name_as_integer)s. Since a
leading '_' is not a valid keyname input (enforced via unit
tests), it will never clash with existing keys. Then, when
building the statement, the query runner will generate
corrected strings."""
self._integer.add(match.group(1))
return "%(" + self._build_integer_key(match.group(1)) + ")s"
def _build_integer_key(self, key):
return "_" + key + "_as_integer"
def _strip_wildcards_to_list(self, value):
"""Take string, and strip out the LIKE special characters.
Technically, this is database dependant, but postgresql and
mysql use the same wildcards, and I am not aware of a general
way to handle this. I think you need a sql statement of the
form:
LIKE_STRING( [ANY,ONE,str]... )
which would treat ANY as their any string, and ONE as their
single glyph, and str as something that needs database
specific encoding to not allow any % or _ to affect the query.
As it stands, I believe it's impossible to write a named query
style interface which uses like to search the entire space of
text available. Imagine the query:
% of brain used by average linden
In order to search for %, it must be escaped, so once you have
escaped the string to not do wildcard searches, and be escaped
for the database, and then prepended the wildcard you come
back with one of:
1) %\% of brain used by average linden
2) %%% of brain used by average linden
Then, when passed to the database to be escaped to be database
safe, you get back:
1) %\\% of brain used by average linden
: which means search for any character sequence, followed by a
backslash, followed by any sequence, followed by ' of
brain...'
2) %%% of brain used by average linden
: which (I believe) means search for a % followed by any
character sequence followed by 'of brain...'
Neither of which is what we want!
So, we need a vendor (or extention) for LIKE_STRING. Anyone
want to write it?"""
utf8_value = unicode(value, "utf-8")
esc_list = []
remove_chars = set(u"%_")
for glyph in utf8_value:
if glyph in remove_chars:
continue
esc_list.append(glyph.encode("utf-8"))
return esc_list
def delete(self):
""" Makes this query unusable by deleting all the members and
setting the deleted member. This is desired when the on-disk
query has been deleted but the in-memory copy remains."""
# blow away all members except _name, _location, and deleted
name, location = self._name, self._location
for key in self.__dict__.keys():
del self.__dict__[key]
self.deleted = True
self._name, self._location = name, location
def ttl(self):
""" Estimated time to live of this query. Used for web
services to set the Expires header."""
return self._ttl
def legacy_dbname(self):
return self._legacy_dbname
def legacy_query(self):
return self._legacy_query
def return_as_map(self):
""" Returns true if this query is configured to return its
results as a single map (as opposed to a list of maps, the
normal behavior)."""
return self._return_as_map
def run(self, connection, params, expect_rows = None, use_dictcursor = True):
"""\
@brief given a connection, run a named query with the params
def for_schema(self, db_name):
"Look trough the alternates and return the correct query"
try:
return self._alternative[db_name]
except KeyError, e:
pass
return self
Note that this function will fetch ALL rows. We do this because it
opens and closes the cursor to generate the values, and this isn't a generator so the
cursor has no life beyond the method call.
@param cursor The connection to use (this generates its own cursor for the query)
@param name The name of the query to run
@param params The parameters passed into the query
@param expect_rows The number of rows expected. Set to 1 if return_as_map is true. Raises ExpectationFailed if the number of returned rows doesn't exactly match. Kind of a hack.
@param use_dictcursor Set to false to use a normal cursor and manually convert the rows to dicts.
@return Returns the result set as a list of dicts, or, if the named query has return_as_map set to true, returns a single dict.
def run(self, connection, params, expect_rows = None, use_dictcursor = True):
"""given a connection, run a named query with the params
Note that this function will fetch ALL rows. We do this because it
opens and closes the cursor to generate the values, and this
isn't a generator so the cursor has no life beyond the method call.
@param cursor The connection to use (this generates its own cursor for the query)
@param name The name of the query to run
@param params The parameters passed into the query
@param expect_rows The number of rows expected. Set to 1 if return_as_map is true. Raises ExpectationFailed if the number of returned rows doesn't exactly match. Kind of a hack.
@param use_dictcursor Set to false to use a normal cursor and manually convert the rows to dicts.
@return Returns the result set as a list of dicts, or, if the named query has return_as_map set to true, returns a single dict.
"""
if use_dictcursor:
cursor = connection.cursor(MySQLdb.cursors.DictCursor)
else:
cursor = connection.cursor()
statement = self.sql(params)
statement = self.sql(connection, params)
#print "SQL:", statement
rows = cursor.execute(statement)
@ -169,47 +359,152 @@ cursor has no life beyond the method call.
return result_set[0]
return result_set
def sql(self, params):
def sql(self, connection, params):
""" Generates an SQL statement from the named query document
and a dictionary of parameters.
"""
self.refresh()
# build the query from the options available and the params
base_query = []
base_query.append(self._base_query)
#print >>sys.stderr, "base_query:",base_query
for opt, extra_where in self._options.items():
if opt in params and (params[opt] == 0 or params[opt]):
if type(extra_where) in (dict, list, tuple):
if type(extra_where) in (dict, list, tuple):
if opt in params:
base_query.append(extra_where[params[opt]])
else:
else:
if opt in params and params[opt]:
base_query.append(extra_where)
if self._query_suffix:
base_query.append(self._query_suffix)
#print >>sys.stderr, "base_query:",base_query
full_query = '\n'.join(base_query)
# do substitution
sql = russ.format(full_query, params)
# Go through the query and rewrite all of the ones with the
# @:name syntax.
rewrite = _RewriteQueryForArray(params)
expr = re.compile("@%\(([a-zA-Z][a-zA-Z0-9_-]*)\)s")
full_query = expr.sub(rewrite.operate, full_query)
params.update(rewrite.new_params)
# build out the params for like. We only have to do this
# parameters which were detected to have ued the where syntax
# during load.
#
# * treat the incoming string as utf-8
# * strip wildcards
# * append or prepend % as appropriate
new_params = {}
for key in params:
if key in self._around:
new_value = ['%']
new_value.extend(self._strip_wildcards_to_list(params[key]))
new_value.append('%')
new_params[self._build_around_key(key)] = ''.join(new_value)
if key in self._append:
new_value = self._strip_wildcards_to_list(params[key])
new_value.append('%')
new_params[self._build_append_key(key)] = ''.join(new_value)
if key in self._integer:
new_params[self._build_integer_key(key)] = int(params[key])
params.update(new_params)
# do substitution using the mysql (non-standard) 'literal'
# function to do the escaping.
sql = full_query % connection.literal(params)
return sql
def refresh(self):
# only stat the file every so often
""" Refresh self from the file on the filesystem.
This is optimized to be callable as frequently as you wish,
without adding too much load. It does so by only stat-ing the
file every N seconds, where N defaults to 5 and is
configurable through the member _stat_interval_seconds. If the stat
reveals that the file has changed, refresh will re-parse the
contents of the file and use them to update the named query
instance. If the stat reveals that the file has been deleted,
refresh will call self.delete to make the in-memory
representation unusable."""
now = time.time()
if(now - self._last_check_time > self._stat_interval):
if(now - self._last_check_time > self._stat_interval_seconds):
self._last_check_time = now
modtime = self.get_modtime()
if(modtime > self._last_mod_time):
self.load_contents()
try:
modtime = self.get_modtime()
if(modtime > self._last_mod_time):
self.load_contents()
except OSError, e:
if e.errno == errno.ENOENT: # file not found
self.delete() # clean up self
raise # pass the exception along to the caller so they know that this query disappeared
class NamedQueryManager(object):
""" Manages the lifespan of NamedQuery objects, drawing from a
directory hierarchy of named query documents.
In practice this amounts to a memory cache of NamedQuery objects."""
def __init__(self, named_queries_dir):
""" Initializes a manager to look for named queries in a
directory."""
self._dir = os.path.abspath(os.path.realpath(named_queries_dir))
self._cached_queries = {}
def sql(self, name, params):
def sql(self, connection, name, params):
nq = self.get(name)
return nq.sql(params)
return nq.sql(connection, params)
def get(self, name):
# new up/refresh a NamedQuery based on the name
""" Returns a NamedQuery instance based on the name, either
from memory cache, or by parsing from disk.
The name is simply a relative path to the directory associated
with the manager object. Before returning the instance, the
NamedQuery object is cached in memory, so that subsequent
accesses don't have to read from disk or do any parsing. This
means that NamedQuery objects returned by this method are
shared across all users of the manager object.
NamedQuery.refresh is used to bring the NamedQuery objects in
sync with the actual files on disk."""
nq = self._cached_queries.get(name)
if nq is None:
nq = NamedQuery(name, os.path.join(self._dir, name))
self._cached_queries[name] = nq
else:
try:
nq.refresh()
except OSError, e:
if e.errno == errno.ENOENT: # file not found
del self._cached_queries[name]
raise # pass exception along to caller so they know that the query disappeared
return nq
class _RewriteQueryForArray(object):
"Helper class for rewriting queries with the @:name syntax"
def __init__(self, params):
self.params = params
self.new_params = dict()
def operate(self, match):
"Given a match, return the string that should be in use"
key = match.group(1)
value = self.params[key]
if type(value) in (list,tuple):
rv = []
for idx in range(len(value)):
new_key = "_" + key + "_" + str(idx)
self.new_params[new_key] = value[idx]
rv.append("%(" + new_key + ")s")
return ','.join(rv)
else:
# not something that can be expanded, so just drop the
# leading @ in the front of the match. This will mean that
# the single value we have, be it a string, int, whatever
# (other than dict) will correctly show up, eg:
#
# where foo in (@:foobar) -- foobar is a string, so we get
# where foo in (:foobar)
return match.group(0)[1:]

View File

@ -35,6 +35,7 @@
#include "llerror.h"
#include "../llmath/llmath.h"
#include "llformat.h"
#include "llsdserialize.h"
#ifndef LL_RELEASE_FOR_DOWNLOAD
#define NAME_UNNAMED_NAMESPACE
@ -765,6 +766,44 @@ const LLSD& LLSD::operator[](Integer i) const
U32 LLSD::allocationCount() { return Impl::sAllocationCount; }
U32 LLSD::outstandingCount() { return Impl::sOutstandingCount; }
static const char *llsd_dump(const LLSD &llsd, bool useXMLFormat)
{
// sStorage is used to hold the string representation of the llsd last
// passed into this function. If this function is never called (the
// normal case when not debugging), nothing is allocated. Otherwise
// sStorage will point to the result of the last call. This will actually
// be one leak, but since this is used only when running under the
// debugger, it should not be an issue.
static char *sStorage = NULL;
delete[] sStorage;
std::string out_string;
{
std::ostringstream out;
if (useXMLFormat)
out << LLSDXMLStreamer(llsd);
else
out << LLSDNotationStreamer(llsd);
out_string = out.str();
}
int len = out_string.length();
sStorage = new char[len + 1];
memcpy(sStorage, out_string.c_str(), len);
sStorage[len] = '\0';
return sStorage;
}
/// Returns XML version of llsd -- only to be called from debugger
const char *LLSD::dumpXML(const LLSD &llsd)
{
return llsd_dump(llsd, true);
}
/// Returns Notation version of llsd -- only to be called from debugger
const char *LLSD::dump(const LLSD &llsd)
{
return llsd_dump(llsd, false);
}
LLSD::map_iterator LLSD::beginMap() { return makeMap(impl).beginMap(); }
LLSD::map_iterator LLSD::endMap() { return makeMap(impl).endMap(); }
LLSD::map_const_iterator LLSD::beginMap() const { return safe(impl).beginMap(); }

View File

@ -331,6 +331,16 @@ public:
static U32 allocationCount(); ///< how many Impls have been made
static U32 outstandingCount(); ///< how many Impls are still alive
//@}
private:
/** @name Debugging Interface */
//@{
/// Returns XML version of llsd -- only to be called from debugger
static const char *dumpXML(const LLSD &llsd);
/// Returns Notation version of llsd -- only to be called from debugger
static const char *dump(const LLSD &llsd);
//@}
};
struct llsd_select_bool : public std::unary_function<LLSD, LLSD::Boolean>

View File

@ -116,6 +116,104 @@ std::string LLServiceBuilder::buildServiceURI(
// Find the Service Name
if(!service_url.empty() && option_map.isMap())
{
// throw in a ridiculously large limiter to make sure we don't
// loop forever with bad input.
int iterations = 100;
bool keep_looping = true;
while(keep_looping)
{
if(0 == --iterations)
{
keep_looping = false;
}
int depth = 0;
int deepest = 0;
bool find_match = false;
std::string::iterator iter(service_url.begin());
std::string::iterator end(service_url.end());
std::string::iterator deepest_node(service_url.end());
std::string::iterator deepest_node_end(service_url.end());
for(; iter != end; ++iter)
{
switch(*iter)
{
case '{':
++depth;
if(depth > deepest)
{
deepest = depth;
deepest_node = iter;
find_match = true;
}
break;
case '}':
--depth;
if(find_match)
{
deepest_node_end = iter;
find_match = false;
}
break;
default:
break;
}
}
if((deepest_node == end) || (deepest_node_end == end))
{
break;
}
// *NOTE: since the c++ implementation only understands
// params and straight string substitution, so it's a
// known distance of 2 to skip the directive.
std::string key(deepest_node + 2, deepest_node_end);
LLSD value = option_map[key];
switch(*(deepest_node + 1))
{
case '$':
if(value.isDefined())
{
service_url.replace(
deepest_node,
deepest_node_end + 1,
value.asString());
}
else
{
llinfos << "Unknown key: " << key << llendl;
keep_looping = false;
}
break;
case '%':
{
std::string query_str = LLURI::mapToQueryString(value);
service_url.replace(
deepest_node,
deepest_node_end + 1,
query_str);
}
break;
default:
llinfos << "Unknown directive: " << *(deepest_node + 1)
<< llendl;
keep_looping = false;
break;
}
}
}
if (service_url.find('{') != std::string::npos)
{
llwarns << "Constructed a likely bogus service URL: " << service_url
<< llendl;
}
return service_url;
}
// Old, not as good implementation. Phoenix 2007-10-15
#if 0
// Do brace replacements - NOT CURRENTLY RECURSIVE
for(LLSD::map_const_iterator option_itr = option_map.beginMap();
option_itr != option_map.endMap();
@ -157,3 +255,4 @@ std::string LLServiceBuilder::buildServiceURI(
return service_url;
}
#endif

View File

@ -480,6 +480,7 @@ bool LLURLRequest::configure()
mDetail->mHeaders);
}
curl_easy_setopt(mDetail->mCurl, CURLOPT_URL, mDetail->mURL);
lldebugs << "URL: " << mDetail->mURL << llendl;
curl_multi_add_handle(mDetail->mCurlMulti, mDetail->mCurl);
mDetail->mNeedToRemoveEasyHandle = true;
}

View File

@ -42,6 +42,7 @@
#include <string>
#include "lliopipe.h"
#include "llchainio.h"
#include "llerror.h"
class LLURLRequestDetail;
@ -62,6 +63,7 @@ class LLURLRequestComplete;
*/
class LLURLRequest : public LLIOPipe
{
LOG_CLASS(LLURLRequest);
public:
/**
* @brief This enumeration is for specifying the type of request.

View File

@ -1100,6 +1100,17 @@ void LLMessageSystem::forwardReliable(const U32 circuit_code)
sendReliable(findHost(circuit_code));
}
S32 LLMessageSystem::forwardReliable( const LLHost &host,
S32 retries,
BOOL ping_based_timeout,
F32 timeout,
void (*callback)(void **,S32),
void ** callback_data)
{
copyMessageRtoS();
return sendReliable(host, retries, ping_based_timeout, timeout, callback, callback_data);
}
S32 LLMessageSystem::flushSemiReliable(const LLHost &host, void (*callback)(void **,S32), void ** callback_data)
{
F32 timeout;

View File

@ -464,6 +464,13 @@ public:
void forwardMessage(const LLHost &host);
void forwardReliable(const LLHost &host);
void forwardReliable(const U32 circuit_code);
S32 forwardReliable(
const LLHost &host,
S32 retries,
BOOL ping_based_timeout,
F32 timeout,
void (*callback)(void **,S32),
void ** callback_data);
LLHTTPClient::ResponderPtr createResponder(const std::string& name);
S32 sendMessage(const LLHost &host);

View File

@ -2117,7 +2117,6 @@ BOOL idle_startup()
msg->setHandlerFuncFast(_PREHASH_PreloadSound, process_preload_sound);
msg->setHandlerFuncFast(_PREHASH_AttachedSound, process_attached_sound);
msg->setHandlerFuncFast(_PREHASH_AttachedSoundGainChange, process_attached_sound_gain_change);
//msg->setHandlerFuncFast(_PREHASH_AttachedSoundCutoffRadius, process_attached_sound_cutoff_radius);
llinfos << "Initialization complete" << llendl;
@ -2771,7 +2770,6 @@ void register_viewer_callbacks(LLMessageSystem* msg)
msg->setHandlerFuncFast(_PREHASH_MeanCollisionAlert, process_mean_collision_alert_message, NULL);
msg->setHandlerFunc("ViewerFrozenMessage", process_frozen_message);
//msg->setHandlerFuncFast(_PREHASH_RequestAvatarInfo, process_avatar_info_request);
msg->setHandlerFuncFast(_PREHASH_NameValuePair, process_name_value);
msg->setHandlerFuncFast(_PREHASH_RemoveNameValuePair, process_remove_name_value);
msg->setHandlerFuncFast(_PREHASH_AvatarAnimation, process_avatar_animation);

View File

@ -3398,29 +3398,6 @@ void process_attached_sound_gain_change(LLMessageSystem *mesgsys, void **user_da
objectp->adjustAudioGain(gain);
}
/* Unused July 2006
void process_attached_sound_cutoff_radius(LLMessageSystem *mesgsys, void **user_data)
{
F32 radius = 0;
LLUUID object_guid;
LLViewerObject *objectp = NULL;
mesgsys->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_guid);
if (!((objectp = gObjectList.findObject(object_guid))))
{
// we don't know about this object, just bail
return;
}
mesgsys->getF32Fast(_PREHASH_DataBlock, _PREHASH_Radius, radius);
if (gAudiop)
{
// gAudiop->attachToObject(sound_guid, object_guid, gain, priority, flags);
}
}
*/
void process_health_message(LLMessageSystem *mesgsys, void **user_data)
{
@ -3566,19 +3543,6 @@ void process_sim_stats(LLMessageSystem *msg, void **user_data)
}
// This info is requested by the simulator when the agent first logs in
// or when it moves into a simulator in which it did not already have
// a child agent.
/*
void process_avatar_info_request(LLMessageSystem *mesgsys, void **user_data)
{
llinfos << "process_avatar_info_request()" << llendl;
// Send the avatar appearance (parameters and texture entry UUIDs)
gAgent.sendAgentSetAppearance();
send_agent_update(TRUE, TRUE);
}*/
void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data)
{

View File

@ -87,12 +87,10 @@ void process_sound_trigger(LLMessageSystem *mesgsys, void **user_data);
void process_preload_sound( LLMessageSystem *mesgsys, void **user_data);
void process_attached_sound( LLMessageSystem *mesgsys, void **user_data);
void process_attached_sound_gain_change( LLMessageSystem *mesgsys, void **user_data);
//void process_attached_sound_cutoff_radius( LLMessageSystem *mesgsys, void **user_data);
void process_energy_statistics(LLMessageSystem *mesgsys, void **user_data);
void process_health_message(LLMessageSystem *mesgsys, void **user_data);
void process_sim_stats(LLMessageSystem *mesgsys, void **user_data);
void process_shooter_agent_hit(LLMessageSystem* msg, void** user_data);
void process_avatar_info_request(LLMessageSystem *mesgsys, void **user_data);
void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data);
void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data);
void process_camera_constraint(LLMessageSystem *mesgsys, void **user_data);

View File

@ -180,7 +180,6 @@
#include "llinstantmessage.h"
#include "llinvite.h"
//#include "llloginflags.h"
#include "lllogtextmessage.h"
#include "llmail.h"
#include "llmessagethrottle.h"
#include "llnamevalue.h"

View File

@ -113,5 +113,66 @@ namespace tut
test_url ,
"/proc/do/something/useful?estate_id=1&query=public");
}
template<> template<>
void ServiceBuilderTestObject::test<6>()
{
LLSD test_block;
test_block["service-builder"] = "Which way to the {${$baz}}?";
mServiceBuilder.createServiceDefinition(
"ServiceBuilderTest",
test_block["service-builder"]);
LLSD data_map;
data_map["foo"] = "bar";
data_map["baz"] = "foo";
std::string test_url = mServiceBuilder.buildServiceURI(
"ServiceBuilderTest",
data_map);
ensure_equals(
"recursive url creation",
test_url ,
"Which way to the bar?");
}
template<> template<>
void ServiceBuilderTestObject::test<7>()
{
LLSD test_block;
test_block["service-builder"] = "Which way to the {$foo}?";
mServiceBuilder.createServiceDefinition(
"ServiceBuilderTest",
test_block["service-builder"]);
LLSD data_map;
data_map["baz"] = "foo";
std::string test_url = mServiceBuilder.buildServiceURI(
"ServiceBuilderTest",
data_map);
ensure_equals(
"fails to do replacement",
test_url ,
"Which way to the {$foo}?");
}
template<> template<>
void ServiceBuilderTestObject::test<8>()
{
LLSD test_block;
test_block["service-builder"] = "/proc/{$proc}{%params}";
mServiceBuilder.createServiceDefinition(
"ServiceBuilderTest",
test_block["service-builder"]);
LLSD data_map;
data_map["proc"] = "do/something/useful";
data_map["params"] = LLSD();
std::string test_url = mServiceBuilder.buildServiceURI(
"ServiceBuilderTest",
data_map);
ensure_equals(
"strip params",
test_url ,
"/proc/do/something/useful");
}
}