From bb194f6ab48998eefd026ab63095736ffc970a0a Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Thu, 4 Jun 2026 10:08:40 -0700 Subject: [PATCH 1/3] Add rulesets for generating table based way of passing parameters to functions requiring lists. --- .gitignore | 1 + Sources.cmake | 2 + VM/include/llfluent_builder.h | 77 ++ VM/src/llfluent_builder.cpp | 329 +++++++ autobuild.xml | 14 +- build-cmd.sh | 2 +- builtins.txt | 880 +++++++++--------- init_debian_buster.sh | 5 + tests/SLConformance.test.cpp | 80 ++ .../conformance/fluent_builder_collection.lua | 92 ++ tests/conformance/llprim_particle.lua | 90 ++ 11 files changed, 1122 insertions(+), 450 deletions(-) create mode 100644 VM/include/llfluent_builder.h create mode 100644 VM/src/llfluent_builder.cpp create mode 100644 tests/conformance/fluent_builder_collection.lua create mode 100644 tests/conformance/llprim_particle.lua diff --git a/.gitignore b/.gitignore index 4f34a306..cba1cf26 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ __pycache__ /fuzz/corpus/json/* !/fuzz/corpus/json/*.json *.code-workspace +*.tar.bz2 diff --git a/Sources.cmake b/Sources.cmake index 7736c0a9..8347245f 100644 --- a/Sources.cmake +++ b/Sources.cmake @@ -352,6 +352,7 @@ target_sources(Luau.VM PRIVATE VM/include/luaconf.h VM/include/lualib.h VM/include/llsl.h + VM/include/llfluent_builder.h VM/src/ares.cpp VM/src/lapi.cpp @@ -429,6 +430,7 @@ target_sources(Luau.VM PRIVATE VM/src/llprim.cpp VM/src/llprim.h VM/src/llprim_set_primitive_params.inl + VM/src/llfluent_builder.cpp VM/src/lyieldable.cpp VM/src/lstrbuf.cpp VM/src/lyieldstrlib.h diff --git a/VM/include/llfluent_builder.h b/VM/include/llfluent_builder.h new file mode 100644 index 00000000..cbef88d0 --- /dev/null +++ b/VM/include/llfluent_builder.h @@ -0,0 +1,77 @@ +#pragma once +#include + +struct lua_State; +typedef int (*lua_CFunction)(lua_State* L); + +// Default link target: apply to the prim running the script. +static const int SLUA_LINK_THIS = -4; + +// A note about C++20 porting. +// The const char* name fields in the descriptors must have static storage duration +// (e.g. string literals). +// This is not enforced at compile time, +// but when moving to C++20 we can do something like the following: +// +// struct LiteralString +// { +// consteval LiteralString(const char* p) noexcept : ptr(p) {} +// operator const char*() const noexcept { return ptr; } +// const char* ptr; +// }; +// The consteval constructor ensures that only string literals can be used +// to construct a LiteralString. + +struct FluentParamDescriptor +{ + const char* name; // effective property name (pretty alias or strict fallback) + char semantic; // scalar: 'i'=integer 'f'=float 's'=string 'v'=vector 'r'=rotation 'b'=boolean 'a'=asset 'k'=key + // collection: 'C'=string-csv ({string} array → escaped comma-joined string, one tag/value pair) + // 'M'=string-map ({[string]:string} table → one tag/key/value triple per entry) + // 'N'=string-multi ({string} array → one tag/value pair per element, preserving order) + int tag; // PSYS_* constant integer value +}; + +struct FluentFlagDescriptor +{ + const char* name; // boolean property name, e.g. "color_interp" + int mask; // bitmask, e.g. 0x01 + int field_tag; // tag of the integer field holding the bits, e.g. 0 for "flags" +}; + +struct FluentBuilderDef; + +// Build a FluentBuilderDef from an array of descriptors. +// The .name pointers in descs must have static storage duration (e.g. string literals). +// Caller owns the returned pointer (process lifetime expected). +// Descriptors are sorted by tag internally; caller order does not matter. +FluentBuilderDef* fluent_builder_def_build( + const FluentParamDescriptor* descs, + size_t count +); + +// Attach flag-bit boolean properties to an existing def. +// Each descriptor maps a property name to a bitmask within the integer field at field_tag. +// The .name pointers in descs must have static storage duration (e.g. string literals). +// Call after fluent_builder_def_build(). +void fluent_builder_def_add_flags( + FluentBuilderDef* def, + const FluentFlagDescriptor* descs, + size_t count +); + +// Serialize a params table into a flat tag/value rules list and push it onto the stack. +// params_idx is the stack index of the params table (may be nil — emits an empty list). +// Flag boolean properties are merged into their backing integer field before emission. +void slua_fluent_serialize(lua_State* L, int params_idx, const FluentBuilderDef* def); + +// Register fn as module_name.fn_name in L's globals, with def stored as upvalue 1. +// Creates the module table if it does not yet exist; adds to it if it does. +// Sets the module table readonly after each call. +void slua_register_fluent_fn( + lua_State* L, + const char* module_name, + const char* fn_name, + lua_CFunction fn, + const FluentBuilderDef* def +); diff --git a/VM/src/llfluent_builder.cpp b/VM/src/llfluent_builder.cpp new file mode 100644 index 00000000..4e991822 --- /dev/null +++ b/VM/src/llfluent_builder.cpp @@ -0,0 +1,329 @@ +#define llfluent_builder_c + +#include +#include +#include +#include + +#include "lua.h" +#include "lcommon.h" +#include "lualib.h" +#include "llsl.h" +#include "llfluent_builder.h" + +struct FluentBuilderDef +{ + std::vector descs; // sorted by tag; .name points into string literals + std::vector flag_descs; +}; + +namespace +{ + // Converts the value at TOS to its string representation. + // Numbers, booleans, and strings are always converted. Other types + // (vectors, quaternions, userdata) are converted only if they have a + // __tostring metamethod; everything else is skipped. + // On success: replaces TOS with a Lua string and returns true. + // On failure: pops TOS and returns false. + bool slua_fluent_to_string(lua_State* L) + { + int vtype = lua_type(L, -1); + + // Native conversions — always well-defined. + if (vtype == LUA_TSTRING || vtype == LUA_TNUMBER || vtype == LUA_TBOOLEAN) + { + luaL_tolstring(L, -1, nullptr); + lua_remove(L, -2); + return true; + } + + // Other types: only include if __tostring is present; skip the + // "typename: 0xaddr" fallback. + if (luaL_getmetafield(L, -1, "__tostring")) + { + lua_pop(L, 1); // pop the metamethod itself + luaL_tolstring(L, -1, nullptr); + lua_remove(L, -2); + return true; + } + + lua_pop(L, 1); + return false; + } + + // Encodes a lua array as a CSV string; emit one tag/value pair. + // returns the new index into the list table. + int slua_fluent_serialize_string_csv(lua_State* L, int list, int idx, + const FluentParamDescriptor& desc) + { + int n = (int)lua_objlen(L, -1); + if (n > 0) + { + std::string csv; + for (int j = 1; j <= n; ++j) + { + lua_rawgeti(L, -1, j); + if (slua_fluent_to_string(L)) + { + size_t len = 0; + const char* s = lua_tolstring(L, -1, &len); + if (s) + { + if (!csv.empty()) csv += ','; + for (size_t k = 0; k < len; ++k) + { + if (s[k] == '\\' || s[k] == ',') csv += '\\'; + csv += s[k]; + } + } + lua_pop(L, 1); + } + } + lua_pop(L, 1); // pop the array + if (!csv.empty()) + { + lua_pushinteger(L, desc.tag); + lua_rawseti(L, list, ++idx); + lua_pushlstring(L, csv.c_str(), csv.size()); + lua_rawseti(L, list, ++idx); + } + } + else + { + lua_pop(L, 1); // pop empty array, emit nothing + } + + return idx; + } + + + // Convert a lua table into a series of tag/key/value triples, one per entry, + // sorted by key. + // returns the new index into the list table. + int slua_fluent_serialize_string_map(lua_State* L, int list, int idx, + const FluentParamDescriptor& desc) + { + // Collect keys for deterministic ordering. + std::vector keys; + lua_pushnil(L); + while (lua_next(L, -2)) + { + if (lua_type(L, -2) == LUA_TSTRING) + keys.push_back(lua_tostring(L, -2)); + lua_pop(L, 1); // pop value, keep key for next iteration + } + std::sort(keys.begin(), keys.end()); + + for (const auto& key : keys) + { + lua_pushlstring(L, key.c_str(), key.size()); + lua_rawget(L, -2); // push value + if (!slua_fluent_to_string(L)) + continue; + lua_pushinteger(L, desc.tag); + lua_rawseti(L, list, ++idx); + lua_pushlstring(L, key.c_str(), key.size()); + lua_rawseti(L, list, ++idx); + lua_rawseti(L, list, ++idx); // value + } + lua_pop(L, 1); // pop the table + return idx; + } + + // Convert a lua array into a series of tag/value pairs, one per element, + // preserving order. + // returns the new index into the list table. + int slua_fluent_serialize_string_multi(lua_State* L, int list, int idx, + const FluentParamDescriptor& desc) + { + int n = (int)lua_objlen(L, -1); + for (int j = 1; j <= n; ++j) + { + lua_rawgeti(L, -1, j); + if (slua_fluent_to_string(L)) + { + lua_pushinteger(L, desc.tag); + lua_rawseti(L, list, ++idx); + lua_rawseti(L, list, ++idx); + } + } + lua_pop(L, 1); // pop the array + return idx; + } +} + +FluentBuilderDef* fluent_builder_def_build( + const FluentParamDescriptor* descs, + size_t count +) +{ + auto* def = new FluentBuilderDef; + + // Copy descriptors. .name pointers reference string literals with static + // storage duration — no deep copy needed. + def->descs.assign(descs, descs + count); + + // Sort by tag for deterministic serialization order. + std::sort(def->descs.begin(), def->descs.end(), + [](const FluentParamDescriptor& a, const FluentParamDescriptor& b) { + return a.tag < b.tag; + }); + + return def; +} + +void fluent_builder_def_add_flags( + FluentBuilderDef* def, + const FluentFlagDescriptor* descs, + size_t count +) +{ + // .name pointers reference string literals — no deep copy needed. + def->flag_descs.assign(descs, descs + count); +} + +void slua_fluent_serialize(lua_State* L, int params_idx, const FluentBuilderDef* def) +{ + lua_newtable(L); + int list = lua_gettop(L); + int idx = 0; + + if (lua_isnoneornil(L, params_idx)) + return; // empty list + + // Phase 1: accumulate flag bits per backing field. + std::unordered_map flag_set_bits; // field_tag -> bits to OR in + std::unordered_map flag_clear_bits; // field_tag -> bits to AND out + for (const auto& fdesc : def->flag_descs) + { + lua_rawgetfield(L, params_idx, fdesc.name); + if (!lua_isnil(L, -1)) + { + bool set = lua_isboolean(L, -1) ? (bool)lua_toboolean(L, -1) + : (lua_tointeger(L, -1) != 0); + if (set) + flag_set_bits[fdesc.field_tag] |= fdesc.mask; + else + flag_clear_bits[fdesc.field_tag] |= fdesc.mask; + } + lua_pop(L, 1); + } + + // Phase 2: emit tag/value pairs in tag order. + for (const auto& desc : def->descs) + { + int raw_int = 0; + bool has_raw = false; + + lua_rawgetfield(L, params_idx, desc.name); + has_raw = !lua_isnil(L, -1); + if (has_raw && lua_isnumber(L, -1)) + raw_int = (int)lua_tointeger(L, -1); + if (!has_raw) + lua_pop(L, 1); + + // Integer fields that back flags: merge accumulated bits. + auto set_it = flag_set_bits.find(desc.tag); + auto clear_it = flag_clear_bits.find(desc.tag); + if (desc.semantic == 'i' && + (set_it != flag_set_bits.end() || clear_it != flag_clear_bits.end())) + { + int set_mask = (set_it != flag_set_bits.end()) ? set_it->second : 0; + int clear_mask = (clear_it != flag_clear_bits.end()) ? clear_it->second : 0; + int merged = (raw_int | set_mask) & ~clear_mask; + if (has_raw) lua_pop(L, 1); + lua_pushinteger(L, desc.tag); + lua_rawseti(L, list, ++idx); + lua_pushinteger(L, merged); + lua_rawseti(L, list, ++idx); + continue; + } + + // 'C': string-csv — value is a Lua array of strings. + // Encode as comma-joined string; emit one tag/value pair. + if (desc.semantic == 'C') + { + if (!has_raw || !lua_istable(L, -1)) + { + if (has_raw) lua_pop(L, 1); + continue; + } + + idx = slua_fluent_serialize_string_csv(L, list, idx, desc); + continue; + } + + // 'M': string-map — value is a Lua string-keyed table. + // Emit one tag/key/value triple per entry, in sorted key order. + if (desc.semantic == 'M') + { + if (!has_raw || !lua_istable(L, -1)) + { + if (has_raw) lua_pop(L, 1); + continue; + } + idx = slua_fluent_serialize_string_map(L, list, idx, desc); + continue; + } + + // 'N': string-multi — value is a Lua array of strings. + // Emit one tag/value pair per element, preserving order. + if (desc.semantic == 'N') + { + if (!has_raw || !lua_istable(L, -1)) + { + if (has_raw) lua_pop(L, 1); + continue; + } + idx = slua_fluent_serialize_string_multi(L, list, idx, desc); + continue; + } + + if (!has_raw) + continue; + + // Append tag then value. + lua_pushinteger(L, desc.tag); + lua_rawseti(L, list, ++idx); + + if (desc.semantic == 'b' && lua_isboolean(L, -1)) + lua_pushinteger(L, lua_toboolean(L, -1)); + else + lua_pushvalue(L, -1); + lua_rawseti(L, list, ++idx); + + lua_pop(L, 1); + } +} + +void slua_register_fluent_fn( + lua_State* L, + const char* module_name, + const char* fn_name, + lua_CFunction fn, + const FluentBuilderDef* def +) +{ + int top = lua_gettop(L); + + lua_getglobal(L, module_name); + if (!lua_istable(L, -1)) + { + lua_pop(L, 1); + lua_newtable(L); + } + else + { + lua_setreadonly(L, -1, false); + } + int module_idx = lua_gettop(L); + + lua_pushlightuserdata(L, (void*)def); + lua_pushcclosurek(L, fn, fn_name, 1, nullptr); + lua_setfield(L, module_idx, fn_name); + + lua_setreadonly(L, module_idx, true); + lua_setglobal(L, module_name); + + LUAU_ASSERT(lua_gettop(L) == top); +} diff --git a/autobuild.xml b/autobuild.xml index b83ae227..3510e079 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -102,11 +102,11 @@ archive hash - ffc81687f9c141d5bcfb79b8a201080c64b86a2c + 5d173e32624eebc6adb4241874309e8e3dd49d72 hash_algorithm sha1 url - https://github.com/secondlife/lsl-definitions/releases/download/v0.6.2/lsl_definitions-0.6.2-common-25239282650.tar.zst + https://github.com/secondlife/lsl-definitions/releases/download/v0.6.10/lsl_definitions-0.6.10-common-28481241270.tar.zst name common @@ -119,17 +119,11 @@ copyright Copyright (c) 2026, Linden Lab version - 0.6.2 + 0.6.10 use_scm_version true name lsl_definitions - vcs_branch - refs/tags/v0.6.1 - vcs_revision - e16250ee55fab9ed399d50cf232630e9c5bb22ef - vcs_url - git://github.com/secondlife/lsl-definitions.git source_type git canonical_repo @@ -259,7 +253,7 @@ license_file LICENSES/luau.txt copyright - Copyright (c) 2024 Linden Lab Inc., Copyright (c) 2022-2024 Harold Cindy, Copyright (c) 2019-2024 Roblox Corporation, Copyright (c) 1994–2019 Lua.org, PUC-Rio. + Copyright (c) 2024 Linden Research, Inc., Copyright (c) 2022-2024 Harold Cindy, Copyright (c) 2019-2024 Roblox Corporation, Copyright (c) 1994–2019 Lua.org, PUC-Rio. use_scm_version true name diff --git a/build-cmd.sh b/build-cmd.sh index 3146ed2f..df076335 100755 --- a/build-cmd.sh +++ b/build-cmd.sh @@ -37,7 +37,7 @@ mkdir -p "$stage/lib/release" pushd "$top" pushd "VM/include" - cp -v lua.h luaconf.h lualib.h llsl.h "$stage/include/luau/" + cp -v lua.h luaconf.h lualib.h llsl.h llfluent_builder.h "$stage/include/luau/" popd pushd "Compiler/include" cp -v luacode.h "$stage/include/luau/" diff --git a/builtins.txt b/builtins.txt index 2d8ae3c1..d0b9b038 100644 --- a/builtins.txt +++ b/builtins.txt @@ -1,148 +1,148 @@ // Generated by gen_definitions.py -integer llAbs( integer Value ) -float llAcos( float Value ) -void llAddToLandBanList( key ID, float Hours ) -void llAddToLandPassList( key ID, float Hours ) -void llAdjustDamage( integer Number, float Damage ) -void llAdjustSoundVolume( float Volume ) -integer llAgentInExperience( key AgentID ) -void llAllowInventoryDrop( integer Flag ) -float llAngleBetween( rotation Rot1, rotation Rot2 ) -void llApplyImpulse( vector Force, integer Local ) -void llApplyRotationalImpulse( vector Force, integer Local ) -float llAsin( float Value ) +integer llAbs( integer val ) +float llAcos( float val ) +void llAddToLandBanList( key avatar, float hours ) +void llAddToLandPassList( key avatar, float hours ) +void llAdjustDamage( integer number, float new_damage ) +void llAdjustSoundVolume( float volume ) +integer llAgentInExperience( key agent ) +void llAllowInventoryDrop( integer add ) +float llAngleBetween( rotation a, rotation b ) +void llApplyImpulse( vector momentum, integer is_local ) +void llApplyRotationalImpulse( vector force, integer is_local ) +float llAsin( float val ) float llAtan2( float y, float x ) -void llAttachToAvatar( integer AttachmentPoint ) -void llAttachToAvatarTemp( integer AttachPoint ) -key llAvatarOnLinkSitTarget( integer LinkNumber ) +void llAttachToAvatar( integer attach_point ) +void llAttachToAvatarTemp( integer attach_point ) +key llAvatarOnLinkSitTarget( integer link ) key llAvatarOnSitTarget( ) -rotation llAxes2Rot( vector Forward, vector Left, vector Up ) -rotation llAxisAngle2Rot( vector Axis, float Angle ) -integer llBase64ToInteger( string Text ) -string llBase64ToString( string Text ) +rotation llAxes2Rot( vector fwd, vector left, vector up ) +rotation llAxisAngle2Rot( vector axis, float angle ) +integer llBase64ToInteger( string str ) +string llBase64ToString( string str ) void llBreakAllLinks( ) -void llBreakLink( integer LinkNumber ) -list llCSV2List( string Text ) -list llCastRay( vector Start, vector End, list Options ) -integer llCeil( float Value ) -string llChar( integer value ) +void llBreakLink( integer link ) +list llCSV2List( string src ) +list llCastRay( vector start_pos, vector end_pos, list options ) +integer llCeil( float val ) +string llChar( integer val ) void llClearCameraParams( ) -void llClearExperience( key AgentID, key ExperienceID ) -void llClearExperiencePermissions( key AgentID ) -integer llClearLinkMedia( integer Link, integer Face ) -integer llClearPrimMedia( integer Face ) -void llCloseRemoteDataChannel( key ChannelID ) -float llCloud( vector Offset ) -void llCollisionFilter( string ObjectName, key ObjectID, integer Accept ) -void llCollisionSound( string ImpactSound, float ImpactVolume ) -void llCollisionSprite( string ImpactSprite ) -string llComputeHash( string Message, string Algorithm ) -float llCos( float Theta ) -void llCreateCharacter( list Options ) -key llCreateKeyValue( string Key, string Value ) -void llCreateLink( key TargetPrim, integer Parent ) -void llDamage( key target, float damage, integer type ) +void llClearExperience( key agentid, key experienceid ) +void llClearExperiencePermissions( key agentid ) +integer llClearLinkMedia( integer link, integer face ) +integer llClearPrimMedia( integer face ) +void llCloseRemoteDataChannel( key channel ) +float llCloud( vector offset ) +void llCollisionFilter( string name, key id, integer accept ) +void llCollisionSound( string impact_sound, float impact_volume ) +void llCollisionSprite( string impact_sprite ) +string llComputeHash( string message, string algorithm ) +float llCos( float theta ) +void llCreateCharacter( list options ) +key llCreateKeyValue( string k, string v ) +void llCreateLink( key target, integer parent ) +void llDamage( key target, float damage, integer damage_type ) key llDataSizeKeyValue( ) void llDeleteCharacter( ) -key llDeleteKeyValue( string Key ) -list llDeleteSubList( list Source, integer Start, integer End ) -string llDeleteSubString( string Source, integer Start, integer End ) -integer llDerezObject( key ID, integer flags ) +key llDeleteKeyValue( string k ) +list llDeleteSubList( list src, integer start_index, integer end_index ) +string llDeleteSubString( string src, integer start_index, integer end_index ) +integer llDerezObject( key id, integer flag ) void llDetachFromAvatar( ) -list llDetectedDamage( integer Number ) -vector llDetectedGrab( integer Number ) -integer llDetectedGroup( integer Number ) -key llDetectedKey( integer Number ) -integer llDetectedLinkNumber( integer Number ) -string llDetectedName( integer Number ) -key llDetectedOwner( integer Number ) -vector llDetectedPos( integer Number ) -key llDetectedRezzer( integer Number ) -rotation llDetectedRot( integer Number ) -vector llDetectedTouchBinormal( integer Index ) -integer llDetectedTouchFace( integer Index ) -vector llDetectedTouchNormal( integer Index ) -vector llDetectedTouchPos( integer Index ) -vector llDetectedTouchST( integer Index ) -vector llDetectedTouchUV( integer Index ) -integer llDetectedType( integer Number ) -vector llDetectedVel( integer Number ) -void llDialog( key AvatarID, string Text, list Buttons, integer Channel ) +list llDetectedDamage( integer number ) +vector llDetectedGrab( integer number ) +integer llDetectedGroup( integer number ) +key llDetectedKey( integer number ) +integer llDetectedLinkNumber( integer number ) +string llDetectedName( integer item ) +key llDetectedOwner( integer number ) +vector llDetectedPos( integer number ) +key llDetectedRezzer( integer number ) +rotation llDetectedRot( integer number ) +vector llDetectedTouchBinormal( integer index ) +integer llDetectedTouchFace( integer index ) +vector llDetectedTouchNormal( integer index ) +vector llDetectedTouchPos( integer index ) +vector llDetectedTouchST( integer index ) +vector llDetectedTouchUV( integer index ) +integer llDetectedType( integer number ) +vector llDetectedVel( integer number ) +void llDialog( key avatar, string message, list buttons, integer channel ) void llDie( ) -string llDumpList2String( list Source, string Separator ) -integer llEdgeOfWorld( vector Position, vector Direction ) -void llEjectFromLand( key AvatarID ) -void llEmail( string Address, string Subject, string Text ) -string llEscapeURL( string URL ) -rotation llEuler2Rot( vector Vector ) -void llEvade( key TargetID, list Options ) -void llExecCharacterCmd( integer Command, list Options ) -float llFabs( float Value ) -key llFindNotecardTextCount( string NotecardName, string Pattern, list Options ) -list llFindNotecardTextSync( string NotecardName, string Pattern, integer StartMatch, integer Count, list Options ) -void llFleeFrom( vector Source, float Distance, list Options ) -integer llFloor( float Value ) -void llForceMouselook( integer Enable ) -float llFrand( float Magnitude ) +string llDumpList2String( list src, string separator ) +integer llEdgeOfWorld( vector pos, vector dir ) +void llEjectFromLand( key avatar ) +void llEmail( string address, string subject, string message ) +string llEscapeURL( string url ) +rotation llEuler2Rot( vector v ) +void llEvade( key target, list options ) +void llExecCharacterCmd( integer command, list options ) +float llFabs( float val ) +key llFindNotecardTextCount( string notecardname, string pattern, list options ) +list llFindNotecardTextSync( string name, string pattern, integer start, integer count, list options ) +void llFleeFrom( vector position, float distance, list options ) +integer llFloor( float val ) +void llForceMouselook( integer mouselook ) +float llFrand( float mag ) key llGenerateKey( ) vector llGetAccel( ) -integer llGetAgentInfo( key AvatarID ) -string llGetAgentLanguage( key AvatarID ) -list llGetAgentList( integer Scope, list Options ) -vector llGetAgentSize( key AvatarID ) -float llGetAlpha( integer Face ) +integer llGetAgentInfo( key id ) +string llGetAgentLanguage( key avatar ) +list llGetAgentList( integer scope, list options ) +vector llGetAgentSize( key avatar ) +float llGetAlpha( integer face ) float llGetAndResetTime( ) -string llGetAnimation( key AvatarID ) -list llGetAnimationList( key AvatarID ) -string llGetAnimationOverride( string AnimationState ) +string llGetAnimation( key id ) +list llGetAnimationList( key avatar ) +string llGetAnimationOverride( string anim_state ) integer llGetAttached( ) -list llGetAttachedList( key ID ) -list llGetAttachedListFiltered( key AgentID, list Options ) -list llGetBoundingBox( key ID ) +list llGetAttachedList( key avatar ) +list llGetAttachedListFiltered( key avatar, list options ) +list llGetBoundingBox( key object ) float llGetCameraAspect( ) float llGetCameraFOV( ) vector llGetCameraPos( ) rotation llGetCameraRot( ) vector llGetCenterOfMass( ) -list llGetClosestNavPoint( vector Point, list Options ) -vector llGetColor( integer Face ) +list llGetClosestNavPoint( vector point, list options ) +vector llGetColor( integer face ) key llGetCreator( ) string llGetDate( ) integer llGetDayLength( ) integer llGetDayOffset( ) -string llGetDisplayName( key AvatarID ) +string llGetDisplayName( key id ) float llGetEnergy( ) -string llGetEnv( string DataRequest ) -list llGetEnvironment( vector Position, list EnvParams ) -list llGetExperienceDetails( key ExperienceID ) -string llGetExperienceErrorMessage( integer Error ) -list llGetExperienceList( key AgentID ) +string llGetEnv( string name ) +list llGetEnvironment( vector pos, list params ) +list llGetExperienceDetails( key experience_id ) +string llGetExperienceErrorMessage( integer error ) +list llGetExperienceList( key agentid ) vector llGetForce( ) integer llGetFreeMemory( ) integer llGetFreeURLs( ) float llGetGMTclock( ) vector llGetGeometricCenter( ) -string llGetHTTPHeader( key HTTPRequestID, string Header ) -float llGetHealth( key ID ) -string llGetInventoryAcquireTime( string InventoryItem ) -key llGetInventoryCreator( string InventoryItem ) -string llGetInventoryDesc( string InventoryItem ) -key llGetInventoryKey( string InventoryItem ) -string llGetInventoryName( integer InventoryType, integer Index ) -integer llGetInventoryNumber( integer InventoryType ) -integer llGetInventoryPermMask( string InventoryItem, integer BitMask ) -integer llGetInventoryType( string InventoryItem ) +string llGetHTTPHeader( key request_id, string header ) +float llGetHealth( key id ) +string llGetInventoryAcquireTime( string item ) +key llGetInventoryCreator( string item ) +string llGetInventoryDesc( string item ) +key llGetInventoryKey( string name ) +string llGetInventoryName( integer type, integer number ) +integer llGetInventoryNumber( integer type ) +integer llGetInventoryPermMask( string item, integer category ) +integer llGetInventoryType( string name ) key llGetKey( ) -key llGetLandOwnerAt( vector Position ) -key llGetLinkKey( integer LinkNumber ) -list llGetLinkMedia( integer LinkNumber, integer Face, list Parameters ) -string llGetLinkName( integer LinkNumber ) +key llGetLandOwnerAt( vector pos ) +key llGetLinkKey( integer link ) +list llGetLinkMedia( integer link, integer face, list params ) +string llGetLinkName( integer link ) integer llGetLinkNumber( ) -integer llGetLinkNumberOfSides( integer LinkNumber ) -list llGetLinkPrimitiveParams( integer LinkNumber, list Parameters ) -integer llGetLinkSitFlags( integer LinkNumber ) -integer llGetListEntryType( list ListVariable, integer Index ) -integer llGetListLength( list ListVariable ) +integer llGetLinkNumberOfSides( integer link ) +list llGetLinkPrimitiveParams( integer link, list params ) +integer llGetLinkSitFlags( integer link ) +integer llGetListEntryType( list src, integer index ) +integer llGetListLength( list src ) vector llGetLocalPos( ) rotation llGetLocalRot( ) float llGetMass( ) @@ -152,35 +152,35 @@ integer llGetMemoryLimit( ) float llGetMinScaleFactor( ) vector llGetMoonDirection( ) rotation llGetMoonRotation( ) -void llGetNextEmail( string Address, string Subject ) -key llGetNotecardLine( string NotecardName, integer LineNumber ) -string llGetNotecardLineSync( string NotecardName, integer LineNumber ) -key llGetNumberOfNotecardLines( string NotecardName ) +void llGetNextEmail( string address, string subject ) +key llGetNotecardLine( string name, integer line ) +string llGetNotecardLineSync( string name, integer line ) +key llGetNumberOfNotecardLines( string name ) integer llGetNumberOfPrims( ) integer llGetNumberOfSides( ) list llGetObjectAnimationNames( ) string llGetObjectDesc( ) -list llGetObjectDetails( key ID, list Parameters ) -key llGetObjectLinkKey( key id, integer link_no ) -float llGetObjectMass( key ID ) +list llGetObjectDetails( key id, list params ) +key llGetObjectLinkKey( key object_id, integer link ) +float llGetObjectMass( key id ) string llGetObjectName( ) -integer llGetObjectPermMask( integer Category ) -integer llGetObjectPrimCount( key ObjectID ) +integer llGetObjectPermMask( integer category ) +integer llGetObjectPrimCount( key prim ) vector llGetOmega( ) key llGetOwner( ) -key llGetOwnerKey( key ObjectID ) -list llGetParcelDetails( vector Position, list ParcelDetails ) -integer llGetParcelFlags( vector Position ) -integer llGetParcelMaxPrims( vector Position, integer SimWide ) +key llGetOwnerKey( key id ) +list llGetParcelDetails( vector pos, list params ) +integer llGetParcelFlags( vector pos ) +integer llGetParcelMaxPrims( vector pos, integer sim_wide ) string llGetParcelMusicURL( ) -integer llGetParcelPrimCount( vector Position, integer Category, integer SimWide ) -list llGetParcelPrimOwners( vector Position ) +integer llGetParcelPrimCount( vector pos, integer category, integer sim_wide ) +list llGetParcelPrimOwners( vector pos ) integer llGetPermissions( ) key llGetPermissionsKey( ) list llGetPhysicsMaterial( ) vector llGetPos( ) -list llGetPrimMediaParams( integer Face, list Parameters ) -list llGetPrimitiveParams( list Parameters ) +list llGetPrimMediaParams( integer face, list params ) +list llGetPrimitiveParams( list params ) integer llGetRegionAgentCount( ) vector llGetRegionCorner( ) integer llGetRegionDayLength( ) @@ -194,336 +194,336 @@ vector llGetRegionSunDirection( ) rotation llGetRegionSunRotation( ) float llGetRegionTimeDilation( ) float llGetRegionTimeOfDay( ) -string llGetRenderMaterial( integer Face ) +string llGetRenderMaterial( integer face ) vector llGetRootPosition( ) rotation llGetRootRotation( ) rotation llGetRot( ) integer llGetSPMaxMemory( ) vector llGetScale( ) string llGetScriptName( ) -integer llGetScriptState( string ScriptName ) -float llGetSimStats( integer StatType ) +integer llGetScriptState( string script ) +float llGetSimStats( integer stat_type ) string llGetSimulatorHostname( ) integer llGetStartParameter( ) string llGetStartString( ) -list llGetStaticPath( vector Start, vector End, float Radius, list Parameters ) -integer llGetStatus( integer StatusFlag ) -string llGetSubString( string String, integer Start, integer End ) +list llGetStaticPath( vector start_pos, vector end_pos, float radius, list params ) +integer llGetStatus( integer status ) +string llGetSubString( string src, integer start_index, integer end_index ) vector llGetSunDirection( ) rotation llGetSunRotation( ) -string llGetTexture( integer Face ) -vector llGetTextureOffset( integer Face ) -float llGetTextureRot( integer Face ) -vector llGetTextureScale( integer Face ) +string llGetTexture( integer face ) +vector llGetTextureOffset( integer face ) +float llGetTextureRot( integer face ) +vector llGetTextureScale( integer face ) float llGetTime( ) float llGetTimeOfDay( ) string llGetTimestamp( ) vector llGetTorque( ) integer llGetUnixTime( ) integer llGetUsedMemory( ) -string llGetUsername( key AvatarID ) +string llGetUsername( key id ) vector llGetVel( ) -list llGetVisualParams( key ID, list Parameters ) +list llGetVisualParams( key agentid, list params ) float llGetWallclock( ) -integer llGiveAgentInventory( key AgentID, string FolderName, list InventoryItems, list Options ) -void llGiveInventory( key TargetID, string InventoryItem ) -void llGiveInventoryList( key TargetID, string FolderName, list InventoryItems ) -integer llGiveMoney( key AvatarID, integer Amount ) -void llGodLikeRezObject( key InventoryItemID, vector Position ) -float llGround( vector Offset ) -vector llGroundContour( vector Offset ) -vector llGroundNormal( vector Offset ) -void llGroundRepel( float Height, integer Water, float Tau ) -vector llGroundSlope( vector Offset ) -string llHMAC( string Key, string Message, string Algorithm ) -key llHTTPRequest( string URL, list Parameters, string Body ) -void llHTTPResponse( key HTTPRequestID, integer Status, string Body ) -integer llHash( string value ) -string llInsertString( string TargetVariable, integer Position, string SourceVariable ) -void llInstantMessage( key AvatarID, string Text ) -string llIntegerToBase64( integer Value ) +integer llGiveAgentInventory( key agent, string folder, list inventory, list options ) +void llGiveInventory( key destination, string inventory ) +void llGiveInventoryList( key target, string folder, list inventory ) +integer llGiveMoney( key destination, integer amount ) +void llGodLikeRezObject( key inventory, vector pos ) +float llGround( vector offset ) +vector llGroundContour( vector offset ) +vector llGroundNormal( vector offset ) +void llGroundRepel( float height, integer water, float tau ) +vector llGroundSlope( vector offset ) +string llHMAC( string private_key, string msg, string algorithm ) +key llHTTPRequest( string url, list parameters, string body ) +void llHTTPResponse( key request_id, integer status, string body ) +integer llHash( string val ) +string llInsertString( string dst, integer pos, string src ) +void llInstantMessage( key user, string message ) +string llIntegerToBase64( integer number ) integer llIsFriend( key agent_id ) integer llIsLinkGLTFMaterial( integer link, integer face ) -list llJson2List( string JSON ) -string llJsonGetValue( string JSON, list Specifiers ) -string llJsonSetValue( string JSON, list Specifiers, string Value ) -string llJsonValueType( string JSON, list Specifiers ) -string llKey2Name( key ID ) +list llJson2List( string src ) +string llJsonGetValue( string json, list specifiers ) +string llJsonSetValue( string json, list specifiers, string value ) +string llJsonValueType( string json, list specifiers ) +string llKey2Name( key id ) key llKeyCountKeyValue( ) -key llKeysKeyValue( integer First, integer Count ) +key llKeysKeyValue( integer first, integer count ) vector llLinear2sRGB( vector color ) -void llLinkAdjustSoundVolume( integer LinkNumber, float Volume ) -void llLinkParticleSystem( integer LinkNumber, list Rules ) -void llLinkPlaySound( integer LinkNumber, string Sound, float Volume, integer Flags ) -void llLinkSetSoundQueueing( integer LinkNumber, integer QueueEnable ) -void llLinkSetSoundRadius( integer LinkNumber, float radius ) -void llLinkSitTarget( integer LinkNumber, vector Offset, rotation Rotation ) -void llLinkStopSound( integer LinkNumber ) +void llLinkAdjustSoundVolume( integer link, float volume ) +void llLinkParticleSystem( integer link, list rules ) +void llLinkPlaySound( integer link, string sound, float volume, integer flags ) +void llLinkSetSoundQueueing( integer link, integer queue ) +void llLinkSetSoundRadius( integer link, float radius ) +void llLinkSitTarget( integer link, vector offset, rotation rot ) +void llLinkStopSound( integer link ) integer llLinksetDataAvailable( ) -integer llLinksetDataCountFound( string search ) +integer llLinksetDataCountFound( string pattern ) integer llLinksetDataCountKeys( ) integer llLinksetDataDelete( string name ) -list llLinksetDataDeleteFound( string search, string pass ) +list llLinksetDataDeleteFound( string pattern, string pass ) integer llLinksetDataDeleteProtected( string name, string pass ) -list llLinksetDataFindKeys( string search, integer start, integer count ) +list llLinksetDataFindKeys( string pattern, integer start, integer count ) list llLinksetDataListKeys( integer start, integer count ) string llLinksetDataRead( string name ) string llLinksetDataReadProtected( string name, string pass ) void llLinksetDataReset( ) integer llLinksetDataWrite( string name, string value ) integer llLinksetDataWriteProtected( string name, string value, string pass ) -string llList2CSV( list ListVariable ) -float llList2Float( list ListVariable, integer Index ) -integer llList2Integer( list ListVariable, integer Index ) -string llList2Json( string JsonType, list Values ) -key llList2Key( list ListVariable, integer Index ) -list llList2List( list ListVariable, integer Start, integer End ) -list llList2ListSlice( list ListVariable, integer Start, integer End, integer Stride, integer slice_index ) -list llList2ListStrided( list ListVariable, integer Start, integer End, integer Stride ) -rotation llList2Rot( list ListVariable, integer Index ) -string llList2String( list ListVariable, integer Index ) -vector llList2Vector( list ListVariable, integer Index ) -integer llListFindList( list ListVariable, list Find ) -integer llListFindListNext( list ListVariable, list Find, integer Instance ) -integer llListFindStrided( list ListVariable, list Find, integer Start, integer End, integer Stride ) -list llListInsertList( list Target, list ListVariable, integer Position ) -list llListRandomize( list ListVariable, integer Stride ) -list llListReplaceList( list Target, list ListVariable, integer Start, integer End ) -list llListSort( list ListVariable, integer Stride, integer Ascending ) -list llListSortStrided( list ListVariable, integer Stride, integer Sortkey, integer Ascending ) -float llListStatistics( integer Operation, list ListVariable ) -integer llListen( integer Channel, string SpeakersName, key SpeakersID, string Text ) -void llListenControl( integer ChannelHandle, integer Active ) -void llListenRemove( integer ChannelHandle ) -void llLoadURL( key AvatarID, string Text, string URL ) -float llLog( float Value ) -float llLog10( float Value ) -void llLookAt( vector Target, float Strength, float Damping ) -void llLoopSound( string Sound, float Volume ) -void llLoopSoundMaster( string Sound, float Volume ) -void llLoopSoundSlave( string Sound, float Volume ) -string llMD5String( string Text, integer Nonce ) -void llMakeExplosion( integer Particles, float Scale, float Velocity, float Lifetime, float Arc, string Texture, vector Offset ) -void llMakeFire( integer Particles, float Scale, float Velocity, float Lifetime, float Arc, string Texture, vector Offset ) -void llMakeFountain( integer Particles, float Scale, float Velocity, float Lifetime, float Arc, integer Bounce, string Texture, vector Offset, float Bounce_Offset ) -void llMakeSmoke( integer Particles, float Scale, float Velocity, float Lifetime, float Arc, string Texture, vector Offset ) -integer llManageEstateAccess( integer Action, key AvatarID ) -void llMapBeacon( string RegionName, vector Position, list Options ) -void llMapDestination( string RegionName, vector Position, vector Direction ) -void llMessageLinked( integer LinkNumber, integer Number, string Text, key ID ) -void llMinEventDelay( float Delay ) -integer llModPow( integer Value, integer Power, integer Modulus ) -void llModifyLand( integer Action, integer Area ) -void llMoveToTarget( vector Target, float Tau ) -key llName2Key( string Name ) -void llNavigateTo( vector Location, list Options ) -void llOffsetTexture( float OffsetS, float OffsetT, integer Face ) +string llList2CSV( list src ) +float llList2Float( list src, integer index ) +integer llList2Integer( list src, integer index ) +string llList2Json( string type, list values ) +key llList2Key( list src, integer index ) +list llList2List( list src, integer start_index, integer end_index ) +list llList2ListSlice( list src, integer start_index, integer end_index, integer stride, integer slice_index ) +list llList2ListStrided( list src, integer start_index, integer end_index, integer stride ) +rotation llList2Rot( list src, integer index ) +string llList2String( list src, integer index ) +vector llList2Vector( list src, integer index ) +integer llListFindList( list src, list test ) +integer llListFindListNext( list src, list test, integer instance ) +integer llListFindStrided( list src, list test, integer start_index, integer end_index, integer stride ) +list llListInsertList( list dest, list src, integer start ) +list llListRandomize( list src, integer stride ) +list llListReplaceList( list dest, list src, integer start_index, integer end_index ) +list llListSort( list src, integer stride, integer ascending ) +list llListSortStrided( list src, integer stride, integer stride_index, integer ascending ) +float llListStatistics( integer operation, list src ) +integer llListen( integer channel, string name, key id, string msg ) +void llListenControl( integer handle, integer active ) +void llListenRemove( integer handle ) +void llLoadURL( key avatar, string message, string url ) +float llLog( float val ) +float llLog10( float val ) +void llLookAt( vector target, float strength, float damping ) +void llLoopSound( string sound, float volume ) +void llLoopSoundMaster( string sound, float volume ) +void llLoopSoundSlave( string sound, float volume ) +string llMD5String( string src, integer nonce ) +void llMakeExplosion( integer particles, float scale, float vel, float lifetime, float arc, string texture, vector offset ) +void llMakeFire( integer particles, float scale, float vel, float lifetime, float arc, string texture, vector offset ) +void llMakeFountain( integer particles, float scale, float vel, float lifetime, float arc, integer bounce, string texture, vector offset, float bounce_offset ) +void llMakeSmoke( integer particles, float scale, float vel, float lifetime, float arc, string texture, vector offset ) +integer llManageEstateAccess( integer action, key avatar ) +void llMapBeacon( string region_name, vector pos, list options ) +void llMapDestination( string simname, vector pos, vector look_at ) +void llMessageLinked( integer link, integer num, string str, key id ) +void llMinEventDelay( float delay ) +integer llModPow( integer a, integer b, integer c ) +void llModifyLand( integer action, integer brush ) +void llMoveToTarget( vector target, float tau ) +key llName2Key( string name ) +void llNavigateTo( vector pos, list options ) +void llOffsetTexture( float u, float v, integer face ) integer llOpenFloater( string floater_name, string url, list params ) void llOpenRemoteDataChannel( ) -integer llOrd( string value, integer index ) -integer llOverMyLand( key ID ) -void llOwnerSay( string Text ) -void llParcelMediaCommandList( list CommandList ) -list llParcelMediaQuery( list QueryList ) -list llParseString2List( string Text, list Separators, list Spacers ) -list llParseStringKeepNulls( string Text, list Separators, list Spacers ) -void llParticleSystem( list Parameters ) -void llPassCollisions( integer Pass ) -void llPassTouches( integer Pass ) -void llPatrolPoints( list Points, list Options ) -void llPlaySound( string Sound, float Volume ) -void llPlaySoundSlave( string Sound, float Volume ) -void llPointAt( vector Point ) -float llPow( float Value, float Exponent ) -void llPreloadSound( string Sound ) -void llPursue( key TargetID, list Options ) -void llPushObject( key ObjectID, vector Impulse, vector AngularImpulse, integer Local ) -key llReadKeyValue( string Key ) +integer llOrd( string val, integer index ) +integer llOverMyLand( key id ) +void llOwnerSay( string msg ) +void llParcelMediaCommandList( list commandList ) +list llParcelMediaQuery( list query ) +list llParseString2List( string src, list separators, list spacers ) +list llParseStringKeepNulls( string src, list separators, list spacers ) +void llParticleSystem( list rules ) +void llPassCollisions( integer pass ) +void llPassTouches( integer pass ) +void llPatrolPoints( list patrolPoints, list options ) +void llPlaySound( string sound, float volume ) +void llPlaySoundSlave( string sound, float volume ) +void llPointAt( vector pos ) +float llPow( float base, float exponent ) +void llPreloadSound( string sound ) +void llPursue( key target, list options ) +void llPushObject( key target, vector impulse, vector ang_impulse, integer is_local ) +key llReadKeyValue( string k ) void llRefreshPrimURL( ) -void llRegionSay( integer Channel, string Text ) -void llRegionSayTo( key TargetID, integer Channel, string Text ) -void llReleaseCamera( key AvatarID ) +void llRegionSay( integer channel, string msg ) +void llRegionSayTo( key target, integer channel, string msg ) +void llReleaseCamera( key avatar ) void llReleaseControls( ) -void llReleaseURL( string URL ) -void llRemoteDataReply( key ChannelID, key MessageID, string sData, integer iData ) +void llReleaseURL( string url ) +void llRemoteDataReply( key channel, key message_id, string sdata, integer idata ) void llRemoteDataSetRegion( ) -void llRemoteLoadScript( key Target, string ScriptName, integer Unknown1, integer Unknown2 ) -void llRemoteLoadScriptPin( key ObjectID, string ScriptName, integer PIN, integer Running, integer StartParameter ) -void llRemoveFromLandBanList( key AvatarID ) -void llRemoveFromLandPassList( key AvatarID ) -void llRemoveInventory( string InventoryItem ) -void llRemoveVehicleFlags( integer Vehiclelags ) +void llRemoteLoadScript( key target, string name, integer running, integer start_param ) +void llRemoteLoadScriptPin( key target, string name, integer pin, integer running, integer start_param ) +void llRemoveFromLandBanList( key avatar ) +void llRemoveFromLandPassList( key avatar ) +void llRemoveInventory( string item ) +void llRemoveVehicleFlags( integer flags ) integer llReplaceAgentEnvironment( key agent_id, float transition, string environment ) integer llReplaceEnvironment( vector position, string environment, integer track_no, integer day_length, integer day_offset ) -string llReplaceSubString( string InitialString, string SubString, string NewSubString, integer Count ) -key llRequestAgentData( key AvatarID, integer Data ) -key llRequestDisplayName( key AvatarID ) -void llRequestExperiencePermissions( key AgentID, string unused ) -key llRequestInventoryData( string InventoryItem ) -void llRequestPermissions( key AvatarID, integer PermissionMask ) +string llReplaceSubString( string src, string pattern, string replacement_pattern, integer count ) +key llRequestAgentData( key id, integer data ) +key llRequestDisplayName( key id ) +void llRequestExperiencePermissions( key agent, string name ) +key llRequestInventoryData( string name ) +void llRequestPermissions( key agent, integer permissions ) key llRequestSecureURL( ) -key llRequestSimulatorData( string RegionName, integer Data ) +key llRequestSimulatorData( string region, integer data ) key llRequestURL( ) -key llRequestUserKey( string Name ) -key llRequestUsername( key AvatarID ) -void llResetAnimationOverride( string AnimationState ) +key llRequestUserKey( string username ) +key llRequestUsername( key id ) +void llResetAnimationOverride( string anim_state ) void llResetLandBanList( ) void llResetLandPassList( ) -void llResetOtherScript( string ScriptName ) +void llResetOtherScript( string name ) void llResetScript( ) void llResetTime( ) -integer llReturnObjectsByID( list ObjectIDs ) -integer llReturnObjectsByOwner( key ID, integer Scope ) -void llRezAtRoot( string InventoryItem, vector Position, vector Velocity, rotation Rotation, integer StartParameter ) -void llRezObject( string InventoryItem, vector Position, vector Velocity, rotation Rotation, integer StartParameter ) -key llRezObjectWithParams( string InventoryItem, list Params ) -float llRot2Angle( rotation Rotation ) -vector llRot2Axis( rotation Rotation ) -vector llRot2Euler( rotation Rotation ) -vector llRot2Fwd( rotation Rotation ) -vector llRot2Left( rotation Rotation ) -vector llRot2Up( rotation Rotation ) -rotation llRotBetween( vector Vector1, vector Vector2 ) -void llRotLookAt( rotation Rotation, float Strength, float Damping ) -integer llRotTarget( rotation Rotation, float LeeWay ) -void llRotTargetRemove( integer Handle ) -void llRotateTexture( float Radians, integer Face ) -integer llRound( float Value ) -string llSHA1String( string Text ) -string llSHA256String( string text ) -integer llSameGroup( key ID ) -void llSay( integer Channel, string Text ) -integer llScaleByFactor( float ScalingFactor ) -void llScaleTexture( float Horizontal, float Vertical, integer Face ) -integer llScriptDanger( vector Position ) -void llScriptProfiler( integer State ) -key llSendRemoteData( key ChannelID, string Destination, integer Value, string Text ) -void llSensor( string Name, key ID, integer Type, float Range, float Arc ) +integer llReturnObjectsByID( list objects ) +integer llReturnObjectsByOwner( key owner, integer scope ) +void llRezAtRoot( string inventory, vector position, vector velocity, rotation rot, integer param ) +void llRezObject( string inventory, vector pos, vector vel, rotation rot, integer param ) +key llRezObjectWithParams( string inventory, list params ) +float llRot2Angle( rotation rot ) +vector llRot2Axis( rotation rot ) +vector llRot2Euler( rotation quat ) +vector llRot2Fwd( rotation q ) +vector llRot2Left( rotation q ) +vector llRot2Up( rotation q ) +rotation llRotBetween( vector start_vec, vector end_vec ) +void llRotLookAt( rotation target_direction, float strength, float damping ) +integer llRotTarget( rotation rot, float error ) +void llRotTargetRemove( integer handle ) +void llRotateTexture( float angle, integer face ) +integer llRound( float val ) +string llSHA1String( string src ) +string llSHA256String( string src ) +integer llSameGroup( key uuid ) +void llSay( integer channel, string msg ) +integer llScaleByFactor( float scaling_factor ) +void llScaleTexture( float u, float v, integer face ) +integer llScriptDanger( vector pos ) +void llScriptProfiler( integer flags ) +key llSendRemoteData( key channel, string dest, integer idata, string sdata ) +void llSensor( string name, key id, integer type, float radius, float arc ) void llSensorRemove( ) -void llSensorRepeat( string Name, key ID, integer Type, float Range, float Arc, float Rate ) -integer llSetAgentEnvironment( key agent_id, float transition, list Settings ) +void llSensorRepeat( string name, key id, integer type, float radius, float arc, float rate ) +integer llSetAgentEnvironment( key agent_id, float transition, list params ) void llSetAgentRot( rotation rot, integer flags ) -void llSetAlpha( float Opacity, integer Face ) -void llSetAngularVelocity( vector AngVel, integer Local ) -void llSetAnimationOverride( string AnimationState, string AnimationName ) -void llSetBuoyancy( float Buoyancy ) -void llSetCameraAtOffset( vector Offset ) -void llSetCameraEyeOffset( vector Offset ) -void llSetCameraParams( list Parameters ) -void llSetClickAction( integer Action ) -void llSetColor( vector Color, integer Face ) -void llSetContentType( key HTTPRequestID, integer ContentType ) -void llSetDamage( float Damage ) -integer llSetEnvironment( vector Position, list EnvParams ) -integer llSetExperienceKey( key ExperienceID ) -void llSetForce( vector Force, integer Local ) -void llSetForceAndTorque( vector Force, vector Torque, integer Local ) -integer llSetGroundTexture( list Changes ) -void llSetHoverHeight( float Height, integer Water, float Tau ) -void llSetInventoryPermMask( string InventoryItem, integer PermissionFlag, integer PermissionMask ) -void llSetKeyframedMotion( list Keyframes, list Options ) -void llSetLinkAlpha( integer LinkNumber, float Opacity, integer Face ) -void llSetLinkCamera( integer LinkNumber, vector EyeOffset, vector LookOffset ) -void llSetLinkColor( integer LinkNumber, vector Color, integer Face ) -void llSetLinkGLTFOverrides( integer link, integer face, list options ) -integer llSetLinkMedia( integer Link, integer Face, list Parameters ) -void llSetLinkPrimitiveParams( integer LinkNumber, list Parameters ) -void llSetLinkPrimitiveParamsFast( integer LinkNumber, list Parameters ) -void llSetLinkRenderMaterial( integer LinkNumber, string RenderMaterial, integer Face ) -void llSetLinkSitFlags( integer LinkNumber, integer Flags ) -void llSetLinkTexture( integer LinkNumber, string Texture, integer Face ) -void llSetLinkTextureAnim( integer LinkNumber, integer Mode, integer Face, integer SizeX, integer SizeY, float Start, float Length, float Rate ) -void llSetLocalRot( rotation Rotation ) -integer llSetMemoryLimit( integer Limit ) -void llSetObjectDesc( string Description ) -void llSetObjectName( string Name ) -void llSetObjectPermMask( integer PermissionFlag, integer PermissionMask ) +void llSetAlpha( float alpha, integer face ) +void llSetAngularVelocity( vector initial_omega, integer is_local ) +void llSetAnimationOverride( string anim_state, string anim ) +void llSetBuoyancy( float buoyancy ) +void llSetCameraAtOffset( vector offset ) +void llSetCameraEyeOffset( vector offset ) +void llSetCameraParams( list rules ) +void llSetClickAction( integer action ) +void llSetColor( vector color, integer face ) +void llSetContentType( key request_id, integer content_type ) +void llSetDamage( float damage ) +integer llSetEnvironment( vector position, list params ) +integer llSetExperienceKey( key experienceid ) +void llSetForce( vector force, integer is_local ) +void llSetForceAndTorque( vector force, vector torque, integer is_local ) +integer llSetGroundTexture( list changes ) +void llSetHoverHeight( float height, integer water, float tau ) +void llSetInventoryPermMask( string item, integer category, integer value ) +void llSetKeyframedMotion( list keyframes, list options ) +void llSetLinkAlpha( integer link, float alpha, integer face ) +void llSetLinkCamera( integer link, vector eye, vector at ) +void llSetLinkColor( integer link, vector color, integer face ) +void llSetLinkGLTFOverrides( integer link, integer face, list params ) +integer llSetLinkMedia( integer link, integer face, list params ) +void llSetLinkPrimitiveParams( integer link, list rules ) +void llSetLinkPrimitiveParamsFast( integer link, list rules ) +void llSetLinkRenderMaterial( integer link, string material, integer face ) +void llSetLinkSitFlags( integer link, integer flags ) +void llSetLinkTexture( integer link, string texture, integer face ) +void llSetLinkTextureAnim( integer link, integer mode, integer face, integer sizex, integer sizey, float start, float length, float rate ) +void llSetLocalRot( rotation rot ) +integer llSetMemoryLimit( integer limit ) +void llSetObjectDesc( string description ) +void llSetObjectName( string name ) +void llSetObjectPermMask( integer mask, integer value ) integer llSetParcelForSale( integer ForSale, list Options ) -void llSetParcelMusicURL( string URL ) -void llSetPayPrice( integer Price, list QuickButtons ) -void llSetPhysicsMaterial( integer MaterialBits, float GravityMultiplier, float Restitution, float Friction, float Density ) -void llSetPos( vector Position ) -integer llSetPrimMediaParams( integer Face, list MediaParameters ) -void llSetPrimURL( string URL ) -void llSetPrimitiveParams( list Parameters ) -integer llSetRegionPos( vector Position ) -void llSetRemoteScriptAccessPin( integer PIN ) -void llSetRenderMaterial( string Material, integer Face ) -void llSetRot( rotation Rotation ) -void llSetScale( vector Scale ) -void llSetScriptState( string ScriptName, integer Running ) -void llSetSitText( string Text ) -void llSetSoundQueueing( integer QueueEnable ) -void llSetSoundRadius( float Radius ) -void llSetStatus( integer Status, integer Value ) -void llSetText( string Text, vector Color, float Opacity ) -void llSetTexture( string Texture, integer Face ) -void llSetTextureAnim( integer Mode, integer Face, integer SizeX, integer SizeY, float Start, float Length, float Rate ) -void llSetTimerEvent( float Rate ) -void llSetTorque( vector Torque, integer Local ) -void llSetTouchText( string Text ) -void llSetVehicleFlags( integer Flags ) -void llSetVehicleFloatParam( integer ParameterName, float ParameterValue ) -void llSetVehicleRotationParam( integer ParameterName, rotation ParameterValue ) -void llSetVehicleType( integer Type ) -void llSetVehicleVectorParam( integer ParameterName, vector ParameterValue ) -void llSetVelocity( vector Velocity, integer Local ) -void llShout( integer Channel, string Text ) -string llSignRSA( string PrivateKey, string Message, string Algorithm ) -float llSin( float Theta ) -integer llSitOnLink( key AvatarID, integer LinkID ) -void llSitTarget( vector Offset, rotation Rotation ) -void llSleep( float Time ) -void llSound( string Sound, float Volume, integer Queue, integer Loop ) -void llSoundPreload( string Sound ) -float llSqrt( float Value ) -void llStartAnimation( string Animation ) -void llStartObjectAnimation( string Animation ) -void llStopAnimation( string Animation ) +void llSetParcelMusicURL( string url ) +void llSetPayPrice( integer price, list quick_pay_buttons ) +void llSetPhysicsMaterial( integer mask, float gravity_multiplier, float restitution, float friction, float density ) +void llSetPos( vector pos ) +integer llSetPrimMediaParams( integer face, list params ) +void llSetPrimURL( string url ) +void llSetPrimitiveParams( list rules ) +integer llSetRegionPos( vector position ) +void llSetRemoteScriptAccessPin( integer pin ) +void llSetRenderMaterial( string material, integer face ) +void llSetRot( rotation rot ) +void llSetScale( vector size ) +void llSetScriptState( string name, integer running ) +void llSetSitText( string text ) +void llSetSoundQueueing( integer queue ) +void llSetSoundRadius( float radius ) +void llSetStatus( integer status, integer value ) +void llSetText( string text, vector color, float alpha ) +void llSetTexture( string texture, integer face ) +void llSetTextureAnim( integer mode, integer face, integer sizex, integer sizey, float start, float length, float rate ) +void llSetTimerEvent( float sec ) +void llSetTorque( vector torque, integer is_local ) +void llSetTouchText( string text ) +void llSetVehicleFlags( integer flags ) +void llSetVehicleFloatParam( integer param, float value ) +void llSetVehicleRotationParam( integer param, rotation rot ) +void llSetVehicleType( integer type ) +void llSetVehicleVectorParam( integer param, vector vec ) +void llSetVelocity( vector velocity, integer is_local ) +void llShout( integer channel, string msg ) +string llSignRSA( string private_key, string msg, string algorithm ) +float llSin( float theta ) +integer llSitOnLink( key agent_id, integer link ) +void llSitTarget( vector offset, rotation rot ) +void llSleep( float sec ) +void llSound( string sound, float volume, integer queue, integer loop ) +void llSoundPreload( string sound ) +float llSqrt( float val ) +void llStartAnimation( string anim ) +void llStartObjectAnimation( string anim ) +void llStopAnimation( string anim ) void llStopHover( ) void llStopLookAt( ) void llStopMoveToTarget( ) -void llStopObjectAnimation( string Animation ) +void llStopObjectAnimation( string anim ) void llStopPointAt( ) void llStopSound( ) -integer llStringLength( string Text ) -string llStringToBase64( string Text ) -string llStringTrim( string Text, integer TrimType ) -integer llSubStringIndex( string Text, string Sequence ) -void llTakeCamera( key AvatarID ) -void llTakeControls( integer Controls, integer Accept, integer PassOn ) -float llTan( float Theta ) -integer llTarget( vector Position, float Range ) -void llTargetOmega( vector Axis, float SpinRate, float Gain ) -void llTargetRemove( integer Target ) -void llTargetedEmail( integer Target, string Subject, string Text ) -void llTeleportAgent( key AvatarID, string LandmarkName, vector Position, vector LookAtPoint ) -void llTeleportAgentGlobalCoords( key AvatarID, vector GlobalPosition, vector RegionPosition, vector LookAtPoint ) -void llTeleportAgentHome( key AvatarID ) -void llTextBox( key AvatarID, string Text, integer Channel ) -string llToLower( string Text ) -string llToUpper( string Text ) -key llTransferLindenDollars( key AvatarID, integer Amount ) -integer llTransferOwnership( key AgentID, integer Flags, list Params ) -void llTriggerSound( string Sound, float Volume ) -void llTriggerSoundLimited( string Sound, float Volume, vector TNE, vector BSW ) -void llUnSit( key AvatarID ) -string llUnescapeURL( string URL ) -void llUpdateCharacter( list Options ) -key llUpdateKeyValue( string Key, string Value, integer Checked, string OriginalValue ) -float llVecDist( vector Location1, vector Location2 ) -float llVecMag( vector Vector ) -vector llVecNorm( vector Vector ) -integer llVerifyRSA( string PublicKey, string Message, string Signature, string Algorithm ) -void llVolumeDetect( integer DetectEnabled ) -void llWanderWithin( vector Origin, vector Area, list Options ) -float llWater( vector Offset ) -void llWhisper( integer Channel, string Text ) -vector llWind( vector Offset ) +integer llStringLength( string str ) +string llStringToBase64( string str ) +string llStringTrim( string src, integer type ) +integer llSubStringIndex( string source, string pattern ) +void llTakeCamera( key avatar ) +void llTakeControls( integer controls, integer accept, integer pass_on ) +float llTan( float theta ) +integer llTarget( vector position, float range ) +void llTargetOmega( vector axis, float spinrate, float gain ) +void llTargetRemove( integer handle ) +void llTargetedEmail( integer target, string subject, string message ) +void llTeleportAgent( key agent, string landmark, vector position, vector look_at ) +void llTeleportAgentGlobalCoords( key agent, vector global_coordinates, vector region_coordinates, vector look_at ) +void llTeleportAgentHome( key avatar ) +void llTextBox( key avatar, string message, integer channel ) +string llToLower( string src ) +string llToUpper( string src ) +key llTransferLindenDollars( key destination, integer amount ) +integer llTransferOwnership( key agent_id, integer flags, list options ) +void llTriggerSound( string sound, float volume ) +void llTriggerSoundLimited( string sound, float volume, vector top_north_east, vector bottom_south_west ) +void llUnSit( key id ) +string llUnescapeURL( string url ) +void llUpdateCharacter( list options ) +key llUpdateKeyValue( string k, string v, integer checked, string original_value ) +float llVecDist( vector vec_a, vector vec_b ) +float llVecMag( vector vec ) +vector llVecNorm( vector vec ) +integer llVerifyRSA( string public_key, string msg, string signature, string algorithm ) +void llVolumeDetect( integer detect ) +void llWanderWithin( vector origin, vector dist, list options ) +float llWater( vector offset ) +void llWhisper( integer channel, string msg ) +vector llWind( vector offset ) vector llWorldPosToHUD( vector world_pos ) -string llXorBase64( string Text1, string Text2 ) -string llXorBase64Strings( string Text1, string Text2 ) -string llXorBase64StringsCorrect( string Text1, string Text2 ) +string llXorBase64( string str1, string str2 ) +string llXorBase64Strings( string str1, string str2 ) +string llXorBase64StringsCorrect( string str1, string str2 ) vector llsRGB2Linear( vector srgb ) const integer ACTIVE = 0x2 const integer AGENT = 0x1 @@ -910,6 +910,7 @@ const integer OBJECT_HEALTH = 50 const integer OBJECT_HOVER_HEIGHT = 25 const integer OBJECT_LAST_OWNER_ID = 27 const integer OBJECT_LINK_NUMBER = 46 +const integer OBJECT_LOCKED = 55 const integer OBJECT_MASS = 43 const integer OBJECT_MATERIAL = 42 const integer OBJECT_NAME = 1 @@ -949,6 +950,7 @@ const integer OBJECT_TOTAL_INVENTORY_COUNT = 31 const integer OBJECT_TOTAL_SCRIPT_COUNT = 10 const integer OBJECT_UNKNOWN_DETAIL = -1 const integer OBJECT_VELOCITY = 5 +const integer OBJECT_VOLUME_DETECT = 56 const integer OPT_AVATAR = 1 const integer OPT_CHARACTER = 2 const integer OPT_EXCLUSION_VOLUME = 6 @@ -1551,46 +1553,46 @@ const integer XP_ERROR_THROTTLED = 1 const integer XP_ERROR_UNKNOWN_ERROR = 10 const rotation ZERO_ROTATION = <0.0, 0.0, 0.0, 1.0> const vector ZERO_VECTOR = <0.0, 0.0, 0.0> -event at_rot_target( integer TargetNumber, rotation TargetRotation, rotation CurrentRotation ) -event at_target( integer TargetNumber, vector TargetPosition, vector CurrentPosition ) -event attach( key AvatarID ) -event changed( integer Changed ) -event collision( integer NumberOfCollisions ) -event collision_end( integer NumberOfCollisions ) -event collision_start( integer NumberOfCollisions ) -event control( key AvatarID, integer Levels, integer Edges ) -event dataserver( key RequestID, string Data ) -event email( string Time, string Address, string Subject, string Body, integer NumberRemaining ) +event at_rot_target( integer handle, rotation targetrot, rotation ourrot ) +event at_target( integer tnum, vector targetpos, vector ourpos ) +event attach( key id ) +event changed( integer change ) +event collision( integer num_detected ) +event collision_end( integer num_detected ) +event collision_start( integer num_detected ) +event control( key id, integer level, integer edge ) +event dataserver( key queryid, string data ) +event email( string time, string address, string subject, string message, integer num_left ) event experience_permissions( key agent_id ) -event experience_permissions_denied( key agent_id, integer Reason ) -event final_damage( integer count ) -event game_control( key id, integer buttons, list axes ) -event http_request( key HTTPRequestID, string HTTPMethod, string Body ) -event http_response( key HTTPRequestID, integer Status, list Metadata, string Body ) -event land_collision( vector Position ) -event land_collision_end( vector Position ) -event land_collision_start( vector Position ) -event link_message( integer SendersLink, integer Value, string Text, key ID ) +event experience_permissions_denied( key agent_id, integer reason ) +event final_damage( integer num_detected ) +event game_control( key id, integer button_levels, list axes ) +event http_request( key request_id, string method, string body ) +event http_response( key request_id, integer status, list metadata, string body ) +event land_collision( vector pos ) +event land_collision_end( vector pos ) +event land_collision_start( vector pos ) +event link_message( integer sender_num, integer num, string str, key id ) event linkset_data( integer action, string name, string value ) -event listen( integer Channel, string Name, key ID, string Text ) -event money( key Payer, integer Amount ) +event listen( integer channel, string name, key id, string message ) +event money( key id, integer amount ) event moving_end( ) event moving_start( ) event no_sensor( ) event not_at_rot_target( ) event not_at_target( ) -event object_rez( key RezzedObjectsID ) -event on_damage( integer count ) +event object_rez( key id ) +event on_damage( integer num_detected ) event on_death( ) -event on_rez( integer StartParameter ) -event path_update( integer Type, list Reserved ) -event remote_data( integer EventType, key ChannelID, key MessageID, string Sender, integer IData, string SData ) -event run_time_permissions( integer PermissionFlags ) -event sensor( integer NumberDetected ) +event on_rez( integer start_param ) +event path_update( integer type, list reserved ) +event remote_data( integer event_type, key channel, key message_id, string sender, integer idata, string sdata ) +event run_time_permissions( integer perm ) +event sensor( integer num_detected ) event state_entry( ) event state_exit( ) event timer( ) -event touch( integer NumberOfTouches ) -event touch_end( integer NumberOfTouches ) -event touch_start( integer NumberOfTouches ) -event transaction_result( key RequestID, integer Success, string Message ) +event touch( integer num_detected ) +event touch_end( integer num_detected ) +event touch_start( integer num_detected ) +event transaction_result( key id, integer success, string data ) diff --git a/init_debian_buster.sh b/init_debian_buster.sh index fe7736e6..bba6b3b6 100755 --- a/init_debian_buster.sh +++ b/init_debian_buster.sh @@ -29,10 +29,15 @@ apt-get install -y gcc-multilib g++-multilib cmake # Finally, autobuild pip3 --no-cache-dir install pydot==1.4.2 pyzstd==0.15.10 autobuild +update-alternatives --install /usr/bin/clang clang /usr/bin/clang-11 100 +update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-11 100 update-alternatives --install /usr/bin/cc cc /usr/bin/clang-11 100 update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-11 100 +update-alternatives --install /usr/bin/python python /usr/bin/python3 100 cd /root git clone https://github.com/secondlife/build-variables.git cd build-variables echo 'export AUTOBUILD_VARIABLES_FILE=/root/build-variables/variables' >> ~/.bashrc + +git config --global --add safe.directory $(pwd) diff --git a/tests/SLConformance.test.cpp b/tests/SLConformance.test.cpp index e52fd325..b86df875 100644 --- a/tests/SLConformance.test.cpp +++ b/tests/SLConformance.test.cpp @@ -3,6 +3,7 @@ #include "lualib.h" #include "luacode.h" #include "luacodegen.h" +#include "llfluent_builder.h" #include "Luau/BuiltinDefinitions.h" #include "Luau/DenseHash.h" @@ -1486,4 +1487,83 @@ TEST_CASE("Memory hygiene") runConformance("memory_hygiene.lua"); } +TEST_CASE("llparticle") +{ + static const FluentParamDescriptor kDescs[] = { + {"flags", 'i', 0}, + {"color_begin", 'v', 1}, + {"alpha_begin", 'f', 2}, + {"burst_rate", 'f', 13}, + }; + static const FluentFlagDescriptor kFlagDescs[] = { + {"color_interp", 0x1, 0}, + {"scale_interp", 0x2, 0}, + {"bounce", 0x4, 0}, + {"emissive", 0x100, 0}, + }; + static FluentBuilderDef* s_def = []() { + auto* d = fluent_builder_def_build(kDescs, std::size(kDescs)); + fluent_builder_def_add_flags(d, kFlagDescs, std::size(kFlagDescs)); + return d; + }(); + + runConformance("llprim_particle.lua", nullptr, [](lua_State* L) { + // Mock ll.LinkParticleSystem so the dispatch wrapper can be exercised. + static const luaL_Reg test_ll_lib[] = { + {"LinkParticleSystem", [](lua_State* L) -> int { + luaL_checkinteger(L, 1); + luaL_checktype(L, 2, LUA_TTABLE); + lua_pushvalue(L, 1); + lua_setglobal(L, "_captured_link"); + lua_pushvalue(L, 2); + lua_setglobal(L, "_captured_rules"); + return 0; + }}, + {nullptr, nullptr} + }; + luaL_register_noclobber(L, LUA_LLLIBNAME, test_ll_lib); + + auto particle_system = [](lua_State* L) -> int { + const auto* def = (const FluentBuilderDef*)lua_tolightuserdata(L, lua_upvalueindex(1)); + int link = lua_isnoneornil(L, 2) ? SLUA_LINK_THIS : luaL_checkinteger(L, 2); + slua_fluent_serialize(L, 1, def); + int rules_idx = lua_gettop(L); + lua_rawgetfield(L, LUA_BASEGLOBALSINDEX, "ll"); + lua_rawgetfield(L, -1, "LinkParticleSystem"); + lua_pushinteger(L, link); + lua_pushvalue(L, rules_idx); + lua_call(L, 2, 0); + return 0; + }; + slua_register_fluent_fn(L, "llprim", "ParticleSystem", particle_system, s_def); + }); +} + +TEST_CASE("fluent_builder_collection") +{ + // Synthetic descriptor table exercising the two collection semantics. + // tag 5: 'C' string-csv (like PRIM_MEDIA_WHITELIST) + // tag 7: 'M' string-map (like HTTP_CUSTOM_HEADER) + // tag 9: 's' plain string (control: must be unaffected) + static const FluentParamDescriptor kDescs[] = { + {"whitelist", 'C', 5}, + {"custom_headers", 'M', 7}, + {"label", 's', 9}, + }; + static FluentBuilderDef* s_def = []() { + return fluent_builder_def_build(kDescs, std::size(kDescs)); + }(); + + runConformance("fluent_builder_collection.lua", nullptr, [](lua_State* L) { + // Mock apply function: serialize params and store result as _captured_rules. + auto apply_fn = [](lua_State* L) -> int { + const auto* def = (const FluentBuilderDef*)lua_tolightuserdata(L, lua_upvalueindex(1)); + slua_fluent_serialize(L, 1, def); + lua_setglobal(L, "_captured_rules"); + return 0; + }; + slua_register_fluent_fn(L, "testbuilder", "apply", apply_fn, s_def); + }); +} + TEST_SUITE_END(); diff --git a/tests/conformance/fluent_builder_collection.lua b/tests/conformance/fluent_builder_collection.lua new file mode 100644 index 00000000..9c799df1 --- /dev/null +++ b/tests/conformance/fluent_builder_collection.lua @@ -0,0 +1,92 @@ +-- Conformance tests for 'C' (string-csv) and 'M' (string-map) fluent builder semantics. +-- +-- The C++ harness registers testbuilder.apply(params) which serializes params +-- using a synthetic descriptor table: +-- tag 5 'C' whitelist +-- tag 7 'M' custom_headers +-- tag 9 's' label + +local function apply(params) + testbuilder.apply(params) + return _captured_rules +end + +-- ─── 'C' string-csv ────────────────────────────────────────────────────────── + +-- Single URL: emits {tag, "url"}. +local r = apply({ whitelist = {"https://example.com"} }) +assert(#r == 2, "csv single: expected 2 slots, got " .. #r) +assert(r[1] == 5, "csv single: tag should be 5") +assert(r[2] == "https://example.com", "csv single: value mismatch") + +-- Multiple URLs: comma-joined. +r = apply({ whitelist = {"https://a.com", "https://b.com"} }) +assert(#r == 2, "csv multi: expected 2 slots") +assert(r[1] == 5, "csv multi: tag should be 5") +assert(r[2] == "https://a.com,https://b.com", "csv multi: value mismatch, got " .. tostring(r[2])) + +-- URL containing a comma: comma must be backslash-escaped. +r = apply({ whitelist = {"https://a.com/p?x=1,y=2"} }) +assert(#r == 2, "csv comma-escape: expected 2 slots") +assert(r[2] == "https://a.com/p?x=1\\,y=2", "csv comma-escape: value mismatch, got " .. tostring(r[2])) + +-- URL containing a backslash: backslash must be doubled. +r = apply({ whitelist = {"https://a.com/a\\b"} }) +assert(#r == 2, "csv backslash-escape: expected 2 slots") +assert(r[2] == "https://a.com/a\\\\b", "csv backslash-escape: value mismatch, got " .. tostring(r[2])) + +-- Empty array: tag must not appear in output. +r = apply({ whitelist = {} }) +assert(#r == 0, "csv empty: output should be empty, got " .. #r) + +-- Nil / unset: tag must not appear in output. +r = apply({}) +assert(#r == 0, "csv nil: output should be empty") + +-- Array index order is preserved in the CSV. +r = apply({ whitelist = {"first", "second", "third", "fourth", "fifth"} }) +assert(#r == 2, "csv order: expected 2 slots") +assert(r[2] == "first,second,third,fourth,fifth", "csv order: insertion order not preserved, got " .. tostring(r[2])) + +-- ─── 'M' string-map ────────────────────────────────────────────────────────── + +-- Single header: emits {tag, key, value}. +r = apply({ custom_headers = {["X-Foo"] = "bar"} }) +assert(#r == 3, "map single: expected 3 slots, got " .. #r) +assert(r[1] == 7, "map single: tag should be 7") +assert(r[2] == "X-Foo", "map single: key mismatch") +assert(r[3] == "bar", "map single: value mismatch") + +-- Multiple headers: tag appears once per pair, keys sorted. +r = apply({ custom_headers = {["Z-Last"] = "z", ["A-First"] = "a"} }) +assert(#r == 6, "map multi: expected 6 slots, got " .. #r) +assert(r[1] == 7 and r[4] == 7, "map multi: both entries must start with tag 7") +assert(r[2] == "A-First", "map multi: first key should be A-First (sorted), got " .. tostring(r[2])) +assert(r[3] == "a", "map multi: first value should be 'a'") +assert(r[5] == "Z-Last", "map multi: second key should be Z-Last, got " .. tostring(r[5])) +assert(r[6] == "z", "map multi: second value should be 'z'") + +-- Empty table: tag must not appear in output. +r = apply({ custom_headers = {} }) +assert(#r == 0, "map empty: output should be empty") + +-- Nil / unset: tag must not appear in output. +r = apply({}) +assert(#r == 0, "map nil: output should be empty") + +-- ─── Co-existence with scalar property ─────────────────────────────────────── + +-- All three descriptor types in one call. +r = apply({ + whitelist = {"https://x.com"}, + custom_headers = {["K"] = "V"}, + label = "hello", +}) +-- Expected output (tag-sorted: 5, 7, 9): +-- 5, "https://x.com", 7, "K", "V", 9, "hello" +assert(#r == 7, "mixed: expected 7 slots, got " .. #r) +assert(r[1] == 5 and r[2] == "https://x.com", "mixed: csv slot mismatch") +assert(r[3] == 7 and r[4] == "K" and r[5] == "V", "mixed: map slot mismatch") +assert(r[6] == 9 and r[7] == "hello", "mixed: string slot mismatch") + +return "OK" diff --git a/tests/conformance/llprim_particle.lua b/tests/conformance/llprim_particle.lua new file mode 100644 index 00000000..4f986549 --- /dev/null +++ b/tests/conformance/llprim_particle.lua @@ -0,0 +1,90 @@ +-- Conformance test for llprim.ParticleSystem(). +-- The C++ test harness registers a mock ll.LinkParticleSystem that stores +-- _captured_link and _captured_rules as globals, and registers llprim +-- with a subset of particle descriptors: +-- flags 'i' tag 0 (backing field for flag bits) +-- color_begin 'v' tag 1 +-- alpha_begin 'f' tag 2 +-- burst_rate 'f' tag 13 +-- Flag descriptors (all backed by tag 0): +-- color_interp 0x001 +-- scale_interp 0x002 +-- bounce 0x004 +-- emissive 0x100 + +-- Helper: call and return captured state. +local function dispatch(params, link) + if link ~= nil then + llprim.ParticleSystem(params, link) + else + llprim.ParticleSystem(params) + end + return _captured_link, _captured_rules +end + +-- Test 1: nil params emits empty rules list, link defaults to LINK_THIS. +local link, rules = dispatch(nil) +assert(link == LINK_THIS, "nil params: link should be LINK_THIS") +assert(type(rules) == "table", "nil params: rules should be a table") +assert(#rules == 0, "nil params: rules should be empty") + +-- Test 2: explicit link number is forwarded. +link, rules = dispatch(nil, 3) +assert(link == 3, "explicit link=3 should be forwarded") + +-- Test 3: scalar float property is serialized with its tag. +link, rules = dispatch({ burst_rate = 2.5 }) +assert(#rules == 2, "burst_rate: expected 2 elements") +assert(rules[1] == PSYS_SRC_BURST_RATE, "burst_rate: tag should be PSYS_SRC_BURST_RATE") +assert(rules[2] == 2.5, "burst_rate: value should be 2.5") + +-- Test 4: vector property is serialized correctly. +local col = vector(1, 0.5, 0.25) +link, rules = dispatch({ color_begin = col }) +assert(#rules == 2, "color_begin: expected 2 elements") +assert(rules[1] == PSYS_PART_START_COLOR, "color_begin: tag should be PSYS_PART_START_COLOR") +assert(rules[2] == col, "color_begin: value should be the vector") + +-- Test 5: single flag boolean is merged into the flags integer field. +link, rules = dispatch({ color_interp = true }) +assert(#rules == 2, "color_interp: expected 2 elements (flags field only)") +assert(rules[1] == PSYS_PART_FLAGS, "color_interp: tag should be PSYS_PART_FLAGS") +assert(rules[2] == PSYS_PART_INTERP_COLOR_MASK, "color_interp: value should be PSYS_PART_INTERP_COLOR_MASK") + +-- Test 6: multiple flag booleans accumulate into a single flags field entry. +link, rules = dispatch({ color_interp = true, bounce = true }) +assert(#rules == 2, "two flags: should still be one flags field entry") +assert(rules[1] == PSYS_PART_FLAGS, "two flags: tag should be PSYS_PART_FLAGS") +assert(rules[2] == bit32.bor(PSYS_PART_INTERP_COLOR_MASK, PSYS_PART_BOUNCE_MASK), + "two flags: both bits should be set") + +-- Test 7: raw flags integer and boolean flag properties are merged together. +link, rules = dispatch({ flags = PSYS_PART_EMISSIVE_MASK, bounce = true }) +assert(#rules == 2, "raw+boolean flags: should be one flags field entry") +assert(rules[1] == PSYS_PART_FLAGS, "raw+boolean flags: tag should be PSYS_PART_FLAGS") +assert(rules[2] == bit32.bor(PSYS_PART_EMISSIVE_MASK, PSYS_PART_BOUNCE_MASK), + "raw+boolean flags: both bits should be set") + +-- Test 8: multiple params serialize in ascending tag order. +link, rules = dispatch({ burst_rate = 1.0, color_begin = vector(1, 0, 0) }) +-- color_begin tag=1, burst_rate tag=13 — tag 1 must come first. +assert(#rules == 4, "two params: expected 4 elements") +assert(rules[1] == PSYS_PART_START_COLOR, "tag order: color_begin should come before burst_rate") +assert(rules[3] == PSYS_SRC_BURST_RATE, "tag order: burst_rate should be second") + +-- Test 9: a sole flag set to false still emits PSYS_PART_FLAGS = 0. +-- Validates the clear path: the flags field is emitted even when the merged value is 0. +link, rules = dispatch({ color_interp = false }) +assert(#rules == 2, "false flag only: expected 2 elements (flags field)") +assert(rules[1] == PSYS_PART_FLAGS, "false flag only: tag should be PSYS_PART_FLAGS") +assert(rules[2] == 0, "false flag only: value should be 0 (bit was cleared)") + +-- Test 10: a false flag clears its bit from the raw flags integer. +-- Validates that false both removes its own mask and leaves other bits intact. +link, rules = dispatch({ flags = bit32.bor(PSYS_PART_INTERP_COLOR_MASK, PSYS_PART_BOUNCE_MASK), color_interp = false }) +assert(#rules == 2, "false flag with raw: expected 2 elements") +assert(rules[1] == PSYS_PART_FLAGS, "false flag with raw: tag should be PSYS_PART_FLAGS") +assert(rules[2] == PSYS_PART_BOUNCE_MASK, + "false flag with raw: color_interp bit should be cleared, bounce bit should remain") + +return "OK" From 4ee72ad00135f34c0d3e3dba8b23d31ee9546e88 Mon Sep 17 00:00:00 2001 From: Harold Cindy <120691094+HaroldCindy@users.noreply.github.com> Date: Thu, 2 Jul 2026 16:46:56 -0700 Subject: [PATCH 2/3] Make testcases more straightforward to read --- .../conformance/fluent_builder_collection.lua | 104 +++++++---------- tests/conformance/llprim_particle.lua | 106 +++++++----------- 2 files changed, 78 insertions(+), 132 deletions(-) diff --git a/tests/conformance/fluent_builder_collection.lua b/tests/conformance/fluent_builder_collection.lua index 9c799df1..ac9f0f0f 100644 --- a/tests/conformance/fluent_builder_collection.lua +++ b/tests/conformance/fluent_builder_collection.lua @@ -6,87 +6,59 @@ -- tag 7 'M' custom_headers -- tag 9 's' label -local function apply(params) +local function check(params, expected) testbuilder.apply(params) - return _captured_rules + -- it's fine to compare the json-encoded rules because they will _always_ + -- be array-like tables with defined sort order. The whole intent of the functions + -- is to turn mappings into flat lists. + local got, want = lljson.slencode(_captured_rules), lljson.slencode(expected) + assert(got == want, `expected {want}, got {got}`) end -- ─── 'C' string-csv ────────────────────────────────────────────────────────── -- Single URL: emits {tag, "url"}. -local r = apply({ whitelist = {"https://example.com"} }) -assert(#r == 2, "csv single: expected 2 slots, got " .. #r) -assert(r[1] == 5, "csv single: tag should be 5") -assert(r[2] == "https://example.com", "csv single: value mismatch") - --- Multiple URLs: comma-joined. -r = apply({ whitelist = {"https://a.com", "https://b.com"} }) -assert(#r == 2, "csv multi: expected 2 slots") -assert(r[1] == 5, "csv multi: tag should be 5") -assert(r[2] == "https://a.com,https://b.com", "csv multi: value mismatch, got " .. tostring(r[2])) - --- URL containing a comma: comma must be backslash-escaped. -r = apply({ whitelist = {"https://a.com/p?x=1,y=2"} }) -assert(#r == 2, "csv comma-escape: expected 2 slots") -assert(r[2] == "https://a.com/p?x=1\\,y=2", "csv comma-escape: value mismatch, got " .. tostring(r[2])) - --- URL containing a backslash: backslash must be doubled. -r = apply({ whitelist = {"https://a.com/a\\b"} }) -assert(#r == 2, "csv backslash-escape: expected 2 slots") -assert(r[2] == "https://a.com/a\\\\b", "csv backslash-escape: value mismatch, got " .. tostring(r[2])) - --- Empty array: tag must not appear in output. -r = apply({ whitelist = {} }) -assert(#r == 0, "csv empty: output should be empty, got " .. #r) - --- Nil / unset: tag must not appear in output. -r = apply({}) -assert(#r == 0, "csv nil: output should be empty") - --- Array index order is preserved in the CSV. -r = apply({ whitelist = {"first", "second", "third", "fourth", "fifth"} }) -assert(#r == 2, "csv order: expected 2 slots") -assert(r[2] == "first,second,third,fourth,fifth", "csv order: insertion order not preserved, got " .. tostring(r[2])) +check({ whitelist = {"https://example.com"} }, {5, "https://example.com"}) + +-- Multiple URLs: comma-joined, array index order preserved. +check( + { whitelist = {"https://a.com", "https://b.com"} }, + {5, "https://a.com,https://b.com"} +) +check( + { whitelist = {"first", "second", "third", "fourth", "fifth"} }, + {5, "first,second,third,fourth,fifth"} +) + +-- Comma in a URL must be backslash-escaped; a backslash must be doubled. +check({ whitelist = {"https://a.com/p?x=1,y=2"} }, {5, "https://a.com/p?x=1\\,y=2"}) +check({ whitelist = {"https://a.com/a\\b"} }, {5, "https://a.com/a\\\\b"}) + +-- Empty array or unset: tag must not appear in output. +check({ whitelist = {} }, {}) +check({}, {}) -- ─── 'M' string-map ────────────────────────────────────────────────────────── -- Single header: emits {tag, key, value}. -r = apply({ custom_headers = {["X-Foo"] = "bar"} }) -assert(#r == 3, "map single: expected 3 slots, got " .. #r) -assert(r[1] == 7, "map single: tag should be 7") -assert(r[2] == "X-Foo", "map single: key mismatch") -assert(r[3] == "bar", "map single: value mismatch") +check({ custom_headers = {["X-Foo"] = "bar"} }, {7, "X-Foo", "bar"}) -- Multiple headers: tag appears once per pair, keys sorted. -r = apply({ custom_headers = {["Z-Last"] = "z", ["A-First"] = "a"} }) -assert(#r == 6, "map multi: expected 6 slots, got " .. #r) -assert(r[1] == 7 and r[4] == 7, "map multi: both entries must start with tag 7") -assert(r[2] == "A-First", "map multi: first key should be A-First (sorted), got " .. tostring(r[2])) -assert(r[3] == "a", "map multi: first value should be 'a'") -assert(r[5] == "Z-Last", "map multi: second key should be Z-Last, got " .. tostring(r[5])) -assert(r[6] == "z", "map multi: second value should be 'z'") - --- Empty table: tag must not appear in output. -r = apply({ custom_headers = {} }) -assert(#r == 0, "map empty: output should be empty") +check( + { custom_headers = {["Z-Last"] = "z", ["A-First"] = "a"} }, + {7, "A-First", "a", 7, "Z-Last", "z"} +) --- Nil / unset: tag must not appear in output. -r = apply({}) -assert(#r == 0, "map nil: output should be empty") +-- Empty table or unset: tag must not appear in output. +check({ custom_headers = {} }, {}) +check({}, {}) -- ─── Co-existence with scalar property ─────────────────────────────────────── --- All three descriptor types in one call. -r = apply({ - whitelist = {"https://x.com"}, - custom_headers = {["K"] = "V"}, - label = "hello", -}) --- Expected output (tag-sorted: 5, 7, 9): --- 5, "https://x.com", 7, "K", "V", 9, "hello" -assert(#r == 7, "mixed: expected 7 slots, got " .. #r) -assert(r[1] == 5 and r[2] == "https://x.com", "mixed: csv slot mismatch") -assert(r[3] == 7 and r[4] == "K" and r[5] == "V", "mixed: map slot mismatch") -assert(r[6] == 9 and r[7] == "hello", "mixed: string slot mismatch") +-- All three descriptor types in one call, tag-sorted output. +check( + { whitelist = {"https://x.com"}, custom_headers = {["K"] = "V"}, label = "hello" }, + {5, "https://x.com", 7, "K", "V", 9, "hello"} +) return "OK" diff --git a/tests/conformance/llprim_particle.lua b/tests/conformance/llprim_particle.lua index 4f986549..f3c09b97 100644 --- a/tests/conformance/llprim_particle.lua +++ b/tests/conformance/llprim_particle.lua @@ -12,79 +12,53 @@ -- bounce 0x004 -- emissive 0x100 --- Helper: call and return captured state. -local function dispatch(params, link) - if link ~= nil then - llprim.ParticleSystem(params, link) - else - llprim.ParticleSystem(params) - end - return _captured_link, _captured_rules +local function check(params, expected_rules) + llprim.ParticleSystem(params) + local got, want = lljson.slencode(_captured_rules), lljson.slencode(expected_rules) + assert(got == want, `expected {want}, got {got}`) end --- Test 1: nil params emits empty rules list, link defaults to LINK_THIS. -local link, rules = dispatch(nil) -assert(link == LINK_THIS, "nil params: link should be LINK_THIS") -assert(type(rules) == "table", "nil params: rules should be a table") -assert(#rules == 0, "nil params: rules should be empty") +-- Link handling: nil params emits empty rules and defaults to LINK_THIS; +-- an explicit link number is forwarded. +llprim.ParticleSystem(nil) +assert(_captured_link == LINK_THIS, `default link: got {_captured_link}`) +assert(lljson.slencode(_captured_rules) == "[]", "nil params: rules should be empty") +llprim.ParticleSystem(nil, 3) +assert(_captured_link == 3, `explicit link: got {_captured_link}`) --- Test 2: explicit link number is forwarded. -link, rules = dispatch(nil, 3) -assert(link == 3, "explicit link=3 should be forwarded") +-- Scalar float and vector properties are serialized with their tags. +check({ burst_rate = 2.5 }, {PSYS_SRC_BURST_RATE, 2.5}) +check( + { color_begin = vector(1, 0.5, 0.25) }, + {PSYS_PART_START_COLOR, vector(1, 0.5, 0.25)} +) --- Test 3: scalar float property is serialized with its tag. -link, rules = dispatch({ burst_rate = 2.5 }) -assert(#rules == 2, "burst_rate: expected 2 elements") -assert(rules[1] == PSYS_SRC_BURST_RATE, "burst_rate: tag should be PSYS_SRC_BURST_RATE") -assert(rules[2] == 2.5, "burst_rate: value should be 2.5") +-- Flag booleans merge into a single flags field entry. +check({ color_interp = true }, {PSYS_PART_FLAGS, PSYS_PART_INTERP_COLOR_MASK}) +check( + { color_interp = true, bounce = true }, + {PSYS_PART_FLAGS, bit32.bor(PSYS_PART_INTERP_COLOR_MASK, PSYS_PART_BOUNCE_MASK)} +) --- Test 4: vector property is serialized correctly. -local col = vector(1, 0.5, 0.25) -link, rules = dispatch({ color_begin = col }) -assert(#rules == 2, "color_begin: expected 2 elements") -assert(rules[1] == PSYS_PART_START_COLOR, "color_begin: tag should be PSYS_PART_START_COLOR") -assert(rules[2] == col, "color_begin: value should be the vector") +-- Raw flags integer and boolean flag properties are merged together. +check( + { flags = PSYS_PART_EMISSIVE_MASK, bounce = true }, + {PSYS_PART_FLAGS, bit32.bor(PSYS_PART_EMISSIVE_MASK, PSYS_PART_BOUNCE_MASK)} +) --- Test 5: single flag boolean is merged into the flags integer field. -link, rules = dispatch({ color_interp = true }) -assert(#rules == 2, "color_interp: expected 2 elements (flags field only)") -assert(rules[1] == PSYS_PART_FLAGS, "color_interp: tag should be PSYS_PART_FLAGS") -assert(rules[2] == PSYS_PART_INTERP_COLOR_MASK, "color_interp: value should be PSYS_PART_INTERP_COLOR_MASK") +-- Multiple params serialize in ascending tag order (color_begin tag=1 before burst_rate tag=13). +check( + { burst_rate = 1.0, color_begin = vector(1, 0, 0) }, + {PSYS_PART_START_COLOR, vector(1, 0, 0), PSYS_SRC_BURST_RATE, 1.0} +) --- Test 6: multiple flag booleans accumulate into a single flags field entry. -link, rules = dispatch({ color_interp = true, bounce = true }) -assert(#rules == 2, "two flags: should still be one flags field entry") -assert(rules[1] == PSYS_PART_FLAGS, "two flags: tag should be PSYS_PART_FLAGS") -assert(rules[2] == bit32.bor(PSYS_PART_INTERP_COLOR_MASK, PSYS_PART_BOUNCE_MASK), - "two flags: both bits should be set") +-- A sole flag set to false still emits PSYS_PART_FLAGS = 0 (the clear path). +check({ color_interp = false }, {PSYS_PART_FLAGS, 0}) --- Test 7: raw flags integer and boolean flag properties are merged together. -link, rules = dispatch({ flags = PSYS_PART_EMISSIVE_MASK, bounce = true }) -assert(#rules == 2, "raw+boolean flags: should be one flags field entry") -assert(rules[1] == PSYS_PART_FLAGS, "raw+boolean flags: tag should be PSYS_PART_FLAGS") -assert(rules[2] == bit32.bor(PSYS_PART_EMISSIVE_MASK, PSYS_PART_BOUNCE_MASK), - "raw+boolean flags: both bits should be set") - --- Test 8: multiple params serialize in ascending tag order. -link, rules = dispatch({ burst_rate = 1.0, color_begin = vector(1, 0, 0) }) --- color_begin tag=1, burst_rate tag=13 — tag 1 must come first. -assert(#rules == 4, "two params: expected 4 elements") -assert(rules[1] == PSYS_PART_START_COLOR, "tag order: color_begin should come before burst_rate") -assert(rules[3] == PSYS_SRC_BURST_RATE, "tag order: burst_rate should be second") - --- Test 9: a sole flag set to false still emits PSYS_PART_FLAGS = 0. --- Validates the clear path: the flags field is emitted even when the merged value is 0. -link, rules = dispatch({ color_interp = false }) -assert(#rules == 2, "false flag only: expected 2 elements (flags field)") -assert(rules[1] == PSYS_PART_FLAGS, "false flag only: tag should be PSYS_PART_FLAGS") -assert(rules[2] == 0, "false flag only: value should be 0 (bit was cleared)") - --- Test 10: a false flag clears its bit from the raw flags integer. --- Validates that false both removes its own mask and leaves other bits intact. -link, rules = dispatch({ flags = bit32.bor(PSYS_PART_INTERP_COLOR_MASK, PSYS_PART_BOUNCE_MASK), color_interp = false }) -assert(#rules == 2, "false flag with raw: expected 2 elements") -assert(rules[1] == PSYS_PART_FLAGS, "false flag with raw: tag should be PSYS_PART_FLAGS") -assert(rules[2] == PSYS_PART_BOUNCE_MASK, - "false flag with raw: color_interp bit should be cleared, bounce bit should remain") +-- A false flag clears its own bit from the raw flags integer, leaving other bits intact. +check( + { flags = bit32.bor(PSYS_PART_INTERP_COLOR_MASK, PSYS_PART_BOUNCE_MASK), color_interp = false }, + {PSYS_PART_FLAGS, PSYS_PART_BOUNCE_MASK} +) return "OK" From ab423e8365f3fa422f34aa2fb4b4944264937841 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Mon, 6 Jul 2026 17:52:25 +0000 Subject: [PATCH 3/3] Ruleset rename --- Sources.cmake | 4 +- ...llfluent_builder.h => llruleset_builder.h} | 46 +++++------ ...uent_builder.cpp => llruleset_builder.cpp} | 80 +++++++++---------- autobuild.xml | 8 +- build-cmd.sh | 2 +- tests/SLConformance.test.cpp | 34 ++++---- ...ion.lua => ruleset_builder_collection.lua} | 2 +- 7 files changed, 88 insertions(+), 88 deletions(-) rename VM/include/{llfluent_builder.h => llruleset_builder.h} (72%) rename VM/src/{llfluent_builder.cpp => llruleset_builder.cpp} (80%) rename tests/conformance/{fluent_builder_collection.lua => ruleset_builder_collection.lua} (96%) diff --git a/Sources.cmake b/Sources.cmake index 8347245f..0e245b2c 100644 --- a/Sources.cmake +++ b/Sources.cmake @@ -352,7 +352,7 @@ target_sources(Luau.VM PRIVATE VM/include/luaconf.h VM/include/lualib.h VM/include/llsl.h - VM/include/llfluent_builder.h + VM/include/llruleset_builder.h VM/src/ares.cpp VM/src/lapi.cpp @@ -430,7 +430,7 @@ target_sources(Luau.VM PRIVATE VM/src/llprim.cpp VM/src/llprim.h VM/src/llprim_set_primitive_params.inl - VM/src/llfluent_builder.cpp + VM/src/llruleset_builder.cpp VM/src/lyieldable.cpp VM/src/lstrbuf.cpp VM/src/lyieldstrlib.h diff --git a/VM/include/llfluent_builder.h b/VM/include/llruleset_builder.h similarity index 72% rename from VM/include/llfluent_builder.h rename to VM/include/llruleset_builder.h index cbef88d0..6d082e92 100644 --- a/VM/include/llfluent_builder.h +++ b/VM/include/llruleset_builder.h @@ -22,56 +22,56 @@ static const int SLUA_LINK_THIS = -4; // The consteval constructor ensures that only string literals can be used // to construct a LiteralString. -struct FluentParamDescriptor +struct RulesetParamDescriptor { const char* name; // effective property name (pretty alias or strict fallback) char semantic; // scalar: 'i'=integer 'f'=float 's'=string 'v'=vector 'r'=rotation 'b'=boolean 'a'=asset 'k'=key - // collection: 'C'=string-csv ({string} array → escaped comma-joined string, one tag/value pair) - // 'M'=string-map ({[string]:string} table → one tag/key/value triple per entry) - // 'N'=string-multi ({string} array → one tag/value pair per element, preserving order) + // collection: 'C'=string-csv ({string} array -> escaped comma-joined string, one tag/value pair) + // 'M'=string-map ({[string]:string} table -> one tag/key/value triple per entry) + // 'N'=string-multi ({string} array -> one tag/value pair per element, preserving order) int tag; // PSYS_* constant integer value }; -struct FluentFlagDescriptor +struct RulesetFlagDescriptor { const char* name; // boolean property name, e.g. "color_interp" int mask; // bitmask, e.g. 0x01 int field_tag; // tag of the integer field holding the bits, e.g. 0 for "flags" }; -struct FluentBuilderDef; +struct RulesetBuilderDef; -// Build a FluentBuilderDef from an array of descriptors. +// Build a RulesetBuilderDef from an array of descriptors. // The .name pointers in descs must have static storage duration (e.g. string literals). // Caller owns the returned pointer (process lifetime expected). // Descriptors are sorted by tag internally; caller order does not matter. -FluentBuilderDef* fluent_builder_def_build( - const FluentParamDescriptor* descs, - size_t count +RulesetBuilderDef* ruleset_builder_def_build( + const RulesetParamDescriptor* descs, + size_t count ); // Attach flag-bit boolean properties to an existing def. // Each descriptor maps a property name to a bitmask within the integer field at field_tag. // The .name pointers in descs must have static storage duration (e.g. string literals). -// Call after fluent_builder_def_build(). -void fluent_builder_def_add_flags( - FluentBuilderDef* def, - const FluentFlagDescriptor* descs, - size_t count +// Call after ruleset_builder_def_build(). +void ruleset_builder_def_add_flags( + RulesetBuilderDef* def, + const RulesetFlagDescriptor* descs, + size_t count ); // Serialize a params table into a flat tag/value rules list and push it onto the stack. -// params_idx is the stack index of the params table (may be nil — emits an empty list). +// params_idx is the stack index of the params table (may be nil -- emits an empty list). // Flag boolean properties are merged into their backing integer field before emission. -void slua_fluent_serialize(lua_State* L, int params_idx, const FluentBuilderDef* def); +void slua_ruleset_serialize(lua_State* L, int params_idx, const RulesetBuilderDef* def); // Register fn as module_name.fn_name in L's globals, with def stored as upvalue 1. // Creates the module table if it does not yet exist; adds to it if it does. // Sets the module table readonly after each call. -void slua_register_fluent_fn( - lua_State* L, - const char* module_name, - const char* fn_name, - lua_CFunction fn, - const FluentBuilderDef* def +void slua_register_ruleset_fn( + lua_State* L, + const char* module_name, + const char* fn_name, + lua_CFunction fn, + const RulesetBuilderDef* def ); diff --git a/VM/src/llfluent_builder.cpp b/VM/src/llruleset_builder.cpp similarity index 80% rename from VM/src/llfluent_builder.cpp rename to VM/src/llruleset_builder.cpp index 4e991822..318b89a4 100644 --- a/VM/src/llfluent_builder.cpp +++ b/VM/src/llruleset_builder.cpp @@ -1,4 +1,4 @@ -#define llfluent_builder_c +#define llruleset_builder_c #include #include @@ -9,12 +9,12 @@ #include "lcommon.h" #include "lualib.h" #include "llsl.h" -#include "llfluent_builder.h" +#include "llruleset_builder.h" -struct FluentBuilderDef +struct RulesetBuilderDef { - std::vector descs; // sorted by tag; .name points into string literals - std::vector flag_descs; + std::vector descs; // sorted by tag; .name points into string literals + std::vector flag_descs; }; namespace @@ -25,11 +25,11 @@ namespace // __tostring metamethod; everything else is skipped. // On success: replaces TOS with a Lua string and returns true. // On failure: pops TOS and returns false. - bool slua_fluent_to_string(lua_State* L) + bool slua_ruleset_to_string(lua_State* L) { int vtype = lua_type(L, -1); - // Native conversions — always well-defined. + // Native conversions -- always well-defined. if (vtype == LUA_TSTRING || vtype == LUA_TNUMBER || vtype == LUA_TBOOLEAN) { luaL_tolstring(L, -1, nullptr); @@ -53,8 +53,8 @@ namespace // Encodes a lua array as a CSV string; emit one tag/value pair. // returns the new index into the list table. - int slua_fluent_serialize_string_csv(lua_State* L, int list, int idx, - const FluentParamDescriptor& desc) + int slua_ruleset_serialize_string_csv(lua_State* L, int list, int idx, + const RulesetParamDescriptor& desc) { int n = (int)lua_objlen(L, -1); if (n > 0) @@ -63,7 +63,7 @@ namespace for (int j = 1; j <= n; ++j) { lua_rawgeti(L, -1, j); - if (slua_fluent_to_string(L)) + if (slua_ruleset_to_string(L)) { size_t len = 0; const char* s = lua_tolstring(L, -1, &len); @@ -100,8 +100,8 @@ namespace // Convert a lua table into a series of tag/key/value triples, one per entry, // sorted by key. // returns the new index into the list table. - int slua_fluent_serialize_string_map(lua_State* L, int list, int idx, - const FluentParamDescriptor& desc) + int slua_ruleset_serialize_string_map(lua_State* L, int list, int idx, + const RulesetParamDescriptor& desc) { // Collect keys for deterministic ordering. std::vector keys; @@ -118,7 +118,7 @@ namespace { lua_pushlstring(L, key.c_str(), key.size()); lua_rawget(L, -2); // push value - if (!slua_fluent_to_string(L)) + if (!slua_ruleset_to_string(L)) continue; lua_pushinteger(L, desc.tag); lua_rawseti(L, list, ++idx); @@ -133,14 +133,14 @@ namespace // Convert a lua array into a series of tag/value pairs, one per element, // preserving order. // returns the new index into the list table. - int slua_fluent_serialize_string_multi(lua_State* L, int list, int idx, - const FluentParamDescriptor& desc) + int slua_ruleset_serialize_string_multi(lua_State* L, int list, int idx, + const RulesetParamDescriptor& desc) { int n = (int)lua_objlen(L, -1); for (int j = 1; j <= n; ++j) { lua_rawgeti(L, -1, j); - if (slua_fluent_to_string(L)) + if (slua_ruleset_to_string(L)) { lua_pushinteger(L, desc.tag); lua_rawseti(L, list, ++idx); @@ -152,37 +152,37 @@ namespace } } -FluentBuilderDef* fluent_builder_def_build( - const FluentParamDescriptor* descs, - size_t count +RulesetBuilderDef* ruleset_builder_def_build( + const RulesetParamDescriptor* descs, + size_t count ) { - auto* def = new FluentBuilderDef; + auto* def = new RulesetBuilderDef; // Copy descriptors. .name pointers reference string literals with static - // storage duration — no deep copy needed. + // storage duration -- no deep copy needed. def->descs.assign(descs, descs + count); // Sort by tag for deterministic serialization order. std::sort(def->descs.begin(), def->descs.end(), - [](const FluentParamDescriptor& a, const FluentParamDescriptor& b) { + [](const RulesetParamDescriptor& a, const RulesetParamDescriptor& b) { return a.tag < b.tag; }); return def; } -void fluent_builder_def_add_flags( - FluentBuilderDef* def, - const FluentFlagDescriptor* descs, - size_t count +void ruleset_builder_def_add_flags( + RulesetBuilderDef* def, + const RulesetFlagDescriptor* descs, + size_t count ) { - // .name pointers reference string literals — no deep copy needed. + // .name pointers reference string literals -- no deep copy needed. def->flag_descs.assign(descs, descs + count); } -void slua_fluent_serialize(lua_State* L, int params_idx, const FluentBuilderDef* def) +void slua_ruleset_serialize(lua_State* L, int params_idx, const RulesetBuilderDef* def) { lua_newtable(L); int list = lua_gettop(L); @@ -239,7 +239,7 @@ void slua_fluent_serialize(lua_State* L, int params_idx, const FluentBuilderDef* continue; } - // 'C': string-csv — value is a Lua array of strings. + // 'C': string-csv -- value is a Lua array of strings. // Encode as comma-joined string; emit one tag/value pair. if (desc.semantic == 'C') { @@ -249,11 +249,11 @@ void slua_fluent_serialize(lua_State* L, int params_idx, const FluentBuilderDef* continue; } - idx = slua_fluent_serialize_string_csv(L, list, idx, desc); + idx = slua_ruleset_serialize_string_csv(L, list, idx, desc); continue; } - // 'M': string-map — value is a Lua string-keyed table. + // 'M': string-map -- value is a Lua string-keyed table. // Emit one tag/key/value triple per entry, in sorted key order. if (desc.semantic == 'M') { @@ -262,11 +262,11 @@ void slua_fluent_serialize(lua_State* L, int params_idx, const FluentBuilderDef* if (has_raw) lua_pop(L, 1); continue; } - idx = slua_fluent_serialize_string_map(L, list, idx, desc); + idx = slua_ruleset_serialize_string_map(L, list, idx, desc); continue; } - // 'N': string-multi — value is a Lua array of strings. + // 'N': string-multi -- value is a Lua array of strings. // Emit one tag/value pair per element, preserving order. if (desc.semantic == 'N') { @@ -275,7 +275,7 @@ void slua_fluent_serialize(lua_State* L, int params_idx, const FluentBuilderDef* if (has_raw) lua_pop(L, 1); continue; } - idx = slua_fluent_serialize_string_multi(L, list, idx, desc); + idx = slua_ruleset_serialize_string_multi(L, list, idx, desc); continue; } @@ -296,12 +296,12 @@ void slua_fluent_serialize(lua_State* L, int params_idx, const FluentBuilderDef* } } -void slua_register_fluent_fn( - lua_State* L, - const char* module_name, - const char* fn_name, - lua_CFunction fn, - const FluentBuilderDef* def +void slua_register_ruleset_fn( + lua_State* L, + const char* module_name, + const char* fn_name, + lua_CFunction fn, + const RulesetBuilderDef* def ) { int top = lua_gettop(L); diff --git a/autobuild.xml b/autobuild.xml index 3510e079..6981a6f1 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -102,11 +102,11 @@ archive hash - 5d173e32624eebc6adb4241874309e8e3dd49d72 + f8949fca1ee107be2b878f38b916d7fe254be03f hash_algorithm sha1 url - https://github.com/secondlife/lsl-definitions/releases/download/v0.6.10/lsl_definitions-0.6.10-common-28481241270.tar.zst + https://github.com/secondlife/lsl-definitions/releases/download/v0.6.11/lsl_definitions-0.6.11-common-28811066278.tar.zst name common @@ -117,9 +117,9 @@ license_file LICENSES/lsl_definitions.txt copyright - Copyright (c) 2026, Linden Lab + Copyright (c) 2026, Linden Research, Inc. version - 0.6.10 + 0.6.11 use_scm_version true name diff --git a/build-cmd.sh b/build-cmd.sh index df076335..5e768dd7 100755 --- a/build-cmd.sh +++ b/build-cmd.sh @@ -37,7 +37,7 @@ mkdir -p "$stage/lib/release" pushd "$top" pushd "VM/include" - cp -v lua.h luaconf.h lualib.h llsl.h llfluent_builder.h "$stage/include/luau/" + cp -v lua.h luaconf.h lualib.h llsl.h llruleset_builder.h "$stage/include/luau/" popd pushd "Compiler/include" cp -v luacode.h "$stage/include/luau/" diff --git a/tests/SLConformance.test.cpp b/tests/SLConformance.test.cpp index b86df875..099a0134 100644 --- a/tests/SLConformance.test.cpp +++ b/tests/SLConformance.test.cpp @@ -3,7 +3,7 @@ #include "lualib.h" #include "luacode.h" #include "luacodegen.h" -#include "llfluent_builder.h" +#include "llruleset_builder.h" #include "Luau/BuiltinDefinitions.h" #include "Luau/DenseHash.h" @@ -1489,21 +1489,21 @@ TEST_CASE("Memory hygiene") TEST_CASE("llparticle") { - static const FluentParamDescriptor kDescs[] = { + static const RulesetParamDescriptor kDescs[] = { {"flags", 'i', 0}, {"color_begin", 'v', 1}, {"alpha_begin", 'f', 2}, {"burst_rate", 'f', 13}, }; - static const FluentFlagDescriptor kFlagDescs[] = { + static const RulesetFlagDescriptor kFlagDescs[] = { {"color_interp", 0x1, 0}, {"scale_interp", 0x2, 0}, {"bounce", 0x4, 0}, {"emissive", 0x100, 0}, }; - static FluentBuilderDef* s_def = []() { - auto* d = fluent_builder_def_build(kDescs, std::size(kDescs)); - fluent_builder_def_add_flags(d, kFlagDescs, std::size(kFlagDescs)); + static RulesetBuilderDef* s_def = []() { + auto* d = ruleset_builder_def_build(kDescs, std::size(kDescs)); + ruleset_builder_def_add_flags(d, kFlagDescs, std::size(kFlagDescs)); return d; }(); @@ -1524,9 +1524,9 @@ TEST_CASE("llparticle") luaL_register_noclobber(L, LUA_LLLIBNAME, test_ll_lib); auto particle_system = [](lua_State* L) -> int { - const auto* def = (const FluentBuilderDef*)lua_tolightuserdata(L, lua_upvalueindex(1)); + const auto* def = (const RulesetBuilderDef*)lua_tolightuserdata(L, lua_upvalueindex(1)); int link = lua_isnoneornil(L, 2) ? SLUA_LINK_THIS : luaL_checkinteger(L, 2); - slua_fluent_serialize(L, 1, def); + slua_ruleset_serialize(L, 1, def); int rules_idx = lua_gettop(L); lua_rawgetfield(L, LUA_BASEGLOBALSINDEX, "ll"); lua_rawgetfield(L, -1, "LinkParticleSystem"); @@ -1535,34 +1535,34 @@ TEST_CASE("llparticle") lua_call(L, 2, 0); return 0; }; - slua_register_fluent_fn(L, "llprim", "ParticleSystem", particle_system, s_def); + slua_register_ruleset_fn(L, "llprim", "ParticleSystem", particle_system, s_def); }); } -TEST_CASE("fluent_builder_collection") +TEST_CASE("ruleset_builder_collection") { // Synthetic descriptor table exercising the two collection semantics. // tag 5: 'C' string-csv (like PRIM_MEDIA_WHITELIST) // tag 7: 'M' string-map (like HTTP_CUSTOM_HEADER) // tag 9: 's' plain string (control: must be unaffected) - static const FluentParamDescriptor kDescs[] = { + static const RulesetParamDescriptor kDescs[] = { {"whitelist", 'C', 5}, {"custom_headers", 'M', 7}, {"label", 's', 9}, }; - static FluentBuilderDef* s_def = []() { - return fluent_builder_def_build(kDescs, std::size(kDescs)); + static RulesetBuilderDef* s_def = []() { + return ruleset_builder_def_build(kDescs, std::size(kDescs)); }(); - runConformance("fluent_builder_collection.lua", nullptr, [](lua_State* L) { + runConformance("ruleset_builder_collection.lua", nullptr, [](lua_State* L) { // Mock apply function: serialize params and store result as _captured_rules. auto apply_fn = [](lua_State* L) -> int { - const auto* def = (const FluentBuilderDef*)lua_tolightuserdata(L, lua_upvalueindex(1)); - slua_fluent_serialize(L, 1, def); + const auto* def = (const RulesetBuilderDef*)lua_tolightuserdata(L, lua_upvalueindex(1)); + slua_ruleset_serialize(L, 1, def); lua_setglobal(L, "_captured_rules"); return 0; }; - slua_register_fluent_fn(L, "testbuilder", "apply", apply_fn, s_def); + slua_register_ruleset_fn(L, "testbuilder", "apply", apply_fn, s_def); }); } diff --git a/tests/conformance/fluent_builder_collection.lua b/tests/conformance/ruleset_builder_collection.lua similarity index 96% rename from tests/conformance/fluent_builder_collection.lua rename to tests/conformance/ruleset_builder_collection.lua index ac9f0f0f..18f428b8 100644 --- a/tests/conformance/fluent_builder_collection.lua +++ b/tests/conformance/ruleset_builder_collection.lua @@ -1,4 +1,4 @@ --- Conformance tests for 'C' (string-csv) and 'M' (string-map) fluent builder semantics. +-- Conformance tests for 'C' (string-csv) and 'M' (string-map) ruleset builder semantics. -- -- The C++ harness registers testbuilder.apply(params) which serializes params -- using a synthetic descriptor table: