mirror of
https://github.com/Sei-Lisa/LSL-PyOptimizer
synced 2025-07-02 08:08:20 +00:00
As of Second Life Server 2022-09-09.574921, STATUS_DIE_AT_EDGE and STATUS_DIE_AT_NO_ENTRY return their corresponding settings rather than always FALSE. Regression/coverage tests are clearly lacking in this area, as the change didn't trigger any test breakage, therefore add some unit tests.
354 lines
10 KiB
Python
354 lines
10 KiB
Python
# (C) Copyright 2015-2022 Sei Lisa. All rights reserved.
|
|
#
|
|
# This file is part of LSL PyOptimizer.
|
|
#
|
|
# LSL PyOptimizer is free software: you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License as
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
# License, or (at your option) any later version.
|
|
#
|
|
# LSL PyOptimizer 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 General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with LSL PyOptimizer. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
# Extra functions that have predictable return values for certain arguments.
|
|
|
|
from lslopt.lslcommon import Key, Vector #, Quaternion
|
|
from lslopt.lslbasefuncs import ELSLCantCompute, fi,ff,fs,fk,v2f,q2f,fl, \
|
|
NULL_KEY, ZERO_VECTOR, ZERO_ROTATION, \
|
|
TOUCH_INVALID_TEXCOORD, cond
|
|
#from strutil import unicode
|
|
ff, q2f # keep pyflakes happy as these are not used
|
|
|
|
GetEnvSettings = frozenset({'agent_limit', 'dynamic_pathfinding', 'estate_id'
|
|
, 'estate_name', 'frame_number', 'region_cpu_ratio', 'region_idle'
|
|
, 'region_product_name', 'region_product_sku', 'region_start_time'
|
|
, 'sim_channel', 'sim_version', 'simulator_hostname'
|
|
, 'region_max_prims' # <http://wiki.secondlife.com/wiki/Release_Notes/Second_Life_RC_Magnum/16#16.11.02.321369>
|
|
, 'region_object_bonus' # <http://wiki.secondlife.com/wiki/Release_Notes/Second_Life_RC_Magnum/16#16.12.03.322072>
|
|
, 'whisper_range', 'chat_range', 'shout_range' #? (jun 2020)
|
|
, 'agent_limit_max', 'agent_reserved', 'agent_underserved' # <https://releasenotes.secondlife.com/simulator/2022-04-21.571166.html>
|
|
})
|
|
|
|
# Values valid for llGetVisualDetails parameters
|
|
GVD_ValidValues = \
|
|
{ 33, "height"
|
|
, 38, "torso_length"
|
|
, 80, "male"
|
|
, 198, "heel_height"
|
|
, 503, "platform_height"
|
|
, 616, "shoe_height"
|
|
, 692, "leg_length"
|
|
, 693, "arm_length"
|
|
, 756, "neck_length"
|
|
, 814, "waist_height"
|
|
, 842, "hip_length"
|
|
, 11001, "hover"
|
|
}
|
|
|
|
xp_error_messages = {
|
|
-1:u'unknown error id',
|
|
0:u'no error', 1:u'exceeded throttle', 2:u'experiences are disabled',
|
|
3:u'invalid parameters', 4:u'operation not permitted',
|
|
5:u'script not associated with an experience', 6:u'not found',
|
|
7:u'invalid experience', 8:u'experience is disabled',
|
|
9:u'experience is suspended', 10:u'unknown error',
|
|
11:u'experience data quota exceeded',
|
|
12:u'key-value store is disabled',
|
|
13:u'key-value store communication failed', 14:u'key doesn\'t exist',
|
|
15:u'retry update', 16:u'experience content rating too high',
|
|
17:u'not allowed to run in current location',
|
|
18:u'experience permissions request timed out'
|
|
}
|
|
|
|
valid_inventory_kinds = frozenset({0, 1, 3, 5, 6, 7, 10, 13, 20, 21})
|
|
|
|
def llCloud(v):
|
|
v = v2f(v)
|
|
return 0.0
|
|
|
|
def llAvatarOnLinkSitTarget(link):
|
|
link = fi(link)
|
|
if link > 255 or link == -2147483648:
|
|
return Key(NULL_KEY)
|
|
raise ELSLCantCompute
|
|
|
|
# llClearPrimMedia always has side effects (emits errors for every face that
|
|
# is not supported)
|
|
def llClearLinkMedia(link, face):
|
|
if link > 255 or link == -2147483648:
|
|
return 0
|
|
raise ELSLCantCompute
|
|
|
|
def llDetectedGrab(idx, evsym=None):
|
|
idx = fi(idx)
|
|
if 0 <= idx <= 15 and (evsym is None or 'grab' in evsym):
|
|
raise ELSLCantCompute
|
|
return ZERO_VECTOR
|
|
|
|
def llDetectedGroup(idx, evsym=None):
|
|
idx = fi(idx)
|
|
if 0 <= idx <= 15 and (evsym is None or 'detect' in evsym):
|
|
raise ELSLCantCompute
|
|
return 0
|
|
|
|
def llDetectedKey(idx, evsym=None):
|
|
idx = fi(idx)
|
|
if 0 <= idx <= 15 and (evsym is None or 'detect' in evsym):
|
|
raise ELSLCantCompute
|
|
return Key(NULL_KEY)
|
|
|
|
def llDetectedLinkNumber(idx, evsym=None):
|
|
idx = fi(idx)
|
|
if 0 <= idx <= 15 and (evsym is None or 'detect' in evsym):
|
|
raise ELSLCantCompute
|
|
return 0
|
|
|
|
def llDetectedName(idx, evsym=None):
|
|
idx = fi(idx)
|
|
if 0 <= idx <= 15 and (evsym is None or 'detect' in evsym):
|
|
raise ELSLCantCompute
|
|
return NULL_KEY
|
|
|
|
def llDetectedOwner(idx, evsym=None):
|
|
idx = fi(idx)
|
|
if 0 <= idx <= 15 and (evsym is None or 'detect' in evsym):
|
|
raise ELSLCantCompute
|
|
return Key(NULL_KEY)
|
|
|
|
def llDetectedPos(idx, evsym=None):
|
|
idx = fi(idx)
|
|
if 0 <= idx <= 15 and (evsym is None or 'detect' in evsym):
|
|
raise ELSLCantCompute
|
|
return ZERO_VECTOR
|
|
|
|
def llDetectedRot(idx, evsym=None):
|
|
idx = fi(idx)
|
|
if 0 <= idx <= 15 and (evsym is None or 'detect' in evsym):
|
|
raise ELSLCantCompute
|
|
return ZERO_ROTATION
|
|
|
|
def llDetectedTouchBinormal(idx, evsym=None):
|
|
idx = fi(idx)
|
|
if 0 <= idx <= 15 and (evsym is None or 'touch' in evsym):
|
|
raise ELSLCantCompute
|
|
return ZERO_VECTOR
|
|
|
|
def llDetectedTouchFace(idx, evsym=None):
|
|
idx = fi(idx)
|
|
if 0 <= idx <= 15 and (evsym is None or 'touch' in evsym):
|
|
raise ELSLCantCompute
|
|
return -1 if 'detect' in evsym and 0 <= idx <= 15 else 0
|
|
|
|
def llDetectedTouchNormal(idx, evsym=None):
|
|
idx = fi(idx)
|
|
if 0 <= idx <= 15 and (evsym is None or 'touch' in evsym):
|
|
raise ELSLCantCompute
|
|
return ZERO_VECTOR
|
|
|
|
def llDetectedTouchPos(idx, evsym=None):
|
|
idx = fi(idx)
|
|
if 0 <= idx <= 15 and (evsym is None or 'touch' in evsym):
|
|
raise ELSLCantCompute
|
|
return ZERO_VECTOR
|
|
|
|
def llDetectedTouchST(idx, evsym=None):
|
|
idx = fi(idx)
|
|
if 0 <= idx <= 15 and (evsym is None or 'detect' in evsym):
|
|
# In detection events that are not touch events, it returns
|
|
# TOUCH_INVALID_TEXCOORD if idx < num, else ZERO_VECTOR,
|
|
# but we only know that num >= 1.
|
|
if idx == 0 and evsym is not None and 'touch' not in evsym:
|
|
# index 0 always exists, so we know the result
|
|
return TOUCH_INVALID_TEXCOORD
|
|
raise ELSLCantCompute
|
|
return ZERO_VECTOR
|
|
|
|
def llDetectedTouchUV(idx, evsym=None):
|
|
idx = fi(idx)
|
|
if 0 <= idx <= 15 and (evsym is None or 'detect' in evsym):
|
|
# In detection events that are not touch events, it returns
|
|
# TOUCH_INVALID_TEXCOORD if idx < num, else ZERO_VECTOR,
|
|
# but we only know that num >= 1.
|
|
if idx == 0 and evsym is not None and 'touch' not in evsym:
|
|
# index 0 always exists, so we know the result
|
|
return TOUCH_INVALID_TEXCOORD
|
|
raise ELSLCantCompute
|
|
return ZERO_VECTOR
|
|
|
|
def llDetectedType(idx, evsym=None):
|
|
idx = fi(idx)
|
|
if 0 <= idx <= 15 and (evsym is None or 'detect' in evsym):
|
|
raise ELSLCantCompute
|
|
return 0
|
|
|
|
def llDetectedVel(idx, evsym=None):
|
|
idx = fi(idx)
|
|
if 0 <= idx <= 15 and (evsym is None or 'detect' in evsym):
|
|
raise ELSLCantCompute
|
|
return ZERO_VECTOR
|
|
|
|
def llEdgeOfWorld(v1, v2):
|
|
v1 = v2f(v1)
|
|
v2 = v2f(v2)
|
|
if v2[0] == v2[1] == 0:
|
|
return 1
|
|
raise ELSLCantCompute
|
|
|
|
def llGetAgentInfo(id):
|
|
id = fk(id)
|
|
if not cond(id):
|
|
return 0
|
|
raise ELSLCantCompute
|
|
|
|
def llGetAgentLanguage(id):
|
|
id = fk(id)
|
|
if not cond(id):
|
|
return u''
|
|
raise ELSLCantCompute
|
|
|
|
def llGetAgentList(scope, options):
|
|
scope = fi(scope)
|
|
options = fl(options)
|
|
if scope not in (1, 2, 4):
|
|
return [u'INVALID_SCOPE']
|
|
raise ELSLCantCompute
|
|
|
|
def llGetAgentSize(id):
|
|
id = fk(id)
|
|
if not cond(id):
|
|
return ZERO_VECTOR
|
|
raise ELSLCantCompute
|
|
|
|
def llGetAlpha(face):
|
|
face = fi(face)
|
|
if face > 8:
|
|
return 1.0
|
|
# Negative face numbers return (float)llGetNumberOfSides(), which isn't
|
|
# computable.
|
|
raise ELSLCantCompute
|
|
|
|
def llGetAnimation(id):
|
|
id = fk(id)
|
|
if not cond(id):
|
|
return u''
|
|
raise ELSLCantCompute
|
|
|
|
def llGetAnimationList(id):
|
|
id = fk(id)
|
|
if not cond(id):
|
|
return []
|
|
raise ELSLCantCompute
|
|
|
|
def llGetAttachedList(id):
|
|
id = fk(id)
|
|
if not cond(id):
|
|
return [u'NOT FOUND']
|
|
raise ELSLCantCompute
|
|
|
|
def llGetBoundingBox(id):
|
|
id = fk(id)
|
|
if not cond(id):
|
|
return []
|
|
raise ELSLCantCompute
|
|
|
|
def llGetColor(face):
|
|
face = fi(face)
|
|
if face > 8:
|
|
return Vector((1.,1.,1.))
|
|
# Returns the average colour when negative (can't be computed)
|
|
raise ELSLCantCompute
|
|
|
|
def llGetDisplayName(id):
|
|
id = fk(id)
|
|
if not cond(id):
|
|
return u''
|
|
raise ELSLCantCompute
|
|
|
|
def llGetEnv(s):
|
|
s = fs(s)
|
|
if s not in GetEnvSettings:
|
|
return u""
|
|
raise ELSLCantCompute
|
|
|
|
def llGetExperienceErrorMessage(errno):
|
|
errno = fi(errno)
|
|
if errno < -1 or errno > 18:
|
|
errno = -1
|
|
return xp_error_messages[errno]
|
|
|
|
def llGetExperienceList(id):
|
|
id = fk(id)
|
|
# This function is not implemented and always returns empty list
|
|
return []
|
|
|
|
def llGetHTTPHeader(id, s):
|
|
id = fk(id)
|
|
s = fs(s)
|
|
if not cond(id):
|
|
return u''
|
|
raise ELSLCantCompute
|
|
|
|
def llGetInventoryKey(s):
|
|
s = fs(s)
|
|
if s == u'':
|
|
return Key(NULL_KEY)
|
|
raise ELSLCantCompute
|
|
|
|
def llGetInventoryName(kind, index):
|
|
kind = fi(kind)
|
|
index = fi(index)
|
|
if kind != -1 and kind not in valid_inventory_kinds or index < 0:
|
|
return u''
|
|
raise ELSLCantCompute
|
|
|
|
def llGetInventoryNumber(kind):
|
|
kind = fi(kind)
|
|
if kind != -1 and kind not in valid_inventory_kinds:
|
|
return 0
|
|
raise ELSLCantCompute
|
|
|
|
def llGetInventoryPermMask(item, category):
|
|
item = fs(item)
|
|
category = fi(category)
|
|
if category < 0 or category > 4 or item == u'':
|
|
return 0
|
|
raise ELSLCantCompute
|
|
|
|
def llGetLinkName(link):
|
|
link = fi(link)
|
|
if link < -2147483646 or link > 256:
|
|
return NULL_KEY
|
|
raise ELSLCantCompute
|
|
|
|
def llGetOwnerKey(id):
|
|
id = fk(id)
|
|
if not cond(id):
|
|
return Key(NULL_KEY)
|
|
raise ELSLCantCompute
|
|
|
|
def llGetStatus(mask):
|
|
# Leave out STATUS_CAST_SHADOWS from the flags to check.
|
|
if (mask & ~0x200) == 0:
|
|
return 0
|
|
raise ELSLCantCompute
|
|
|
|
def llGetVisualParams(id, params):
|
|
id = fk(id)
|
|
params = fl(params)
|
|
if not cond(id) or params.nt == 'CONST' and not params.ch:
|
|
return []
|
|
# TODO: This needs to check whether the list is a literal, and whether the
|
|
# elements are constant.
|
|
# if params.nt == 'LIST':
|
|
# for i in params:
|
|
# if (i.lower() if type(i) == unicode else i) in GVD_ValidValues:
|
|
# raise ELSLCantCompute
|
|
# return [u""] * len(params)
|
|
raise ELSLCantCompute
|
|
|
|
# TODO: Add more predictable functions.
|