With all its power and complexity, the Avorion Scripting API and its environment comes with a few pitfalls that you simply have to be aware of to avoid them.
- 1 Script Path Caching
- 2 References instead of New Objects
- 3 New Objects instead of References
- 4 Entity Reference Invalidation
- 5 Client-Script Resets
- 6 UI Lazy Initialization
- 7 initialize() when loading from disk
- 8 Coordinates of UI Elements
- 9 include() instead of require()
- 10 Restricted Access to local File System
- 11 See Also
Script Path Caching[edit | edit source]
This behavior is also only important when you create new script files while Avorion is running.
In order to save performance and avoid disk I/O, the paths to scripts and script extensions are cached. So when the game once accessed a script and its modded extensions, it remembers those file paths and will reuse them whenever the same script is loaded again. Newly added extensions and scripts that were added while the game was already running might be skipped.
In a scenario where there are, say, 100 mods installed, this behavior significantly improves script-loading performance because the game doesn't have to search each mod folder for potential extensions of scripts whenever a script is loaded, but just once per script.
You can disable this behavior by checking the
Dev Mode check box in the Mods window. This will impact performance somewhat when loading scripts, but it will make sure that the game always checks all possible files that are available on disk, without reusing previous file paths.
Note: This only affects file paths and extensions, not the content of the files.
References instead of New Objects[edit | edit source]
Some instantiations of objects create only a reference to the actual object. This is true for a lot of objects, some of the most common ones are:
Entity([id])creates a reference to an existing, not a new Entity. Use
Sector:createEntity(...)to create new entities.
CargoBay()(and other component classes) create a reference, not a new component. New components are created when entities are created.
Label([index])(and other UI classes) create a reference to an existing, not a new UI element. Use the
UIContainer:createLabel(...)etc. functions to create new UI elements.
Alliance([index])create a reference to an existing Faction, not a new one. Use the
Galaxy():createAIFaction()functions to create new AI factions. You cannot create new Players or Alliances.
New Objects instead of References[edit | edit source]
Sometimes it's also the other way around: What looks like a reference to an object is actually a copy of an object. Due to internal architecture and how lua does interfacing of classes, some properties that look like they return a reference to a value, actually return a copy.
local mat = Matrix() mat.position.x = 5 -- mat.position returns a copy of the 'position' vec3 variable, so we're modifying a temporary copy here mat.position.y = 0 mat.position.z = 2 print (mat.position.x); print (mat.position.y); print (mat.position.z)
prints the following: 0 0 0
Instead, you should do the following when you want to assign the position of the matrix:
local mat = Matrix() mat.position = vec3(5, 0, 2) -- this assigns a vec3 to the mat.position variable print (mat.position.x); print (mat.position.y); print (mat.position.z)
prints the following: 5 0 2
How to avoid[edit | edit source]
This error is easy to avoid when you're aware of the situations where it can appear. For example, when you see an assignment where there are two dots on the left side like this:
mat.position.x = 5 -- or a.b.x = 5 -- or a:b().x = 5
and you're using API classes then it's almost 100% sure to be the above case.
Entity Reference Invalidation[edit | edit source]
When a script changes sectors, for example because it's attached to an Entity or a Player that changes sectors, all of its
Entity objects are invalidated.
Entity is only a reference to an entity in a sector, and since scripts can't access things that are outside their current sector, existing
Entity objects are invalidated.
local entity function initialize() entity = Entity() end function update() -- this will fail after a sector change because 'entity' was invalidated print(entity.name) -- this will always work, since the Entity will always be newly created and thus always be valid local e = Entity() print(e.name) end
If you want to create an
Entity and need regular access to it, regardless of sector changes, you should save the
Entity's id instead, and use
Entity(id) to access it.
Client-Script Resets[edit | edit source]
Due to technical reasons, client-sided Sector and Entity scripts are deleted and recreated every time a sector change is done. This includes the player's ship!
UI Lazy Initialization[edit | edit source]
In order to save memory, Avorion uses lazy initialization for scripted UI. So your
initUI function will only be called when the player actually interacts with the entity.
initialize() when loading from disk[edit | edit source]
In scriptable object scripts, when they're being restored, their
initialize() function will also be called when loading the object from disk - but without parameters. When the
initialize() function is called during a restoration, a global variable
_restoring will be set to
restore() is then called with the data you previously returned with
Coordinates of UI Elements[edit | edit source]
When creating UI elements, you have to give them a location where they'll be placed. The
UIContainer:createLabel(...) and other UI creation functions expect local 2D coordinates, inside the window. But when you access coordinates of UI elements, they will return global 2D screen coordinates.
local rect = Rect(0, 0, 200, 40) local button = window:createButton(rect, "Press Me", "onButtonPressed") -- this will print the global render position of the button's rect. Also changes when the window is moved around local glbl = button.rect print (tostring(glbl.lower)) print (tostring(glbl.upper)) local lcl = button.localRect print (tostring(lcl.lower)) print (tostring(lcl.upper))
If you want to use coordinates of UI elements, for example for formatting, you should keep that in mind. Use
UIElement.localRect to access the local coordinates inside the window.
include() instead of require()[edit | edit source]
Avorion defines its own version of
include() behaves the exact same way as
require(), with one important difference: It can load mod file extensions.
require() can only load a single file, and that can be problematic. It can be necessary for mods to modify files such as data/scripts/lib/utility.lua, which are being
required by other lua files. But
require() doesn't know about Avorion's mod structure, and so it cannot load additional files provided by mods, that are supposed to change how data/scripts/lib/utility.lua works.
include() looks for those files in addition to the vanilla file, and includes them into the loaded file.
require() is still around and wasn't altered at all.
Restricted Access to local File System[edit | edit source]
In Avorion, client mods only have restricted access to the file system, for security reasons. So with
io functions, you cannot modify files outside of %AppData%/Avorion (~/.avorion/ on Unix).
-- Not working: local file, err = io.open("/home/user/not_working_example.txt", "w") -- Working: local file, err = io.open("./moddata/working_example.txt", "w") if file==nil then print("Couldn't open file: "..err) else file:write("example text") file:close() end