Modding Pitfalls

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.

## Script Path Caching

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

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.
* `Faction([index])`, `AIFaction([index])`, `Player([index])` or `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

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

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

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

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

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

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 `true`.

After `initialize()`, `restore()` is then called with the data you previously returned with `secure()`.

## Coordinates of UI Elements

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()

Avorion defines its own version of `require()`: `include()`. `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 `require`d 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.

You can read more about mod loading with include, and why you should use it, here.

**Note:** `require()` is still around and wasn't altered at all.

## Restricted Access to local File System

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

```

## See Also

* Debugging Scripts
* Writing your own Mod
* Scripting API
* Avorion Internal Architecture
* Performance Optimization