Difference between revisions of "SLua Alpha"
Line 54: | Line 54: | ||
*** For example, <code>local OBJECT_NAME = 1; ll.GetObjectDetails(ll.GetKey(), {OBJECT_NAME})</code> will return an empty list because <code>OBJECT_NAME</code> is a Luau <code>number</code> type, instead of the LSL <code>integer</code> type expected by [[llGetObjectDetails]]. For cases like this, there are special functions which provide the correct data types. | *** For example, <code>local OBJECT_NAME = 1; ll.GetObjectDetails(ll.GetKey(), {OBJECT_NAME})</code> will return an empty list because <code>OBJECT_NAME</code> is a Luau <code>number</code> type, instead of the LSL <code>integer</code> type expected by [[llGetObjectDetails]]. For cases like this, there are special functions which provide the correct data types. | ||
*** Correct: <code>ll.GetObjectDetails(ll.GetKey(), {integer(OBJECT_NAME)})</code> | *** Correct: <code>ll.GetObjectDetails(ll.GetKey(), {integer(OBJECT_NAME)})</code> | ||
*** Similar functions exist for: '''integer''', '''uuid''' (key), '''vector''', '''quaternion''' | *** Similar functions exist for: '''integer''', '''uuid''' (key), '''vector''', '''quaternion''' (rotation) | ||
=== Luau Libraries === | === Luau Libraries === |
Revision as of 09:06, 5 March 2025
This functionality is in alpha. Instability is to be expected, and there may be very sharp edges. At this point it is expected that Luau can crash regions and perform other types of undesirable behavior.
Second Life Luau Alpha
We're thrilled to announce the launch of the Luau Alpha for Second Life! This significant update introduces the Luau scripting language, offering creators enhanced performance, improved memory efficiency, and a more versatile scripting environment.
What is Luau?
Luau is a fast, small, safe, and gradually typed embeddable scripting language derived from Lua. It is designed to be backwards compatible with Lua 5.1, incorporating features from future Lua releases and expanding the feature set with type annotations and a state-of-the-art type inference system. Luau is largely implemented from scratch, with the language runtime being a heavily modified version of the Lua 5.1 runtime, featuring a completely rewritten interpreter and other performance innovations.
Why Luau?
The decision to integrate Luau into Second Life was driven by its ability to meet all the requirements for a scripting engine within the platform. Luau offers a high-quality scripting experience to creators, addressing many of the limitations present in the current LSL (Linden Scripting Language) environment. Its lightweight nature and performance optimizations make it an ideal choice for enhancing the scripting capabilities in Second Life. For more information on why Luau was chosen, please see the Lua FAQ.
How to Get Started with Luau in Second Life
In order to play with Luau, you'll need to download our Lua project viewer, and log onto our Aditi beta grid.
- Access the latest build of the Luau-enabled Second Life Viewer from here.
Once you've got the new viewer and have logged onto the beta grid, head over to these Luau-enabled regions:
When editing a script in the new Lua project viewer, you'll notice a new Compiler drop-down near the save button. This drop-down will allow you to select which compiler will be used, as well as which script runtime will be used (LSO2, Mono, Luau).
Compiler drop-down options:
- LSL: Legacy (LSO2) - Scripts written in LSL, to be run on the old LSO2 VM
- LSL: Mono- Scripts written in LSL, to be run on the Mono VM
- Lua - Scripts written in Lua, to be run on the Luau VM
- LSL/Luau- Scripts written in LSL, to be run on the Luau VM
Transitioning from LSL to Luau
- Function Namespacing:
- In Luau, Linden Lab functions have been moved under the ll namespace.
- For example:
- llSay becomes ll.Say
- llGetPos becomes ll.GetPos
- Constants:
- Constants like PERMISSION_TAKE_CONTROLS and PRIM_ROT_LOCAL are not predefined in Luau.
- You must specify these constants if you wish to reference them in your scripts.
- Lists
- Luau indexes begin from 1, unlike LSL where indexes begin from 0.
- Luau uses
{}
for tables, unlike LSL where[]
is used for lists. - When calling LL functions in Luau, lists often have type-strict requirements, unlike Luau in general.
- For example,
local OBJECT_NAME = 1; ll.GetObjectDetails(ll.GetKey(), {OBJECT_NAME})
will return an empty list becauseOBJECT_NAME
is a Luaunumber
type, instead of the LSLinteger
type expected by llGetObjectDetails. For cases like this, there are special functions which provide the correct data types. - Correct:
ll.GetObjectDetails(ll.GetKey(), {integer(OBJECT_NAME)})
- Similar functions exist for: integer, uuid (key), vector, quaternion (rotation)
- For example,
Luau Libraries
- Coroutines:
- Luau supports coroutines, allowing for cooperative multitasking within scripts.
- Key functions include:
- coroutine.create
- coroutine.status
- coroutine.resume
- Refer to the coroutine library documentation for more details.
- Bitwise Operations:
- Luau includes a bit32 library for bitwise operations, enabling more efficient data manipulation.
- Refer to the bit32 library documentation for more details.
- Standard Library:
- Luau comes equipped with a standard library of functions designed to manipulate built-in data types.
- Explore the Luau Standard Library for a comprehensive list of available functions.
Feedback and Support
We encourage all creators to explore the new Luau scripting capabilities and provide feedback. Your insights are invaluable in refining and enhancing this feature. For more information and to share your experiences, please refer to our Lua FAQ.
Example Scripts
To help you get started, we've assembled some example scripts that demonstrate the capabilities of Luau in Second Life. These scripts cover various functionalities and can serve as a foundation for your own creations. Please feel free to propose changes to these scripts, or modify them to your heart's desire!
default_script.lua
This script is roughly equivalent to the "new script" that gets created.
function state_entry()
ll.Say(0, "Hello, Avatar!")
end
-- Called when the object is touched.
function touch_start(total_number)
ll.Say(0, "Touched.")
end
-- Invoke state_entry on startup, since simulator doesn't invoke
-- it like it does in LSL
state_entry()
dialog.lua
This script demonstrates how one can interact with dialog menus.
-- Define the menu buttons and dialog message.
local buttons = {"-", "Red", "Green", "Yellow"}
local dialogInfo = "\nPlease make a choice."
local ToucherID = nil
local dialogChannel = nil
local listenHandle = nil
-- This function is called when the script first starts.
function state_entry()
-- Get the object's key and compute a dialog channel number.
local key = ll.GetKey()
-- Extract the last 7 characters of the key and convert it from hex.
dialogChannel = -1 - tonumber(string.sub(tostring(key), -7, -1), 16)
end
-- Called when the object is touched.
function touch_start(num_detected)
ToucherID = ll.DetectedKey(0)
-- If there is already a listen handle, then remove it
if listenHandle then
ll.ListenRemove(listenHandle)
end
listenHandle = ll.Listen(dialogChannel, "", ToucherID, "")
ll.Dialog(ToucherID, dialogInfo, buttons, dialogChannel)
-- Set a 60-second timer for response.
ll.SetTimerEvent(60.0)
end
-- Called when a dialog response is received.
function listen(channel, name, sender_id, message)
if message == "-" then
-- Redisplay the dialog if the "-" option is selected.
ll.Dialog(ToucherID, dialogInfo, buttons, dialogChannel)
return
end
-- Stop the timer, and stop the listening handler.
ll.ListenRemove(listenHandle)
ll.SetTimerEvent(0)
-- Let the user know what they selected
ll.Say(0, `You selected {message}`)
end
-- Called when the timer expires.
function timer()
-- Stop the timer and clean up the listener.
if listenHandle then
ll.SetTimerEvent(0)
ll.ListenRemove(listenHandle)
ll.Whisper(0, "Sorry. You snooze; you lose.")
end
end
-- Invoke state_entry on startup, since simulator doesn't invoke
-- it like it does in LSL
state_entry()
dialog_coroutine.lua
This script demonstrates how one could use coroutines to handle dialog responses, with multi-user support.
-------------------------
-- Minimal EventLoop
-------------------------
local EventLoop = {
-- Coroutine -> eventName it’s waiting for
_coros = {},
running = false
}
function EventLoop:create_task(func)
local coro = coroutine.create(func)
self._coros[coro] = false
self:_run_coro(coro)
return coro
end
function EventLoop:kill_task(coro)
self._coros[coro] = nil
if coroutine.status(coro) ~= "dead" then
coroutine.close(coro)
end
end
-- Internal helper: resumes a coroutine
function EventLoop:_run_coro(coro, ...)
if coroutine.status(coro) == "dead" then
return
end
local old_running = self.running
self.running = true
self._coros[coro] = false
local ok, eventAwaited = coroutine.resume(coro, ...)
self.running = old_running
if not ok then
ll.OwnerSay(`Coroutine error: {eventAwaited}`)
self._coros[coro] = nil
return
end
-- If still alive, 'eventAwaited' is the next event it wants
self._coros[coro] = eventAwaited
end
function EventLoop:handle_event(eventName, ...)
ll.OwnerSay(`Handling event {eventName}`)
local snapshot = table.clone(self._coros)
for coro, waitingFor in pairs(snapshot) do
if coroutine.status(coro) == "dead" then
self._coros[coro] = nil
elseif waitingFor == eventName then
ll.OwnerSay(`Dispatching event {eventName} to {coro}`)
self:_run_coro(coro, ...)
end
end
end
-- Coroutines use this to yield until an event
local function await_event(name)
assert(EventLoop.running, "await_event called outside a coroutine!")
return coroutine.yield(name)
end
-------------------------
-- Script Logic
-------------------------
local buttons = {"-", "Red", "Green", "Yellow"}
local dialogInfo = "\nPlease make a choice."
-- Use the chat listener to feed the event-loop
function listen(channel, name, sender_id, message)
-- We handle all 'listen' events via the event-loop
EventLoop:handle_event(`listen_{channel}`, channel, name, sender_id, message)
end
-- Called when the script starts
function state_entry()
-- Seed math.random so each new script run doesn’t repeat the same channels
math.randomseed(ll.GetUnixTime())
ll.OwnerSay("Script started with random channels for each user.")
end
-- A coroutine function for a single user's dialog flow
local function handle_dialog_for_user(userId)
-- Use a random channel
local channel = math.random(0x1, 0xFFFF)
-- Create a listener for that channel
local listenHandle = ll.Listen(channel, "", "", "")
while true do
-- Show the user a dialog
ll.Dialog(userId, dialogInfo, buttons, channel)
-- Wait for the next 'listen' event (channel, name, sender_id, message)
local c, n, sid, msg = await_event(`listen_{channel}`)
-- If this "listen" event isn't for our channel/user, ignore it
if c == channel and sid == userId then
-- If user pressed "-", re-display the menu, else they picked a final color
if msg ~= "-" then
ll.Say(0, `{n} selected {msg}`)
-- Now that they've chosen something else, remove the listener and finish
ll.ListenRemove(listenHandle)
return
end
end
end
end
-- Called when the object is touched
function touch_start(num_detected)
for i=0, num_detected-1 do
local toucherId = ll.DetectedKey(i)
-- Create a separate coroutine for each person who touches
EventLoop:create_task(function()
handle_dialog_for_user(toucherId)
end)
end
end
-- Run state_entry on load:
state_entry()
More Examples
- Find more example scripts at Luau Example Scripts