Lua – Apps - CheesyManiac/acc-extension-config GitHub Wiki

Since CSP 0.1.76, it is possible to create apps using Lua v5.1

In order to create UI CSP uses the Dear ImGui library. This has a different approach to building UI elements; instead of creating buttons and text fields and such, arranging them in a certain way and keeping track of their state, it works in immediate mode. Write ui.button('Label') and it would create a button in the current cursor position and move that cursor. If button is clicked, function would return true. You can read more about its paradigm here.

CSP Lua apps run within the Graphics Thread. This means they update with each drawn frame of the game, and NOT with the physics engine. For this reason, apps will desync from physics data by a few physics steps and so it is important that apps are NOT used to control physics related functions (outside of debugging, of course).

App Manifest Format

Manifest can define multiple windows, each of windows gets its own icon on AC apps taskbar. By default first “WINDOW_…” section acts as main window, although you can explicitly set main window with a window flag (more on that later).

When a window is opened and visible, the function mentioned in “FUNCTION_MAIN” will be called each frame to draw its contents.

App ABOUT Section

This shows information about your app in Content Manager CM > Content > Miscellaneous > Lua Apps

Since CSP 0.2.3, you can now specify a minimum CSP version that the app requires. In the future, if you CSP version is lower than the Required value the app will not be loaded, avoiding potential errors and crashing.

[ABOUT]
NAME = AppNameHere            ; Optional, full name of the app
AUTHOR = AuthorNamesHere      ; Optional, list of authors
VERSION = 1.0                 ; Recommended, version of your app. Format is not important, but should increase with developement iterations
DESCRIPTION = Text            ; Optional, a small description to show in CM
URL = LinkToAppSource         ; Optional, URL for the app. Can be whatever, but Recommended to be the download source
Required_VERSION = BuildID    ; Optional, Recommended, minimum version of CSP Required for the app to be loaded

Standard App Window

Syntax

All values listed are default values

[WINDOW_...]
ID = WINDOW_ID                ; Required, defaults to section name
NAME = Window Name            ; Required, shown in the title bar of a window and in taskbar
TAGS: …                       ; Optional, list of key words separated by a comma that can be used to search for the window
SIZE = X, Y                   ; Optional, default window size in pixels (can be scaled based on global UI scaling parameter)
MIN_SIZE = 40, 20             ; Optional, minimum window size
MAX_SIZE = ∞, ∞               ; Optional, maximum window size
DEFAULT_POSITION = X, Y       ; Optional, default position of the window when first opened in pixels
PADDING = X, Y                ; Optional, if set, overwrites default window padding
BACKGROUND_COLOR = R,G,B,M    ; Optional, used to set the background color of the window. "SETUP" flag will override to (0.1,0.1,0.1,0.6)
GROUP = …                     ; Optional, places window into a group within the taskbar. Available options: SETTINGS, DEVELOPER, TOOL
                              ;           TOOL places the app window into the CSP Object Inspector app

FLAGS = …                     ; Optional, window flags separated by comma:
      ; • MAIN: makes window act like main window (if not set, first window gets that role)
      ; • SETTINGS: adds settings button next to collapse and close buttons in title bar, opening settings window
      ; • AUTO_RESIZE: automatically resizes window to fit its content
      ; • FADING: makes window fade when inactive, similar to chat app
      ; • DARK_HEADER: black font and symbols in titlebar
      ; • FIXED_SIZE: prevents window from being resized
      ; • FLOATING_TITLE_BAR: text and symbols floating/stacked
      ; • NO_BACKGROUND: makes background transparent
      ; • NO_COLLAPSE: hides collapse button
      ; • NO_SCROLL_WITH_MOUSE: stops mouse wheel from scrolling
      ; • NO_SCROLLBAR: hides scrollbar 
      ; • NO_TITLE_BAR: hides title bar
      ; • HIDDEN: hides app window everywhere, can be opened with function.
      ; • HIDDEN_OFFLINE: hidden when in a singleplayer (offline) session (useful if the app use is not intended for offline)
      ; • HIDDEN_ONLINE: hidden when connected to an online server (useful if the app uses functions not available online)
      ; • HIDDEN_RENDER_SINGLE: hidden in single screen mode
      ; • HIDDEN_RENDER_TRIPLE: hidden in triple screen
      ; • HIDDEN_RENDER_VR: hidden in VR
      ; • HIDDEN_RENDER_CUSTOM: hidden in custom screen setup
      ; • SETUP: show in setup screen, open by default, not shown in the normal game screen
      ; • SETUP_HIDDEN: show on setup menu, hidden by default, not shown in the normal game screen
      ; • HANDLE_CTRL_TAB: prevents usage of the app switcher while the app window is currently in focus

FUNCTION_MAIN = fn            ; Required, function to be called each frame to draw window content
FUNCTION_SETTINGS = fn        ; Optional, function to be called to draw content for settings window (only with “SETTINGS” flag)
FUNCTION_MENU = fn            ; Optional, function to be called when in the app context window (right click the title bar)
FUNCTION_ON_SHOW = fn         ; Optional, function to be called once when window opens
FUNCTION_ON_HIDE = fn         ; Optional, function to be called once when window closes
ICON = icon.png               ; Recommended, filename of window icon as png (icon must be located in app folder)
FUNCTION_ICON = fn            ; Optional, function to be called instead to draw a window icon, for dynamic icons

Tool Window

Apps can now add Tools to the CSP Object Inspector app.

[TOOL_...]
TOOL_NAME = ToolNameHere      ; 
TOOL_DESCRIPTION = Text       ; 

Additional App Functions

CORE manifest values

[CORE]
LAZY = …                      ; Optional, list of flags separated by a comma:
      ; • NONE: (default value) Load script when Assetto Corsa is loading, run it until it is closed
      ; • PARTIAL: load script only when app is first opened, after that keep it running until Assetto Corsa is closed.
      ; • FULL: load script when app is opened, when all windows are closed, unload an app completely.

;      Note: when app unloads, all of its internal state (apart from stored with things like `ac.connect()`, `ac.storage()` or `ac.store()`) is completely lost. That’s why sometimes it might make more sense to use partial laziness and unload app manually on closing (for example, if your app has manual saving and a user closed or hid window without saving).


FLAGS = …                     ; Optional, list of flags separated by comma:
      ; • DISABLED                ; Disable app altogether
      ; • DISABLED_OFFLINE        ; Disable app when in a singleplayer (offline) session
      ; • DISABLED_ONLINE         ; Disable app when in a multiplayer (offline) session
      ; • DISABLED_RENDER_SINGLE  ; Disable app when using single screen
      ; • DISABLED_RENDER_TRIPLE  ; Disable app when using triple screens
      ; • DISABLED_RENDER_VR      ; Disable app when using VR
      ; • DISABLED_RENDER_CUSTOM  ; Disable app when using a custom render method


CACHE = 1                     ; Cache the app to bytecode on the first run. This will make loading slightly faster in the future
PREPROCESS = 1                ; Apply some preprocessing to the script, should improve performance in certain areas.
IMMEDIATE = 0                 ; 

script.update(dt)

Called each graphics frame after world matrix traversal ends for each app, even if none of its windows are active. Please make sure to not do anything too computationally expensive there (unless app needs it for some reason).

Sim callbacks

Optional callbacks triggered at different points of AC loop, in case app would need to get actual data or do some work at a very specific point. Can be set in manifest like so:

Since CSP 0.2.4 build 3116 it is now Recommended to use the new render.on(eventName, callback) function instead. More options are available.

[SIM_CALLBACKS]
FRAME_BEGIN = fn              ; If set, script.fn() will be called right before scene has started rendering (can be used to move camera around)
UPDATE = fn                   ; If set, script.fn() will be called after a whole simulation update
WORLD_UPDATE = fn             ; If set, script.fn() will be called 

Render callbacks

Optional render callbacks are triggered in certain points of main scene rendering process. Could be used to draw some extra shapes in world space: for example, if you are working on an app for positioning additional audio events in the world, those callbacks can be used to render some outlines for those audio events, as well as a moving helper.

[RENDER_CALLBACKS]
OPAQUE = fn                   ; Called when opaque geometry (objects without transparent flag) has finished rendering
TRANSPARENT = fn              ; Called when transparent objects are finished rendering

At least at the moment it’s not meant to draw additional geometry, debug only shapes. To load and render additional models, use ac.SceneReference functions to find parent node, load extra KN5 in there and manipulate it.

UI callback

Optional UI callback is meant for creating fullscreen UIs coming together in a predesigned layout to achieve a certain visual style (for example, to recreate HUD of another racing game). Such HUDs can replace certain original elements: virtual mirrors, damage display and low fuel indicator (more coming a bit later).

To stop original elements from working, use ac.redirect… functions. Function ui.drawVirtualMirror() can be used to draw virtual mirror.

[UI_CALLBACKS]
IN_GAME = fn                  ; Called before rendering IMGUI apps to draw things on screen

Note: unlike window rendering functions, this one does not run from a window, so to draw things, you first would need to create a window using function like ui.transparentWindow() or ui.toolWindow().

App Services

Optional feature that creates a background service that is run by CSP periodically. Used by the SetupExchange app as an example.

[SERVICE_...]
SCRIPT = service.lua          ; File containing a script that is run each PERIOD, but only when the game is running.
PERIOD = 10                   ; Number of seconds, or with the postfix of d/h/m for days/hours/minutes
                              ; Example: PERIOD = 1d 12h 30m

Delayed Start

[DELAYED_INITIALIZATION...]
SCRIPT = service.lua          ; Similar to service, but run each time the game is started but after TIMEOUT
TIMEOUT = 3                   ; Period of time in seconds

Awake Triggers

[AWAKE_TRIGGERS]
EVENT = 'value'               ; Use ac.onSharedEvent() to use this. See the internal AppShelf app as an example
URL = '^https?://'            ; 
BUTTON = 'value'              ; Use ac.ControlButton() to use this. See the internal CspDebug app as an example.

Getting Started

To create an app, make a new folder “MyFirstApp” (or anything else) in “assettocorsa/apps/lua” and add “manifest.ini” in there:

[ABOUT]
NAME = Hello World App
AUTHOR = You
VERSION = 1.0
DESCRIPTION = My first Lua app for Assetto Corsa

[CORE]
LAZY = FULL

[WINDOW_...]
ID = main
NAME = Hello World
ICON = icon.png
FUNCTION_MAIN = windowMain
FUNCTION_SETTINGS = windowSettings
SIZE = 400, 200
FLAGS = SETTINGS

After that, create “MyFirstApp.lua” in the same folder and add:

It is important the folder name and the .lua file name are identical (it is case sensitive!)

--This will be run only when the main window is open and on screen.
function script.windowMain(dt)
  ui.text('Hello world!')
end

--Available because of the FUNCTION_SETTINGS that was added to the manifest above
--This will be run when the settings icon is clicked, and the new window is opened.
function script.windowSettings(dt)
  -- Draw the settings window UI
end

--Optional, standard available function
--This is run every graphics frame!
function script.update(dt)
  --UI functions not available in this
end

Example HelloWorld App

For an example, here is a Lua HelloWorld app (it includes some extra media to test video-rendering functions).

Features:

  • Example of a simple fullscreen UI;
  • Example of a custom camera motion;
  • Example of Real Mirrors editor;
  • Example of OS integration: running new processes, use of file dialogs;
  • Console integration: adds new command eval, prints out things to console.

Other Resources

https://github.com/CheesyManiac/cheesy-lua/wiki/Getting-Started-with-CSP-Lua-Scripting