Lune Documentation

repository·main·Indexed 21 days ago

https://github.com/lune-org/lune

Lune is a standalone Luau runtime built in Rust, providing a high-performance environment for running Luau scripts. It features built-in asynchronous APIs for networking, filesystem, and stdio, and includes optional libraries for manipulating Roblox place and model files. The runtime includes a task scheduler designed to be familiar to Roblox developers and supports various Roblox document formats (Binary and XML) and kinds (Place and Model).

Tokens
40.9K
Snippets
186
Records
213
Agent score
75%

What's inside Lune

  1. Overview of Lune

    main

    Lune is a standalone Luau runtime designed for writing and running programs, similar to Node, Deno, or Bun. It is built in Rust for speed and safety and provides a fully asynchronous API surface.

    Key features include:

    • A minimal but powerful interface.
    • Built-in APIs for filesystem, networking, and stdio included in a small (~5mb zipped) executable.
    • A 1-to-1 task scheduler port, making it a familiar environment for Roblox developers.
    • Optional built-in libraries for manipulating Roblox place and model files and their instances.
  2. Use mlua-luau-scheduler for async Luau execution

    main

    The mlua-luau-scheduler crate provides an async scheduler for Luau, built on top of async-executor. It is runtime-agnostic and compatible with any async runtime such as Tokio, smol, or async-std. Because it shares many dependencies with smol, using smol as your runtime is often preferred.

    To use the scheduler, you follow three main steps:

    1. Import dependencies: Include mlua, mlua-luau-scheduler, and your chosen async runtime (e.g., async-io).
    2. Set up the Lua environment: Create a Lua instance and define async functions that can be called from Luau. You can use lua.spawn within these functions to offload tasks to the background without blocking the Luau thread.
    3. Run the scheduler: Initialize a Scheduler with your Lua instance, push Luau threads (created via lua.load) onto the scheduler using push_thread_front, and run the scheduler using your runtime's block-on mechanism.
    use std::time::{Duration, Instant};
    use async_io::block_on;
    use mlua::prelude::*;
    use mlua_luau_scheduler::*;
    
    // 1. Setup
    let lua = Lua::new();
    
    // 2. Define async functions in Luau
    lua.globals().set("sleep", lua.create_async_function(|_, duration: f64| async move {
        // async logic here
        Ok(())
    })?)?;
    
    // 3. Schedule and run
    let sched = Scheduler::new(&lua)?;
    sched.push_thread_front(lua.load("sleep(0.1)"), ());
    block_on(sched.run());
  3. Manage Roblox DOMs using the Dom Registry

    main

    The lune-roblox package uses a global registry to manage multiple Document Object Models (DOMs). This prevents collisions between instances from different files and ensures memory is freed when a DOM is no longer in use.

    Every Instance is identified by a unique pair: (dom_id, dom_ref).

    Key Concepts

    • DomId: A unique u64 identifier for a specific DOM.
    • Default DOM: A shared scratch DOM used for manually created orphan instances (e.g., via Instance.new(...)) before they are parented into a specific document. Use default_dom() to access it.
    • Re-entrancy Warning: When using with or with_mut, the registry lock is held. Do not call other registry functions (like with or with_mut) inside the closure. Instead, collect the necessary referents and build your Instances using the provided &WeakDom reference.
  4. Gracefully stop the server using `ServeHandle`

    main
    When you call serve, it returns a ServeHandle. To stop the server gracefully, you should use this handle. The server implementation monitors the handle; if the handle is dropped or if its shutdown mechanism is triggered, the server will stop accepting new connections and attempt to shut down existing connections gracefully.
  5. Understand Luau module path resolution

    main

    Luau module resolution in Lune follows a specific hierarchy to map an abstract module path (like path/to/module) to a concrete filesystem path. When resolving a module path, the system searches in the following order:

    1. path/to/module.luau
    2. path/to/module.lua
    3. path/to/module/init.luau
    4. path/to/module/init.lua

    If the provided path is already a directory that exists, it is returned as a directory without modifications.

    Resolution Errors:

    • Ambiguous: Occurs if the path could resolve to multiple valid files (e.g., both module.lua and module.luau exist) or if the module name is exactly init.
    • NotFound: Occurs if no valid file or directory matches the search criteria.
  6. Find classes and enums case-insensitively

    main

    If you are unsure of the exact casing or whitespace of a class or enum name, use the find_class or find_enum methods. These methods perform a case-insensitive match and ignore leading or trailing whitespace.

    In Luau, these are available as FindClass(name) and FindEnum(name).

    // Using find_class for a fuzzy match
    let class = db.find_class("  BasePart  ");
  7. Understand FsMetadata structure

    main

    When working with filesystem metadata in Lune, the FsMetadata object is represented as a read-only Lua table. It provides information about a file's type, existence, timestamps, and permissions.

    Available fields in the FsMetadata table:

    • kind: A string representing the type of filesystem object ("file", "dir", "symlink", or "none").
    • exists: A boolean indicating if the filesystem object exists.
    • createdAt: A DateTime object representing when the file was created (optional).
    • modifiedAt: A DateTime object representing the last modification time (optional).
    • accessedAt: A DateTime object representing the last access time (optional).
    • permissions: An FsPermissions table (optional).
  8. Manage process environment variables with `ProcessEnv`

    main

    The ProcessEnv type allows you to manage a collection of environment variable pairs. It is designed to be easily shared and stored in Lua app data, ensuring that all keys and values are valid OS strings.

    In Lua, ProcessEnv behaves like a table where you can access, set, and remove environment variables. It also supports iteration over the variables.

    Initialization

    • Empty environment: Create a new, empty environment.
    • Current environment: Capture the existing process environment variables.
    • From a table: Create an environment from a Lua table of key-value pairs.

    Lua Usage Patterns

    • Accessing values: Use index syntax: `env[
    -- Example of using ProcessEnv in Lua
    
    -- 1. Get the current environment
    local env = true -- In Lune, passing `true` to the ProcessEnv constructor returns the current env
    
    -- 2. Read a value
    local path = env["PATH"]
    
    -- 3. Set a value
    env["MY_VAR"] = "hello"
    
    -- 4. Remove a value
    env["MY_VAR"] = nil
    
    -- 5. Iterate over all variables
    for key, value in env do
        print(key, value)
    end
    
    -- 6. Get the number of variables
    local count = #env
    
    -- 7. Create a custom environment from a table
    local custom_env = {
        VAR_A = "val_a",
        VAR_B = "val_b"
    }
    -- Note: The exact way to convert a table to ProcessEnv depends on the Lune API exposure,
    -- but the underlying type supports conversion from Lua tables.
  9. Interpret `RuntimeReturnValues`

    main

    When a runtime finishes execution, it returns a RuntimeReturnValues struct containing the results of the execution.

    • code: An Option<u8> representing the exit code manually returned from the runtime.
    • errored: A boolean indicating if any threads (main or spawned) encountered errors.
    • values: The final values returned by the main thread.

    Determining Success: Use the .status() and .success() methods to check the outcome:

    • .status(): Returns the explicit exit code if set; otherwise, returns 0 if no threads errored, or 1 if any thread errored.
    • .success(): Returns true if .status() is 0.
    let results = runtime.run_file("script.luau").await?;
    
    if results.success() {
        println!("Success! Values: {:?}", results.values);
    } else {
        println!("Failed with status: {}", results.status());
    }
  10. Represent DateTime in Lua using tables

    main

    In Lune (Luau), a DateTime value is represented as a read-only table. When converting from a Lua table to a DateTimeValues object, the table must contain the following keys. The millisecond key is optional and defaults to 0 if omitted.

    Required keys:

    • year (integer)
    • month (integer)
    • day (integer)
    • hour (integer)
    • minute (integer)
    • second (integer)

    Optional key:

    • millisecond (integer)

    Note: The resulting Lua table is read-only to ensure the integrity of the fixed point in time.

    -- Example of a valid DateTime table structure
    local dt = {
        year = 2023,
        month = 10,
        day = 27,
        hour = 14,
        minute = 30,
        second = 0,
        millisecond = 500
    }