|
| 1 | +--- |
| 2 | +title: Warnings |
| 3 | +--- |
| 4 | + |
| 5 | +# Warnings |
| 6 | + |
| 7 | +When you run Luanti, it often produces helpful warnings |
| 8 | +that indicate issues with mods and games that need to be addressed. |
| 9 | +Most commonly, these are *deprecation warnings* for APIs that |
| 10 | +will change in a future Luanti version. |
| 11 | +This document explains some of the more frequently encountered warnings and how to resolve them. |
| 12 | + |
| 13 | +## Log levels |
| 14 | + |
| 15 | +As a mod developer, you might want to set `chat_log_level = warning` to see client warnings right in your chat. |
| 16 | + |
| 17 | +Your `debug_log_level` should be at least `warning`; you can then find warnings in `debug.txt`. |
| 18 | + |
| 19 | +If you run Luanti in a terminal, you should see warnings in its output. |
| 20 | + |
| 21 | +Note that stack traces are logged at `info` log level. |
| 22 | + |
| 23 | +## Global variable access |
| 24 | + |
| 25 | +Using a static analysis tool like [luacheck](https://github.com/lunarmodules/luacheck/) |
| 26 | +to catch mistakes related to global variables *before the broken code is run* is strongly recommended. |
| 27 | +Luanti also has its own rudimentary warnings for some basic yet frequent mistakes *at runtime*: |
| 28 | + |
| 29 | +> Undeclared global variable "..." accessed at ... |
| 30 | +
|
| 31 | +You are trying to access a global variable which does not exist. |
| 32 | +This is likely a bug: The intent is to access a (local) variable, but instead you get `nil`, |
| 33 | +for example because you mistyped the variable, the variable declaration was moved, |
| 34 | +or the assignment to a global variable was moved. |
| 35 | + |
| 36 | +```lua |
| 37 | +local bar = 42 |
| 38 | +... |
| 39 | +local foo = baz -- typo! meant bar! |
| 40 | +print(foo) -- unexpected nil |
| 41 | +``` |
| 42 | + |
| 43 | +If you want to check whether a global variable does exist, for example for a mod you're optionally depending on, |
| 44 | +you can use either `rawget(_G, "name")` or `core.global_exists("name")`. |
| 45 | +After you have done the check, you can simply access the global variable, though you may want to localize it instead: |
| 46 | + |
| 47 | +```lua |
| 48 | +-- at the top of the file: |
| 49 | +local my_opt_dep = rawget(_G, "my_opt_dep") |
| 50 | +... |
| 51 | +-- somewhere deep in the remaining code: |
| 52 | +if my_opt_dep then my_opt_dep.frobnicate(...) end |
| 53 | +``` |
| 54 | + |
| 55 | +> Assignment to undeclared global variable "..." |
| 56 | +
|
| 57 | +You are assigning a value to a global variable which was not assigned at load time. |
| 58 | +This usually happens when you forgot to make a variable `local`: |
| 59 | + |
| 60 | +```lua |
| 61 | +local function frognicate(...) |
| 62 | + pi = 3.14 -- oops! should be local! |
| 63 | +end |
| 64 | +``` |
| 65 | + |
| 66 | +This is typically just a minor code quality issue. |
| 67 | +However it is likely to lead to bugs, especially if this happens in multiple places: |
| 68 | +You may overwrite another mod's global variables accidentally; |
| 69 | +recursive functions may overwrite what ought to be local variables. |
| 70 | + |
| 71 | +Instead, you should use a local variable. |
| 72 | +In general, mods should avoid polluting global variables. |
| 73 | +If your mod needs to expose an API, it is recommended to use a single global variable named after the mod. |
| 74 | + |
| 75 | +## Media files |
| 76 | + |
| 77 | +> Server: ignoring file as it has disallowed characters: "<filename>" |
| 78 | +
|
| 79 | +A file is not being considered as a media file as it contains disallowed characters. |
| 80 | +You can use `find -name <filename>` to find the path of the offending file. |
| 81 | + |
| 82 | +Your media files should only use the following characters in their name: |
| 83 | + |
| 84 | +* ASCII letters (`a`-`z`, `A`-`Z`) |
| 85 | +* ASCII digits (`0`-`9`) |
| 86 | +* Underscore (`_`) |
| 87 | +* Hyphen (`-`) |
| 88 | +* Period (`.`) |
| 89 | + |
| 90 | +The fix is simply to rename or, if obsolete, remove the file. |
| 91 | + |
| 92 | +By convention, your media file names should typically start with the mod name followed by an underscore. |
| 93 | +This avoids conflicts and makes it clear which mod a file belongs to. |
| 94 | + |
| 95 | +### PNG |
| 96 | + |
| 97 | +> iCCP: known incorrect sRGB profile |
| 98 | +
|
| 99 | +This warning concerning color profiles is produced by libpng. |
| 100 | +How to fix it is discussed e.g. [on StackOverflow](https://stackoverflow.com/questions/22745076/libpng-warning-iccp-known-incorrect-srgb-profile). |
| 101 | +Re-exporting images with a proper exporter, e.g. using ImageMagick's `mogrify`, fixes the files. |
| 102 | + |
| 103 | +> Interlace handling should be turned on when using `png_read_image` |
| 104 | +
|
| 105 | +[Adam7 interlacing](https://en.wikipedia.org/wiki/Adam7_algorithm) |
| 106 | +lets someone who has only partially received an image see a degraded version of it. |
| 107 | +Because Luanti only loads images once they have been fully received, |
| 108 | +and interlacing typically increases file size, there is no point at all in using interlacing. |
| 109 | + |
| 110 | +Export your images without interlacing. |
| 111 | +Make sure that the "Interlacing (Adam7)" checkbox is unchecked when exporting PNGs from GIMP. |
| 112 | + |
| 113 | +### glTF |
| 114 | + |
| 115 | +> embedded images are not supported |
| 116 | +
|
| 117 | +Your mesh contains embedded images which will be ignored by Luanti. |
| 118 | +This unnecessarily increases file size and might confuse people who work with the model. |
| 119 | + |
| 120 | +Textures are supplied via the `textures` property (for entities) or the `tiles` node definition field (for nodes). |
| 121 | + |
| 122 | +If you're already using a different texture or after you have extracted the texture, you can simply strip images. |
| 123 | +Both extracting and stripping images can be done using [gltfutil](https://github.com/luanti-org/modtools). |
| 124 | +(Since `.gltf` files are just JSON, they are also fairly easy to edit manually. |
| 125 | +In this case, you need to remove the `"images"` property and remove the `"source"` property from all textures.) |
| 126 | + |
| 127 | +Note that URI references to external images are not supported by Luanti |
| 128 | +and hence should not appear in your glTF files either. |
| 129 | + |
| 130 | +> multiple animations are not supported |
| 131 | +
|
| 132 | +The mesh contains multiple glTF animations ("timelines"), |
| 133 | +but Luanti only supports a single timeline and ignores all timelines except for the first one. |
| 134 | + |
| 135 | +You need to batch all animations into a single one and use frame ranges within this animation. |
| 136 | + |
| 137 | +See also [Using Blender](/for-creators/models/using-blender/). |
| 138 | + |
| 139 | +> negative weights |
| 140 | +
|
| 141 | +The mesh contains negative vertex weights which were ignored. |
| 142 | +The affected weights should be set to zero (or outright removed via some other means) instead. |
| 143 | + |
| 144 | +> nodes using matrix transforms must not be animated |
| 145 | +
|
| 146 | +A node uses a matrix transformation, yet there is an animation channel targeting this node. |
| 147 | +This is problematic because the TRS (translation, rotation, scale) decomposition of the matrix need not exist, |
| 148 | +and even if it does exist, need not be unique (e.g. half-turns are equivalent to inverting an axis). |
| 149 | + |
| 150 | +For reasons like these, glTF does not allow animated nodes to use matrix transforms. |
| 151 | +Luanti will warn about this and ignore the animation. |
| 152 | + |
| 153 | +If you do want to animate the node, decompose the matrix into TRS properties yourself. |
| 154 | +Otherwise simply remove the animation channel targeting the node. |
| 155 | + |
| 156 | +## Deprecation warnings |
| 157 | + |
| 158 | +Luanti typically logs deprecation warnings if you use the API in a way that will stop working in a future version. |
| 159 | +Make sure to check the "compatibility notes" section of the relevant changelog. |
| 160 | + |
| 161 | +If you want to continue supporting older Luanti versions, |
| 162 | +you might have to check for the existence of an API and fall back to using the legacy API if necessary. |
| 163 | + |
| 164 | +> Mod ... at ...: |
| 165 | +> Mods not having a `mod.conf` file with the name is deprecated. |
| 166 | +> `depends.txt` is deprecated, please use `mod.conf` instead. |
| 167 | +> `description.txt` is deprecated, please use `mod.conf` instead. |
| 168 | +
|
| 169 | +Very old Luanti versions used to expect mod metadata in a bunch of separate files. |
| 170 | +The mod name was taken to be simply the folder name. |
| 171 | +(This sometimes caused problems when users downloaded mods and got a folder with a different name, e.g. `mod-master`.) |
| 172 | + |
| 173 | +Newer Luanti versions expect mod metadata, including the mod name, in a `mod.conf` file. |
| 174 | + |
| 175 | +If `depends.txt` looked like this: |
| 176 | + |
| 177 | +``` |
| 178 | +my_dep_1 |
| 179 | +my_dep_2 |
| 180 | +my_opt_dep_1? |
| 181 | +my_opt_dep_2? |
| 182 | +``` |
| 183 | + |
| 184 | +you would create a `mod.conf` in the mod folder with the following contents: |
| 185 | + |
| 186 | +``` |
| 187 | +name = <folder name> |
| 188 | +depends = my_dep_1, my_dep_2 |
| 189 | +optional_depends = my_opt_dep_1, my_opt_dep_2 |
| 190 | +description = <contents of description.txt> |
| 191 | +``` |
| 192 | + |
| 193 | +> Field "use_texture_alpha" on node ...: Boolean values are deprecated; use the new choices |
| 194 | +
|
| 195 | +Luanti now gives more fine-grained control over node `use_texture_alpha`. |
| 196 | +If your node is supposed to be opaque (`use_texture_alpha = false`), you should use `use_texture_alpha = "opaque"`. |
| 197 | +This enables some optimizations and makes cheating with texture packs harder. |
| 198 | +If your node is supposed to have transparent areas, but each pixel is either fully opaque or fully transparent, |
| 199 | +use `use_texture_alpha = "clip"`. |
| 200 | +Only if your node needs semitransparent areas should you pick `use_texture_alpha = "blend"`. |
| 201 | +Always using `use_texture_alpha = "blend"` may have disastrous consequences for rendering performance. |
| 202 | + |
| 203 | +> Field "image" on TileDef: Deprecated: new name is "name". |
| 204 | +
|
| 205 | +You have a node tile table with `{image = "...", ...}`; it should be `{name = "...", ...}` instead. |
| 206 | + |
| 207 | +> Reading initial object properties directly from an entity definition is deprecated, |
| 208 | +> move it to the 'initial_properties' table instead. (Property 'prop' in entity '...') |
| 209 | +
|
| 210 | +This happens if your code looks something like this: |
| 211 | + |
| 212 | +```lua |
| 213 | +core.register_entity("my_mod:my_ent", { |
| 214 | + prop = ..., -- initial property right in the entity definition table :( |
| 215 | + ..., |
| 216 | +}) |
| 217 | +``` |
| 218 | + |
| 219 | +which means that `self.prop` will evaluate to the *initial* property; |
| 220 | +modifying `self.prop` will modify the common *initial* property for everyone. |
| 221 | +This is a frequent source of mistakes. |
| 222 | + |
| 223 | +If you want to work with per-instance properties, you need to use |
| 224 | +`self.object:set_properties({prop = ...})` for updates, |
| 225 | +and `self.object:get_properties().prop` to query them |
| 226 | +(for performance reasons, you might want to cache this). |
| 227 | + |
| 228 | +Your entity definition should look like this: |
| 229 | + |
| 230 | +```lua |
| 231 | +core.register_entity("my_mod:my_ent", { |
| 232 | + initial_properties = { |
| 233 | + prop = ..., -- initial property in the proper table :) |
| 234 | + ..., |
| 235 | + }, |
| 236 | + ... |
| 237 | +}) |
| 238 | +``` |
| 239 | + |
| 240 | +If you happened to access initial properties via `self.prop` or `entity_def.prop`, |
| 241 | +you of course now need to access them as |
| 242 | +`self.initial_properties.prop` resp. `entity_def.initial_properties.prop` instead. |
| 243 | + |
| 244 | +> Deprecated call to `set_bone_position`, use `set_bone_override` instead |
| 245 | +
|
| 246 | +Do what it says and switch to the new API. |
| 247 | +Be careful: This is not just a rename, `set_bone_override` expects its arguments in a slightly different format. |
| 248 | + |
| 249 | +> Deprecated call to `get_attribute`, use MetaDataRef methods instead. |
| 250 | +
|
| 251 | +Instead of `player:get_attribute(name)`, use `player:get_meta():get(name)`. |
| 252 | + |
| 253 | +> Deprecated call to `set_attribute`, use MetaDataRef methods instead. |
| 254 | +
|
| 255 | +Instead of `player:set_attribute(name, value)`, use `player:get_meta():set_string(name, value)`. |
| 256 | + |
| 257 | +> Calling `get_connected_players()` at mod load time is deprecated |
| 258 | +
|
| 259 | +Such a call is pointless as it will always be `{}`. |
| 260 | +Doing anything "for each connected player" at load time simply makes no sense: |
| 261 | +There are no connected players. |
| 262 | + |
| 263 | +This warning likely requires eliminating some dead code, |
| 264 | +and possibly a bit of refactoring, to resolve properly. |
| 265 | +Often, it might makes sense to postpone the call to the first server step |
| 266 | +via `core.after(0, my_mod_step)`. |
| 267 | +(A common cause is that some mod "step" function is run initially at mod load time and iterates over connected players.) |
0 commit comments