Cool VL Viewer forum

View unanswered posts | View active topics It is currently 2017-03-29 11:09:27



This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 
Lua scripting and viewer automation feature 
Author Message

Joined: 2009-03-17 18:42:51
Posts: 3502
Starting with v1.26.20.4, the Cool VL Viewer implements Lua (v5.3) scripting/automation support. This thread lists the available features.

Changes brought to Lua scripting in the latest viewer release are colored in blue.

The Cool VL Viewer can execute Lua scripts (that, in their turn, may trigger some viewer actions) on events (e.g. chat, IMs, radar detections, etc), from the chat input line, or even from LSL scripts llOwnerSay()/llInstantMessage() (the latter feature is off by default).

  • On startup, the viewer attempts to read a Lua script ("automation.lua", by default; this file name is configurable via the "LuaAutomationScript" debug setting), from the user_settings/ directory, and executes it if found. The automation script is the only one that may contain callbacks for viewer events (i.e. the said event callbacks won't work if provided in a command line coming from the chat bar or LSL scripts).

  • You may, at any time, load a new automation script (which would then replace any already loaded script) with "Advanced" -> "Lua scripting" -> "Load automation script...").

  • You may, at any time, stop (i.e. disable) any currently loaded automation script with "Advanced" -> "Lua scripting" -> "Stop automation script").

  • You may, at any time, execute a Lua script file (which does not replace the automation script, but may interact with it) via "Advanced" -> "Lua scripting" -> "Execute a Lua script file...".

  • You may issue Lua command lines from the chat bar, by prefixing them with "/lua "; this prefix can be configured via the "LuaCommandPrefix" debug setting (it is recommended that you keep a "/" as the first character for your custom prefix, so that the viewer behaves like when you type a gesture name in the chat bar and does not send an "agent typing" event to the server which would then be relayed to other residents around you).

  • When enabled (via "Advanced" -> "Lua scripting" -> "Accept Lua from LSL scripts", which toggles the "LuaAcceptScriptCommands" debug setting), your own scripts may send Lua command lines via llOwnerSay("/lua ...") and/or llInstantMessage(llGetOwner(), "/lua ..."). Here again, the "/lua " prefix is configurable, independently from the chat bar prefix, via the "LuaScriptCommandPrefix" debug setting (you can see this as a sort of "password", and scripters that would like to distribute scripted items making use of the Cool VL Viewer Lua scripting feature shall provide a mean to configure that prefix in their scripted items).

  • Since the current implementation of the script engine runs in the main viewer loop, it must not block ("freeze") the latter, so there is a timeout watchdog mechanism implemented. You may adjust the timeout of Lua scripts/commands execution from 0.01s to 2s via the "LuaTimeout" debug setting (defaults to 0.2s which allows dozens of thousands of Lua instructions to be executed before the script would time out).

For security or compatibility reasons, the following Lua libraries (AKA "packages" in Lua jargon) are NOT available from the Cool VL Viewer Lua engine:
  • "package" (no "require" Lua command available and no third party Lua library loading allowed, for obvious security reasons)
  • "os" (because of the dangerous/blocking actions it would allow, such as "os.execute()", "os.exit()", etc).
  • "io" (because of the dangerous actions that could allow Lua scripts to overwrite important files, or read files to gather and re-emit/relay private info).
  • "debug" (because it would allow to bypass some of the above restrictions).
  • "coroutine" (because it is incompatible with the current, non-threaded implementation, and would block the viewer execution).
For the same security reasons, the load(), loadfile() and dofile() Lua base functions are also disabled.


Two global string variables are made available to all Lua scripts executed by the viewer:
  • VIEWER_NAME contains the viewer name ("Cool VL Viewer").
  • VIEWER_VERSION contains the viewer version (e.g. "1.26.20.5").


Here are the currently implemented viewer-specific Lua commands:
  • The print() Lua command is replaced with a viewer-specific one that prints into the viewer chat history and chat console. The printed messages are all prefixed with "Lua: " and colored with the "system" messages color.

  • IsAvatar(id) returns true if "id" indeed corresponds to an avatar currently present in the viewer's avatars list (i.e. an avatar present in the current sim or nearby simulators), or false otherwise.

  • IsObject(id) returns true if "id" indeed corresponds to an object currently present in the viewer's objects list (i.e. an object present in the current sim or nearby simulators), or false otherwise.

  • IsAgentFriend(id) returns two booleans, the first being true if "id" corresponds to an avatar part of the users' friends list, or false otherwise, the second boolean being true if the avatar is indeed a friend and currently online, or false otherwise.

  • IsAgentGroup(id) returns two booleans, the first being true if "id" corresponds to a group to which the user belongs, or false otherwise, the second boolean being true if it is the user's current active group (i.e. one of its group titles is set on the agent), or false otherwise.

  • AgentSit([object_id]) attempts to sit your avatar down on object which UUID is "object_id" (string type) or, when object_id is omitted, on the ground. Returns a boolean which is true on success or false on failure (object not found, sit restriction by RestrainedLove...).

  • AgentStand() causes your avatar to stand up when sat. Returns a boolean which is true on success or false on failure (sit restriction by RestrainedLove...).

  • SetAgentTyping([true|false]) sends an "agent typing" event to the server when no parameter or true is used as an argument, or an "agent stopped typing" event when false is used as an argument.

  • SendChat(text[, type]) sends text (string or string convertible type) in chat, as a "say" (normal, chat range), without animating your avatar with the corresponding chat anim (head nodding for normal chat range). With the string variable/parameter "type", you may specify different chat type. E.g. "whisper animate" will cause your avatar to whisper and play the whispering anim. Recognized keywords for "type" are: "whisper", "normal", "shout" and "animate" (case sensitive !).
    NOTE: SendChat() may be used to send text to scripts on non-zero channels (i.e. not on main chat), simply by prefixing the text with "/channel_number", just like what you would do in the chat bar (e.g. "/-1 This is sent to channel -1 and any scripted object listening to your avatar on that channel").

  • GetIMSession(target_id) returns the session id for the IM session opened for "target_id" (string type), which may be an avatar or group id, and if such a session does not exist, it is created/opened. You should let some time (a few seconds) before using the session id in a SendIM() (see below), so that the viewer gets a chance to open and connect to the session (especially for group session). You may achieve such a delay via the CallbackAfter() function (see below).

  • SendIM(session_id, text) sends "text" (string or string convertible type) to the IM session which id is held in "session_id" (string type).

  • BrowseToURL(url[, browser_type]) loads "url" in the Web browser. When browser_type is "0" or omitted, the built-in or external (system) browser is used according to the viewer preferences (as set in "Preferences" -> "Network & Web"), if it is "1" then the built-in browser is used, and if it is 2 the external (system) browser is used.

  • DispatchSLURL(slurl[, trusted]) causes the viewer to "dispatch" (i.e. decode and act upon) the SLURL passed as the "slurl" parameter (string type), as a "non-trusted" one (default or when "trusted" evaluates to false), or as a trusted one when "trusted" evaluates to a true boolean. This is a very powerful command allowing to display all sorts of floaters (landmark info, avatar info, group info, experiences info, inspect object/avatars info, search, pay floater prompt, web pages displaying, etc, etc), and to trigger actions (start IM, TP, wear inventory folder, etc). I'm debating whether making all dispatch requests un-trusted when they come from LSL scripts (some dispatch actions are unavailable to un-trusted SLURLs), even though the latter scripts are always scripts you own (feel free to advocate for or against it in in the feature request thread linked to below).

  • CallbackAfter(delay, SomeFunction[, func_param1[, ...]]) is only available in the automation script, and instructs the viewer to call back SomeFunction() after "delay" seconds (clamped to 1.0 second minimum, to avoid abuses), passing the optional parameters (func_param1 ... func_paramN) listed after the function name. SomeFunction() must be a function defined in the automation script. The number of parameters is limited only by the stack size (20 per default in Lua v5.3), minus three slots.

  • GetDebugSetting(setting_name) attempts to retrieve the value of a debug setting variable (only integer, boolean, float and string types variables are currently supported). This fails (and returns nil) if the setting name is not white-listed in either the "LuaSettingsWhiteList" or "LuaSettingsReadOnlyWhiteList" debug setting variables.

  • SetDebugSeting(setting_name, value) attempts to set the value of a debug setting variable (only integer, boolean, float and string types variables are currently supported). This fails (and returns false instead of true on success) if the setting name is not white-listed in the "LuaSettingsWhiteList" debug setting variable.

  • GetFrameTimeSeconds() returns a float representing the number of seconds elapsed since the beginning of the current viewer session (it's the viewer's gFrameTimeSeconds global variable value).

  • GetTimeStamp([tz, [format]]) returns a time stamp string containing the date and time for the "tz" time zone (integer type, UTC time zone by default) formatted according to "format" (string type), or to the format defined in the viewer preferences ("Cool features", "User interface") by default. E.g.: GetTimeStamp(-8,"%Y-%m-%d %H:%M:%S") returns the time in the PST (SL) time zone in the year-month-date and 24 hours clock format.

  • GetClipBoardString() returns the current string contained in the paste string buffer of the viewer (i.e. any string that got copied into the clipboard from a viewer input line or text editor).

  • GetCameraMode() returns the current camera mode (corresponding to the ECameraMode enumeration in indra/newview/llagent.h).

  • TeleportAgentToPos(x, y[, z]) takes global grid coordinates (the altitude, "z", may be omitted) and attempts to teleport your avatar over there. This function does not return anything.

  • GetGridSimAndPos() returns a Lua table with the following "key"/value pairs: the "grid" name (string value), the "region" name (string value), the region "width" (Lua number value) in meters (always 256 in SL, maybe more in OpenSim, for "var regions"), the "water_height" (Lua number value) in meters, the region "flags" (integer value, corresponding to the flags defined in indra/llmessage/llregionflags.h), the avatar global position as "global_x" and "global_y" (Lua number values) in meters, the avatar local position in the region as "local_x" and "local_y" (Lua number values) in meters, and finally the avatar "altitude" (Lua number value) in meters.

  • GetParcelInfo() returns a Lua table with the following "key"/value pairs: the "name" (string value) of the parcel the agent is currently in, its "description" (string value), its "flags" (integer value, corresponding to the flags defined in indra/llinventory/llparcelflags.h), a "build" Lua boolean which is true when the agent can build on the parcel, a "damage" Lua boolean which is true when the parcel got agent damage enabled, a "fly" Lua boolean which is true when flying over the parcel is permitted, a "push" Lua boolean which is true when scripted pushing is permitted, a "see" Lua boolean which is true when agents inside the parcel can see and chat with agents outside it, a "voice" Lua boolean which is true when voice chat is available.

  • UseRegionTime([true|false]) sets Windlight to use region time (when true or no parameter is passed), or to be overridden when false is passed. Please, note that for custom Windlight sky settings to be properly and fully applied, the region time must be overridden. This function does not return anything.
    NOTE: if called before login happens, this function does nothing (since Windlight is not yet enabled at this point).

  • ApplySkySettings(settings_name) attempts to load and apply Windlight sky settings "settings_name" (string value, case sensitive). This function returns true when successful and false when it failed to find and load the settings. The following settings names got a special meaning (they are equivalent to using the entries for the presets in the "World" -> "Environment settings" sub-menu): "region" reverts the sky settings to region default, "sunrise", "noon", "sunset" and "midnight" set the region time to the corresponding fixed time.
    NOTE 1: if called before login happens, this function does nothing (since Windlight is not yet enabled at this point) and returns false.
    NOTE 2: this function makes an implicit call to UseRegionTime(false) so to ensure that region time is overridden.

  • ApplyWaterSettings(settings_name) attempts to load and apply Windlight water settings "settings_name" (string value, case sensitive). This function returns true when successful and false when it failed to find and load the settings.
    NOTE: if called before login happens, this function does nothing (since Windlight is not yet enabled at this point) and returns false.

  • DerenderObject([object_id[, true|false]]) derenders an in-world object (already rezzed/around or not) which UUID is object_id (when true or no boolean is passed) or removes it from the derendered objects list when false is passed. If no parameter is passed at all to this function, it clears the whole derendered objects list. This function returns true on success or false on failure.
    NOTE 1: even once cleared from the derendered objects list, a formerly derendered object will render again only after the server sends an object update data packet, which may not occur at all till you leave the region and come back later into it (only scripted and moving objects may cause regular server update packets to be emitted).
    NOTE 2: objects attached to your avatar cannot be derendered (the function aborts and returns false if you try).
    NOTE 3: objects your avatar is sitting onto while under RestrainedLove sit restrictions would fail to derender (the function aborts and returns false if you try).

  • FindInventoryObject(item) accepts an "item" string parameter containing the full path (case sensitive,with "|" used as folders separator) of an item in the inventory, or an item UUID, and returns a Lua table containing the following keys and associated values: "id" key with the item UUID as a string value (a null UUID if the item was not found), "name" key, with the item name as a string value (the "item" string parameter contents is returned here if the item was not found), "type" key with the item asset type (as defined in indra/llcommon/llassettype.h) as a value (-1 = AT_NONE if the item was not found), such as 8 (AT_CATEGORY) for a folder ("category" in LL's viewer code jargon), and three (or four in OpenSim grids with export permission support) boolean values with keys "copy_ok", "mod_ok" and "xfer_ok" (and "export_ok" for OpenSim grids with export permission support) to reflect the current owner (your avatar) permissions.

  • GiveInventory(avatar_id, inventory_item_path_and_name) attempts to give an inventory item to the resident corresponding to avatar_id. The inventory item may be a single item (object, notecard, texture, etc) or an inventory folder. The inventory_item_path_and_name parameter must represent the full path (case sensitive, with "|" used as folders separator) of the item in the inventory. The usual restrictions (item permissions with confirmation dialog for no-copy items, number of item in given folder, RestrainedLove restrictions...) apply. Returns true on "success" (this is only the success to find the corresponding inventory item and send the give order to the server: what happens after the order is given, such as a failure to deliver the item or a decline from the recipient, are not known when this function returns to the Lua script), false otherwise.

  • MakeInventoryLink(item_to_link, new_link_parent_folder) takes the full path of the item to link and of the folder where to create the corresponding link (case sensitive, with "|" used as folders separator), and creates the said link. It returns a boolean: false on failure (bad paths, bad item type since not every item type may be linked) and true on "success" (this is only the success to send the link creation request to the server after validating every requirement, but the asset server may still fail to create the link in case of lag or usual SL inventory issues...).

  • DeleteInventoryLink(link_item) takes the full path of an existing link item and deletes it if found. Returns a boolean: false on failure (link item not found) and true on "success" (see MakeInventoryLink() above for the actual meaning of "success").

  • NewInventoryFolder(parent_folder_path, new_folder_name) takes the full path where to create the new folder plus a name for the new folder and creates the latter. Returns a boolean: false on failure (parent folder not found) and true on "success" (see MakeInventoryLink() above for the actual meaning of "success").

  • ListInventoryFolder(path) takes a full path for the folder to list the contents from, and returns either nil (folder not found) or a Lua table containing key/value pairs with inventory items UUIDs as the keys and their name as the values; the names of folders are postfixed with a trailing "|" (path separator) so to easily distinguish them from non-folder items and make it easy to concatenate their name with item names.

  • OpenNotification(type, text) opens a notification to display "text" (string or string convertible type). A "type" (integer type or integer convertible type) of 0 opens a notify tip, 1 opens a pull-down notification, 2 opens a (non-modal) alert box. In order to prevent fake "official" notification attempts, pull-down notifications and notification tips got a green background and the alert boxes bear a "Lua alert" title.

  • OpenFloater(name[, target]) opens a specific floater with optional target (an UUID or a number/index passed as a string) for floaters needing or supporting it. Currently recognized names are "area search", "avatar info" (needs an avatar id), "camera controls", "chat", "debug settings", "experiences", "friends", "group info" (needs a group id), "groups", "inspect" (needs an object or avatar id), "instant messages", "inventory", "map", "media filter", "mini map", "mute list" (with optional avatar or object id), "nearby media", "notifications", pathfinding "characters" and "linksets", "preferences" (takes an optional preferences tab index, from 0 to 11), "pushes", "radar", "search", "snapshot", "sounds list", "teleport history".

  • MakeDialog(title, text, suggestion, button1label, button2label, button3label, button1command, button2command, button3command) opens a dialog floater with "title" used as its title, "text" displayed in a text box (actually, an non-editable text editor with word-wrapping and URL parsing enabled), "suggestion" stuffed in an input-line (when "suggestion" is a single space, the input line is hidden, and when it is a single "*" the input line is setup as a password input), buttonNlabel are text labels for each of the three buttons (when a label is empty, the button is hidden), and buttonNcommand are Lua script commands associated with each button, with "GetDialogInput()", "SetDialogInput(text)" and "DialogClose()" commands added/recognized ("GetDialogInput()" returns the text contained in the input line, "SetDialogInput(text)" sets the dialog input text, and "DialogClose()" causes the dialog to be closed after the rest of the Lua command line is executed). Also, closing a Lua dialog opened with this command causes the OnLuaDialogClose() callback (see below) to be called when it exists in the automation script.
    NOTE: the buttonNcommand commands are executed in their own Lua context (AKA "state", in Lua jargon).

  • OverlayBarLuaButton(label, command[, tooltip]) sets up an overlay bar button (one such button is dedicated for Lua scripts) with "label" as its label and "command" as the Lua command line to execute when the button is clicked, and an optional "tooltip" string. Passing an empty command string removes the button from the overlay bar.
    NOTE 1: the command associated with the button is executed in its own Lua context (AKA "state", in Lua jargon).
    NOTE 2: when the automation script is stopped (via the corresponding entry in the Advanced menu), any existing overlay bar Lua button is removed.

  • StatusBarLuaIcon(command[, tooltip]) sets up the status bar Lua icon, with "command" as the Lua command line to execute when the icon is clicked, and an optional "tooltip" string. Passing an empty command string removes the Lua icon from the status bar.
    NOTE: when the automation script is stopped (via the corresponding entry in the Advanced menu), any existing status bar Lua icon is removed.

  • SideBarButton(number[, icon_or_label, command[, tooltip]]]) sets up the Lua side bar button referenced via its "number" (1 to 12), with "icon_or_label" as the icon/label (file name corresponding to a skin texture name suitable for use as an icon, or up to two characters, including UTF-8 special symbols, to use as a button label), "command" as the Lua command line to execute when the button is clicked (with "nop" recognized as a special no-operation command, mainly useful for toggle control buttons not needing a callback action), and an optional "tooltip" string. If only the icon number is specified, the button is removed from the side bar while if "command" is specified as an empty string, and provided the button was already defined, the icon_or_label and optional tooltip are modified without changing the existing command (which allows to change a side bar button aspect without modifying its action).
    NOTE 1: icons are searched for in the skin folder and skin overlay folders, including the skin/default/textures/ folder that you can add in the Second Life data folder ("~/.secondlife/" under Linux, and something akin to "C:\documents and settings\users\username\application data\second life\" under Windows: see the SL Wiki for the location of that folder with regards to your specific OS and OS "flavour"). Also, by default, icons are centered in the button. You may however instruct this function to justify the icon left or right, by prefixing its file name with either "left|" or "right|".
    NOTE 2: by default, the side bar appears on the right side of the viewer window, but it you prefer, you may place it on the left side via the "Advanced" -> "User interface" -> "Lua side-bar on left" toggle.
    NOTE 3: because the viewer tool bars and root view are initialized only after login, any SideBarButton() invocation performed before your avatar is logged in (e.g. from the main body of automation.lua) would be ignored; you should therefore use the OnLogin() callback to initialize side bar buttons from the automation script (see the example scripts below).
    NOTE 4: when the automation script is stopped (via the corresponding entry in the Advanced menu), all existing side bar Lua buttons are removed.

  • SideBarButtonToggle(number[, toggle]) returns the toggle state of the already defined side bar button "number" and may be used to change its toggle state or control. When "toggle" is omitted, this function simply returns the toggled state as a boolean or nil when the button is not a toggle one. When "toggle" is a Lua boolean (i.e. true or false), the button is changed into a toggle button if not already one, and its toggled state is changed to on (when true is passed) or off (when false is passed). When "toggle" is a non-empty string and does correspond to a boolean debug setting which was white-listed via the "LuaSettingsWhiteList" debug setting variable, then the button is changed to a toggle button (if not already one) which reflects and toggles the said boolean debug setting value. When "toggle" is an empty string or nil, the toggle control and toggle state of the button are reset to none.

  • SideBarButtonHide(number[, true|false]) hides (without changing its icon/label/tooltip or associated command) the Lua button numbered "number" in the side bar, or shows it when passed false.

  • SideBarButtonDisable(number[, true|false]) disables (without changing its icon/label/tooltip or associated command, and without hiding it) the Lua button numbered "number" in the side bar, or enables it when passed false.

  • SideBarHide([true|false]) hides the full Lua side bar or shows it when passed false.

  • SideBarHideOnRightClick([true|false]) causes the side bar to hide whenever any of its buttons is right-clicked, or disables this feature when passed false.

  • AutomationMessage(message) is only available from Lua command lines sent by LSL scripts or Lua dialog/overlay bar/status bar/side bar buttons, or via the chat lines (this function is not defined for the automation script). It sends "message" (string or string convertible Lua type) to the OnAutomationMessage() callback of the automation script, thus allowing the former command lines to send data/feedback to the latter script. It is especially handy for use in conjunction with OverlayBarLuaButton(), StatusBarLuaIcon() and SideBarButton().

  • PlayUISound(sound_name[, force]) plays an UI sound (valid, case-sensitive, Lua strings for "sound_name" are: Alert, BadKeystroke, Click, ClickRelease, HealthReductionF, HealthReductionM, InvalidOp, MoneyChangeDown, MoneyChangeUp, NewIncomingIMSession, ObjectCreate, ObjectDelete, ObjectRezIn, ObjectRezOut, PieMenuAppear, PieMenuHide, PieMenuSliceHighlight, Snapshot, StartIM, TeleportOut, Typing, WindowClose, WindowOpen). When "force" (Lua boolean) is passed and true, the UI sound is played regardless of user preferences (as set in "Preferences" -> "Audio & media" -> "Audio") about allowed UI sounds.

  • SetGlobalData(data) is only available from the automation script and it stores an arbitrary "data" string inside a reserved global debug setting of the viewer, thus allowing to save data/settings for use in a next viewer session. "data" may also be a simple Lua table: valid "simple" tables are made up key/value pairs, where the key is either a number (or an implicit index when the key is omitted) and a Lua string, number, boolean or nil value; in this case, the function automatically serializes the table and converts it into a base64 value before storing the latter into the reserved global debug setting of the viewer.

  • GetGlobalData() is only available from the automation script and it returns the string or (automatically deserialized and restored) table stored inside a reserved global debug setting of the viewer by the last SetGlobalData() invocation, thus allowing to restore data/settings on a new session start

  • SetPerAccountData(data) is only available from the automation script and it stores an arbitrary "data" string inside a reserved per-account debug setting of the viewer, thus allowing to save data/settings for use in a next viewer session with the same avatar. "data" may also be a simple Lua table: valid "simple" tables are made up key/value pairs, where the key is either a number (or an implicit index when the key is omitted) and a Lua string, number, boolean or nil value; in this case, the function automatically serializes the table and converts it into a base64 value before storing the latter into the reserved per-account debug setting of the viewer.

  • GetPerAccountData() is only available from the automation script and it returns the string or (automatically deserialized and restored) table stored inside a reserved per-account debug setting of the viewer by the last SetPerAccountData() invocation, thus allowing to restore data/settings on a new session start with the same avatar.

  • RenderDebugInfo(feature) allows to toggle debug render info features (it is not possible to get several features active at the same time via this function), such as listed in the "Advanced" -> "Rendering" -> "Info display", "Advanced" -> "World" (for wind vectors) and "Advanced" -> "Character" -> "Character debugging". The "feature" number must be an integer from 0 (features all toggled off) to 32 and corresponds to the index (starting from 1, not 0) of the feature in the LLRenderDebugMask enumeration (defined in indra/newview/llpipeline.h).
    NOTE: some features are not available/enabled in the Cool VL viewer and some names are, alas, rather missleading (e.g. the feature corresponding to the avatar complexity score is "RENDER_DEBUG_AVATAR_DRAW_INFO" and not "RENDER_DEBUG_RENDER_COMPLEXITY"). Especially interesting feature numbers are 18 (avatar complexity info) and 30 ("RENDER_DEBUG_ATTACHMENT_INFO" corresponding to attachments memory usage and surface area info).


Here are the currently recognized callbacks for use in the Lua automation script, triggered by viewer events:
  • OnLogin(location) is called on successful login into a grid. The "location" parameter is a Lua table containing the same data as what GetGridSimAndPos() returns. This callback shall not return any value.

  • OnTPStateChange(state, failure_reason) is called each time the teleporting state changes. "state" receives an integer value (corresponding to the ETeleportState enumeration defined in indra/newview/llagent.h) and, when the state change corresponds to a teleport failure, the "failure_reason" is transmitted as a string (it can be some plain text returned by the server, or a key corresponding to the pre-defined failure reasons defined in indra/newview/skins/default/xui/en-us/teleport_strings.xml for the "errors" message_set). This callback shall not return any value.

  • OnFailedTPSimChange(number_of_residents, x, y, z) is called each time the number of residents changes in the sim that you tried and failed to teleport to. The x,y,z coordinates are the coordinates of the failed TP and may be used directly with TeleportAgentToPos() to retry the TP. This callback shall not return any value.

  • OnParcelChange(parcel) is called whenever the agent moves to a new parcel. The "parcel" parameter is a Lua table containing the same data as what GetParcelInfo() returns. This callback shall not return any value.

  • OnWindlightChange(sky_settings_name, water_settings_name) is called each time the Windlight or water presets are changed (other than via the ApplySkySettings() and ApplyWaterSettings() functions). The name of the changed preset (either the sky or the water one) is passed (the other is empty). This callback shall not return any value. Also, the following settings names are passed (in sky_settings_name) whenever the presets in the "World" -> "Environment settings" sub-menu are used: "region" when the sky settings got reverted to region default, "sunrise", "noon", "sunset" and "midnight" when the region time got set to the corresponding fixed time.

  • OnCameraModeChange(mode) is called each time the agent camera mode changes. "mode" is an integer corresponding to the ECameraMode enumeration defined in indra/newview/llagent.h. This callback shall not return any value.

  • OnAgentPush(pusher_id, push_type, magnitude) is called each time your avatar is pushed by a new item/agent (i.e. the same type of push will not trigger repeated OnAgentPush() events when they occur again later). "pusher_id" is the UUID of the origin of the push (avatar id or object id), push_type is an integer reflecting the type of the push (as defined in indra/newview/llmeancollisiondata.h for EMeanCollisionType: 1 for avatar bumps, 2 for object pushes, 3 for selected objects pushes, 4 for scripted pushes, 5 for physical objects pushes) and "magnitude" represents the force of the push. This callback shall not return any value.

  • OnReceivedChat(type, from_id, is_avatar, name, text) is called each time the viewer receives a chat line. "type" is a positive number (corresponding to the e_chat_type enum in indra/newview/llchat.h) with 0 for whispers, 1 for normal avatar chat or scripted objects llInstantMessage(), 2 for shouts, 8 for llOwnerSay(). "from_id" is the UUID of the chat originator, is_avatar is a boolean which is true when the originator is an avatar, "name" is the originator object/avatar name and "text" is the full chat line (name included). This callback shall not return any value.

  • OnSendChat(text) is called each time you send a chat text from the chat bar(s). The text is passed to the callback (with the ':' to '/me ' substitution already done if needed) and the callback may modify that text if needed. The callback shall return the text to actually be sent by the viewer (i.e. you can use it as a way to substitute sub-strings, or filter text, or even as a command hook). If an empty string is returned, then the viewer will not emit any chat (handy for command hooks). Examples of a pseudo-gesture with parameter and of a draw distance setting command are given in the sample automation script below.

  • OnInstantMsg(session_id, origin_id, type, name, text) is called each time an instant message is received. "type" is an integer value reflecting the IM session type: 0 for peer to peer sessions (private IMs), 1 for group sessions, 2 for conferences. The other parameters are obvious. This callback shall not return any value.

  • OnRadar(avatar_id, name, range) is called each time the radar announces an incoming avatar (provided it was configured to do so !). "range" is a positive integer: 0 for an arrival in the sim, beyond draw distance, 1 for an arrival within draw distance, 2 for shout range, 3 for chat range. This callback shall not return any value.

  • OnPieMenu(object_id) is called each time an object or avatar is right-clicked in-world (causing the pie menu to pop up), with object_id (Lua string) containing the clicked object/avatar UUID. This callback shall not return any value.

  • OnLuaDialogClose(title, button, text) is called each time a Lua dialog floater (opened by any Lua script/command line with MakeDialog(), see above) is closed. "title" corresponds to the same parameter value as the one used in MakeDialog(), "button" is the number of the button that got pressed and caused the dialog to close (0 if the dialog was closed with the floater close ("X") button), and "text" is the text that was contained/entered in the input line. This callback shall not return any value.

  • OnSideBarVisibilityChange(visible) is called each time the Lua side bar visibility changes (e.g. whenever SideBarHide() is called, or when the side bar was configured with SideBarHideOnRightClick() and is right clicked) and "visible" is a boolean indicating whether the side bar is visible or not. Please note that this visibility does not relate to the automatic hiding of the side bar in mouse-look mode (the latter mode always hides the side bar but does not affect its internal visibility status). This callback shall not return any value.

  • OnAutomationMessage(message) is called each time another Lua command line uses the AutomationMessage(message) function, "message" (a string or string convertible type) being transmitted as a Lua string from the said command line to the OnAutomationMessage() callback in the automation script. This callback shall not return any value.


For RestrainedLove users, note that the associated restrictions still apply (i.e. all received or sent chat/IM text, or names, will be properly censored when needed, and all floaters will properly fail to open if the corresponding restriction is in force); that is, Lua scripting won't buy you a mean of cheating RestrainedLove ! :lol:

There is a specific thread for Lua scripting related feature requests. Please use it to post any such request.


2017-01-14 11:14:35
Profile WWW

Joined: 2009-03-17 18:42:51
Posts: 3502
Sample/demonstration automation script (user_settings/automation.lua):
Code:
known_ids = {}
known_sessions = {}
tp_started = false
tp_retry = false
max_agents = 20
max_complexity_shield_off = 0
max_complexity_shield_on = 200000
max_area_shield_off = 0
max_area_shield_on = 200
max_memory_shield_off = 0
max_memory_shield_on = 100
shield_on=false
derendered_objects = { ["253538b0-56fe-1feb-8089-2cade9c5a413"]=true ["6b752a3c-c0a8-8df7-0d0e-d95c0cda33a1"]=true }
account_settings = {}

-- Here, we define a custom set of side bar buttons for performing useful
-- tasks or opening commonly used floaters that do not have an associated
-- toolbar button.
-- We also perform tasks that can only happen after successful login.
function OnLogin(location)
    SideBarButton(1, "\u{2699}", "OpenFloater('preferences')", "Opens the Preferences floater")
    ToggleSpellCheckLanguage(false)
    SideBarButton(3, "inv_item_landmark_visited.tga", "OpenFloater('teleport history')", "Opens the Teleport history")
    SideBarButton(4, "inv_item_mesh.tga", "nop", "Toggles mesh queue info")
    SideBarButtonToggle(4, "DebugShowMeshQueue")
    SideBarButton(5, "43f0a590-f3d3-48b5-b460-f5b3e6e03626.tga", "OpenFloater('sounds list')", "Opens the Sounds list floater")
    OnAutomationMessage("shield-off")
    SideBarHideOnRightClick()
    SideBarHide(false)
    -- Retrieve our per-account settings and validate them (we use a table for
    -- our settings, since it is much simpler than dealing with serialization of
    -- settings to a data string ourselves).
    local settings = GetPerAccountData()
    if type(settings) == "table" then
        account_settings = settings
    end
    -- Restore last Windlight and water settings, if any
    if account_settings.last_sky then
        ApplySkySettings(account_settings.last_sky)
    end
    if account_settings.last_water then
        ApplyWaterSettings(account_settings.last_water)
    end
    -- Derender objects we never want to see
    for k, v in pairs(derendered_objects) do
        DerenderObject(k, v)
    end
 end

-- This callback is invoked each time the side bar visibility is changed. We
-- use it to display the Lua icon in the status bar whenever the side bar gets
-- hidden and we setup the command for that icon so that, when clicked, it
-- shows the side bar (which in turn hides the status bar icon via this
-- callback).
function OnSideBarVisibilityChange(visible)
    if visible then
        StatusBarLuaIcon("")
    else
        StatusBarLuaIcon("SideBarHide(false)", "Shows the Lua buttons side bar")
    end
end

-- This function toggles the spell checking language between English and
-- French: much easier than doing it via the Preferences menu... Of course,
-- you will have to make sure first that all the related dictionaries have
-- been installed on your system (from "Preferences" -> "Cool features" ->
-- "Chat, IM & text").
function ToggleSpellCheckLanguage(toggle)
    local lang = GetDebugSetting("SpellCheckLanguage")
    if toggle then
        if lang == "fr_fr" then
            lang = "en_us"
        else
            lang = "fr_fr"
        end
        SetDebugSetting("SpellCheckLanguage", lang)
    end
    if lang == "fr_fr" then
        SideBarButton(2, "En", "AutomationMessage('language')", "Toggles the spell checking language to English (US)")
    else
        SideBarButton(2, "Fr", "AutomationMessage('language')", "Toggles the spell checking language to French")
    end
end

-- This callback receives the messages sent by the various buttons we setup via
-- the AutomationMessage() function.
function OnAutomationMessage(text)
    if text == "shield-on" then
        shield_on=true
        SetDebugSetting("RenderAvatarMaxComplexity", max_complexity_shield_on)
        SetDebugSetting("RenderAutoMuteSurfaceAreaLimit", max_area_shield_on)
        SetDebugSetting("RenderAutoMuteMemoryLimit", max_memory_shield_on)
        SideBarButton(6, "shield_off.png", "AutomationMessage('shield-off')", "Disables anti-griefers features")
        OpenNotification(0, "Anti-griefer features enabled")
    elseif text == "shield-off" then
        shield_on=false
        SetDebugSetting("RenderAvatarMaxComplexity", max_complexity_shield_off)
        SetDebugSetting("RenderAutoMuteSurfaceAreaLimit", max_area_shield_off)
        SetDebugSetting("RenderAutoMuteMemoryLimit", max_memory_shield_off)
        SideBarButton(6, "shield_on.png", "AutomationMessage('shield-on')", "Enables anti-griefers features")
        OpenNotification(0, "Anti-griefer features disabled")
    elseif text == "cancel-auto-tp" then
        tp_retry = false
        OverlayBarLuaButton("", "")
    elseif text == "language" then
        ToggleSpellCheckLanguage(true)
    end
end

-- This is just a demonstration of how Lua scripting may be used as a mean of
-- defense against griefing attempts: here, when pushed and the anti-griefer
-- measures are on, the avatar is automatically sat down on ground to prevent
-- further effects of new pushes.
function OnAgentPush(id, t, magnitude)
    if shield_on and mag > 3 then
        AgentSit()
    end
    OpenNotification(0, "Push detected, magnitude = " .. mag)
end

-- Here we add pseudo commands to:
--  * emulate Firestorm's "/dd" (draw distance) command (with "/dd" alone as an
--    alias for "/dd 256");
--  * toggle the camera front view with "/fc";
--  * adjust the avatar Z offset with "/z " followed by a amount of centimeters
--    (e.g.: "/z -8" to lower your avatar height by 8 cm), with "/z" alone to reset
--    the offset to zero.
-- We also create a pseudo-gesture ("/g " for "greetings") accepting a parameter
-- (the name or title of the resident you want to greet).
function OnSendChat(text)
    if string.sub(text, 1 ,1) ~= "/" then
        -- Do not waste time searching for commands if the first character is
        -- not a slash...
        return text
    end
    if text == "/fc" then
        SetDebugSetting("CameraFrontView", not GetDebugSetting("CameraFrontView"))
        return ""
    end
    if text == "/dd" then
        SetDebugSetting("RenderFarClip", 256)
        return ""
    end
    local i, j = string.find(text, "/dd ")
    if i == 1 then
        local distance = tonumber(string.sub(text, j + 1))
        if distance > 512 then
            distance = 512
        elseif distance < 32 then
            distance = 32
        end
        SetDebugSetting("RenderFarClip", distance)
        return ""
    end
    if text == "/z" then
        SetDebugSetting("AvatarOffsetZ", 0)
        return ""
    end
    i, j = string.find(text, "/z ")
    if i == 1 then
        local offset = tonumber(string.sub(text, j + 1)) / 100
        if offset > 9 then
            offset = 9
        elseif offset < -9 then
            offset = 9
        end
        SetDebugSetting("AvatarOffsetZ", offset)
        return ""
    end
    i, j = string.find(text, "/g ")
    if i == 1 then
        text = "/me smiles softly, \"Greetings" .. string.sub(text, j) .. ".\""
    end
    return text
end

-- This is just a demonstration of SLURL dispatching and floater opening, all
-- wrapped up in an OnReceivedChat() callback: it got no real practical use
-- other than being a sample of what can be done (and may be dangerous to use
-- "as is", due to the SLURL systematic auto-dispatching).
function OnReceivedChat(t, id, is_avatar, name, text)
    local i, j = string.find(text, "secondlife://")
    if i then
        OpenNotification(0, name .. " sent an SURL, dispatching it")
        DispatchSLURL(string.sub(text, i))
        return
    end
    if known_ids[id] then
        return
    end
    known_ids[id] = true
    if is_avatar then
        OpenNotification(0, name .. " is a new chatting avatar: displaying profile.")
        OpenFloater("avatar info", id)
    else
        OpenNotification(0, name .. " is a new chatting object: inspecting it.")
        OpenFloater("inspect", id)
    end
end

-- This is just a demonstration of IM callbacks usage and of how to use the
-- CallbackAfter() function: it got no real practical use other than being a
-- sample of what can be done.
function InstantMsgReply(session_id, name, text)
    OpenNotification(0, name .. " opened a new IM session: replying now.")
    SendIM(session_id, text)
end

function OnInstantMsg(session_id, origin_id, t, name, text)
    if known_sessions[session_id] then
        return
    end
    known_sessions[session_id] = true
    -- Wait 3 seconds for the IM session to start (important for group sessions) before replying:
    CallbackAfter(3, InstantMsgReply, session_id, name, "Hello !")
end

-- The code below allows to auto-retry failed teleports (provided the TP global
-- coordinates were known when the TP was first attempted: this is the case for
-- all TPs done from the world map floater, but may not be the case for landmarks
-- TPs and TP invites, at least on the first TP attempt). It illustrates the use
-- of the Lua dialog and overlay bar button, of the AutomationMessage() function
-- and of the TP related callbacks and functions.
function OnTPStateChange(state, reason)
    if state == 0 then -- TELEPORT_NONE
        tp_retry = false
        if tp_started and string.len(reason) > 0 and
           reason ~= "invalid_tport" and
           reason ~= "nolandmark_tport" and
           reason ~= "noaccess_tport" and
           reason ~= "no_inventory_host" then
            if reason == "no_host" then
                -- Auto-retry after sim comes back online
                tp_retry = true
                OverlayBarLuaButton("Cancel auto-TP", "AutomationMessage('cancel-auto-tp')")
            else
                MakeDialog("Retry TP",
                           "To auto-retry the TP, give the max allowed number of agents for this sim, else press the Cancel button",
                           max_agents,
                           "Cancel", "", "OK",
                           "DialogClose()", "", "DialogClose()")
            end
        end
        tp_started = false
    elseif state == 1 then -- TELEPORT_START
        tp_started = true
        tp_retry = false
    elseif state ~= 2 then -- all other teleport states than TELEPORT_REQUESTED
        tp_started = false
        tp_retry = false
    end
end

function OnLuaDialogClose(title, button, text)
    if title == "Retry TP" and button == 3 then
        max_agent = tonumbe(text)
        if max_agent > 0 then
            tp_retry = true
            OverlayBarLuaButton("Cancel auto-TP", "AutomationMessage('cancel-auto-tp')")
        end
    end
end

function OnFailedTPSimChange(agents, x, y, z)
    if tp_retry and agents < max_agents then
        TeleportAgentToPos(x, y, z)
        OverlayBarLuaButton("", "")
    end
end

-- This is called each time the user loads new Windlight or water presets.
-- We then save the new presets into the per-account settings, so to restore
-- them on next login with the same avatar.
function OnWindlightChange(sky_settings_name, water_settings_name)
    if sky_settings_name ~= "" then
        account_settings.last_sky = sky_settings_name
    end
    if water_settings_name ~= "" then
        account_settings.last_water = water_settings_name
    end
    SetPerAccountData(account_settings)
end


2017-01-28 14:53:34
Profile WWW

Joined: 2009-03-17 18:42:51
Posts: 3502
Here is what the Lua status bar icon looks like:
Image

And here is an example of a side-bar corresponding to the sample script given in the previous post:
Image


2017-02-13 09:37:11
Profile WWW

Joined: 2009-03-17 18:42:51
Posts: 3502
Here is a simple way to emulate Firestorm's parcel Windlight support in the Cool VL Viewer, via Lua (just add/merge to your automation.lua script):
Code:
function OnParcelChange(parcel)
    local desc = parcel.description
    -- Search for and extract text enclosed between "/*" and "*/"
    local i, j = string.find(desc, "/*")
    if not i then
        return
    end
    desc = string.sub(desc, j + 1)
    i, j = string.find(desc, "*/")
    if not i then
        return
    end
    desc = string.sub(desc, 1, i - 1)
    -- Match 'Sky<anything but '"'>"<sky_settings_name>"'
    local sky = string.match(desc, 'Sky[^"]*"([^"]+)')
    -- Match 'Water<anything but '"'>"<water_settings_name>"'
    local water = string.match(desc, 'Water[^"]*"([^"]+)')
    -- Apply settings is found
    if sky and sky ~= "" then
        ApplySkySettings(sky)
    end
    if water and water ~= "" then
        ApplyWaterSettings(water)
    end
end

Notes:
  • I only scripted above the "basic" support, without the altitude zoning syntax (it could be added, however): the Windlight settings will therefore be applied whatever the altitude of your avatar.
  • Since the Cool VL Viewer does not include all the default Windlight settings Firestorm got, you will have to add the latter into the user_settings/windlight/ directory, so that the Cool VL Viewer can find them.


2017-03-28 09:18:38
Profile WWW
Display posts from previous:  Sort by  
This topic is locked, you cannot edit posts or make further replies.   [ 4 posts ] 

Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron
Powered by phpBB® Forum Software © phpBB Group
Designed by ST Software.