Helpers
Helpers are a set of auxiliary Lua functions for comfortable work in the project.
These helper functions significantly extend the standard Lua capabilities and simplify development.
String
Methods provided by Luanti:
string:lower()
Converts string to lowercase with Cyrillic support.
print(('ПРИВЕТ МИР'):lower()) -- 'привет мир'string:upper()
Converts string to uppercase with Cyrillic support.
print(string.upper('привет мир')) -- 'ПРИВЕТ МИР'string:is_one_of(table)
Checks if the string is one of the values in the passed array.
(Semantic analogue of table.contains, which checks if the string is contained in the passed array.)
local text = 'hello'
local options = { 'hello', 'world', 'lua' }
print(text:is_one_of(options)) -- truestring:first_to_upper()
Converts the first letter to uppercase.
local text = 'hello world'
print(text:first_to_upper()) -- 'Hello world'string :title()/:to_headline()
Converts the first letter of each word to title case.
Alternative name: string:to_headline()
local text = 'hello world from lua'
print(text:title()) -- 'Hello World From Lua'
print(text:to_headline()) -- 'Hello World From Lua'string:starts_with(prefix)
Checks if the string starts with the specified prefix.
local filename = 'config.json'
print(filename:starts_with('config')) -- true
print(filename:starts_with('settings')) -- falsestring:ends_with(suffix)
Checks if the string ends with the specified suffix.
local filename = 'config.json'
print(filename:ends_with('.json')) -- true
print(filename:ends_with('.conf')) -- falsestring:contains(sub_string)
Checks for the presence of a substring.
local text = 'Hello, World !!!'
print(text:contains('World')) -- true
print(text:contains('world')) -- false (case sensitive)string:replace(pattern, replacement, n?)
Replaces substring (analogue of gsub, but returns only the string without the number of replacements).
See documentation for string.gsub and Patterns
local text = 'Hello, World, World!'
local result = text:replace('World', 'Lua')
print(result) -- 'Hello, Lua, Lua!'
-- Replace only the first occurrence
local result2 = text:replace('World', 'Lua', 1)
print(result2) -- 'Hello, Lua, World!'string:remove(pattern, n?)
Removes substring.
See documentation for string.gsub and Patterns
local text = 'Hello, World!'
print(text:remove(', ')) -- 'HelloWorld!'
print(text:remove('o', 1)) -- 'Hell, Wrld!' (remove only the first 'o')string:reg_escape()
Escapes special characters for regular expressions.
See Patterns
local pattern = 'file.txt'
print(pattern:reg_escape()) -- 'file%.txt'
local pattern2 = '^$()%.[]*+-?)'
print(pattern2:reg_escape()) -- '%^%$()%.%[%]%*%+\-%?)'string:vxr_split(delimiter?, processor?)
Splits string by specified delimiter with the ability to process parts.
Luanti has its own string.split method, but it doesn't support processing parts.
This method adds the ability to process string parts.
-- Without processing
('hello world'):vxr_split() -- { 'hello', 'world' }
('apple,banana,cherry'):vxr_split(',') -- { 'apple', 'banana', 'cherry' }
-- With processing
local numbers = '1,2,3,4,5'
local squared = numbers:vxr_split(',', function(x)
return tonumber(x)^2
end)
print(squared[1], squared[2], squared[3], squared[4], squared[5])
-- 1 4 9 16 25string.or_nil(value)
Converts to string or returns nil.
print(string.or_nil('hello')) -- 'hello'
print(string.or_nil(42)) -- '42'
print(string.or_nil(nil)) -- nil
print(tostring(nil)) -- 'nil' (comparison)Methods provided by Luanti:
Luanti string.split()
Also see: string.vxr_split(delimiter?, processor?)
Splits string into parts by specified separator. Returns array of strings. Arguments:
separator?separator, default:","include_empty?default:falsemax_splits?if negative, splits are unlimited, default:-1sep_is_pattern?whether separator is a regular string or a pattern (regex), default:false
local text = 'apple,banana,cherry'
local parts = text.split(',')
print(parts[1], parts[2], parts[3]) -- 'apple', 'banana', 'cherry'
-- Split by spaces
local words = 'hello world lua'.split(' ')
print(words[1], words[2], words[3]) -- 'hello', 'world', 'lua'Luanti string.trim()
Removes whitespace characters from the beginning and end of the string.
print(("\n \t\tfoo bar\t "):trim()) -- 'foo bar'Luanti string.pack()
Packs values into a binary string according to format.
Backport from Lua 5.4.
-- Pack integers
local packed = string.pack('i4', 42)
print(#packed) -- 4 (size in bytes)
-- Pack multiple values
local data = string.pack('i4f8', 100, 3.14)
print(#data) -- 12 (4 + 8 bytes)Luanti string.unpack()
Unpacks binary string into values according to format.
Backport from Lua 5.4.
local packed = string.pack('i4f8', 100, 3.14)
local num, float, next_pos = string.unpack('i4f8', packed)
print(num, float) -- 100, 3.14
print(next_pos) -- 13 (position after unpacking)
-- Unpack from specified position
local value = string.unpack('i4', packed, 5)
print(value) -- 3.14 (as float)Luanti string.packsize()
Returns the size of the string that will be created by string.pack.
Backport from Lua 5.4.
local size = string.packsize('i4f8')
print(size) -- 12 (4 + 8 bytes)
-- For complex formats
local complex_size = string.packsize('i4c10f8')
print(complex_size) -- 22 (4 + 10 + 8 bytes)Table
Methods, provided by Luanti:
table.copy()table.copy_with_metatables()table.insert_all()
table.indexof()table.keyof()table.key_value_swap()table.shuffle()
table.keys(table)
Returns an array of table keys.
local data = { name = 'Alek', age = 25, city = 'Vladivostok' }
local keys = table.keys(data)
-- `keys` contains `{ 'name', 'age', 'city' }` (order may vary)table.values(table)
Returns an array of table values.
local data = { name = 'Alek', age = 25, city = 'Vladivostok' }
local values = table.values(data)
-- `values` contains { 'Alek', 25, 'Vladivostok' }table.has_key(table, key)
Checks if a key exists in the table.
local data = { name = 'Alek', age = 25 }
print(table.has_key(data, 'name')) -- true
print(table.has_key(data, 'city')) -- falsetable .contains/has_value(table, value)
Checks if a value exists in the table.
local fruits = { 'apple', 'banana', 'cherry' }
print(table.contains(fruits, 'banana')) -- true
print(table.contains(fruits, 'orange')) -- falseYou can also use string:is_one_of():
print('banana':is_one_of(fruits)) -- true
print('orange':is_one_of(fruits)) -- falseAlternative name: table.has_value
local data = { x = 10, y = 20 }
print(table.has_value(data, 10)) -- true
print(table.has_value(data, 30)) -- falsetable.keys_of(table, value)
Returns a table with keys from the specified table that have the specified value.
local data = { a = 10, b = 20, c = 10, d = 30 }
local keys = table.keys_of(data, 10)
print(dump(keys)) -- { 'a', 'c' }
local keys2 = table.keys_of(data, 99)
print(keys2) -- nil (no such keys)table.has_any_key(table, find_keys)
Checks if the table keys contain at least one value from the specified array.
local data = { name = 'test', age = 25, active = true }
local find_keys = { 'name', 'email', 'phone' }
print(table.has_any_key(data, find_keys)) -- true (key 'name' is in find_keys)
local find_keys2 = { 'email', 'phone', 'address' }
print(table.has_any_key(data, find_keys2)) -- falsetable.equals(table1, table2)
Recursively compares two tables for complete equality.
local table1 = { a = 1, b = { c = 2, d = 3 } }
local table2 = { a = 1, b = { c = 2, d = 3 } }
local table3 = { a = 1, b = { c = 2, d = 4 } }
print(table.equals(table1, table2)) -- true
print(table.equals(table1, table3)) -- falsetable.is_empty(table)
Checks if the table is empty.
print(table.is_empty({})) -- true
print(table.is_empty({ a = 1 })) -- falsetable.is_position(table)
Checks if the table represents coordinates.
print(table.is_position({ x = 10, y = 20, z = 30 })) -- true
print(table.is_position({ x = 10, y = 20 })) -- false
print(table.is_position({ a = 1, b = 2, c = 3 })) -- falsetable.each_value_is(table, value)
Checks that all elements of the table are equal to the specified value. By default checks for true.
local data1 = { true, true, true }
print(table.each_value_is(data1)) -- true
local data2 = { 5, 5, 5, 5 }
print(table.each_value_is(data2, 5)) -- true
local data3 = { 1, 2, 3 }
print(table.each_value_is(data3, 1)) -- falsetable.count(table)
Counts the number of elements in the table.
NOTE
For tables with integer keys, use #table.
Since the # operator doesn't work on tables with non-integer keys, use table.count for them.
local data = { a = 1, b = 2, c = 3 }
print(table.count(data)) -- 3
print(#data) -- 0, because the `#` operator doesn't work with non-integer keys.
local array = { 1, 2, 3, 4, 5 }
print(#array) -- 5table.generate_sequence(max, start_from?, step?)
Generates a sequence of numbers.
local seq1 = table.generate_sequence(5)
print(dump(seq1)) -- { 1, 2, 3, 4, 5 }
local seq2 = table.generate_sequence(10, 2, 2)
print(dump(seq2)) -- { 2, 4, 6, 8, 10 }
local seq3 = table.generate_sequence(1, 5, -1)
print(dump(seq3)) -- { 5, 4, 3, 2, 1 }table.only(table, only)
Returns a new table containing data only with the specified keys.
local data = { name = 'Alek', age = 25, city = 'Vladivostok', country = 'Russia' }
local filtered = table.only(data, { 'name', 'age' })
-- `filtered` contains `{ name = 'Alek', age = 25 }`
print(filtered.name, filtered.age) -- 'Alek', 25
print(filtered.city) -- niltable.except(table, keys)
Returns a new table containing data without the specified keys.
(copies the table, excluding the specified keys)
local data = { name = 'Alek', age = 25, city = 'Vladivostok', country = 'Russia' }
local filtered = table.except(data, { 'age', 'country' })
-- `filtered` contains `{ name = 'Alek', city = 'Vladivostok' }`
print(filtered.name, filtered.city) -- 'Alek', 'Vladivostok'
print(filtered.age, filtered.country) -- nil, niltable.merge(table1, table2, overwrite?)
Recursively merges tables.
By default, the first table is not overwritten, a new one is created.
local defaults = { theme = 'dark', font = { size = 12, family = 'Arial' } }
local user_config = { font = { size = 16 }, language = 'en' }
local merged = table.merge(defaults, user_config)
print(dump(merged))
-- {
-- theme = "dark",
-- font = {
-- size = 16,
-- family = "Arial",
-- },
-- language = "en",
-- }Overwrite the first table:
table.merge(defaults, user_config, true)TIP
It's better to use table.overwrite() for better readability.
table.join(table1, table2, recursively?)
Adds missing keys from table2 to table1.
Values that are tables are copied using table.copy().
local base = { a = 1, b = 2 }
local addition = { b = 10, c = 3 }
table.join(base, addition)
print(dump(base)) -- { a = 1, b = 2, c = 3 }If recursively is true, the function will be applied recursively only for values that are tables in both table1 and table2.
local base = { a = 1, b = { c = 2 } }
local addition = { b = { c = 10, d = 20 } }
table.join(base, addition, true)
print(dump(base))
-- {
-- a = 1,
-- b = {
-- c = 2,
-- d = 20,
-- },
-- }table.overwrite(table1, table2)
Completely overwrites table1 with values from table2.
Semantic variant for table.merge(table1, table2, true).
table.merge_values(table1, table2)
Merges values from two tables into one, removing duplicates.
Order is preserved - first values from table1, then from table2 that weren't in table1.
local table1 = { 'apple', 'banana', 'cherry' }
local table2 = { 'banana', 'date', 'apple', 'elderberry' }
local merged = table.merge_values(table1, table2)
print(dump(merged)) -- { 'apple', 'banana', 'cherry', 'date', 'elderberry' }table.map(table, callback, overwrite?)
Applies function to each element of the table.callback: fun(value: any, key: any): any - accepts value and key, returns new value.
By default returns a new table.
local numbers = { 1, 2, 3, 4, 5 }
local squared = table.map(numbers, function(x) return x * x end)
print(dump(squared))
-- { 1, 4, 9, 16, 25 }
local data = { a = 10, b = 20 }
local doubled = table.map(data, function(value, key)
print(key, value)
return value * 2
end)
-- a 10
-- b 20
print(dump(doubled))
-- { a = 20, b = 40 }If overwrite is true, then the function modifies the original table.
local data = { a = 10, b = 20 }
table.map(data, function(value, key) return value * 2 end, true)
print(dump(data)) -- { a = 20, b = 40 }table .walk/.each(table, callback)
Iterates over the table applying function (does not modify the table).callback: fun(value: any, key: any): void - accepts value and key; returns nothing.
local data = { a = 10, b = 20, c = 30 }
table.walk(data, function(value, key)
print(key, value)
end)
-- a 10
-- c 30
-- b 20Alternative name: table.each()
local data = { a = 10, b = 20, c = 30 }
table.each(data, function(value, key)
data[key] = value * 2
-- Nothing prevents accessing upvalue `data`. Now `data` is overwritten.
end)
print(dump(data)) -- { a = 20, b = 40, c = 60 }table.multiply_each_value(table, multiplier_table)
Multiplies each value of the table by the corresponding value from multiplier_table by key.
local data = { a = 10, b = 20, c = 30 }
local multipliers = { a = 2, b = 0.5, c = 3 }
local result = table.multiply_each_value(data, multipliers)
print(dump(result)) -- { a = 20, b = 10, c = 90 }
-- Keys without a multiplier remain unchangedtable.add_values(table1, table2, empty_value?, overwrite?)
Adds values with the same keys from two tables.
local table1 = { a = 10, b = 20 }
local table2 = { b = 5, c = 15 }
local result = table.add_values(table1, table2)
print(dump(result)) -- { a = 10, b = 25, c = 15 }
-- With empty value for missing keys
local result2 = table.add_values(table1, table2, 0)
print(dump(result2)) -- { a = 10, b = 25, c = 15 }table.sub_values(table1, table2, empty_value?, overwrite?)
Subtracts table2 values from table1 for the same keys.
local table1 = { a = 10, b = 20, c = 30 }
local table2 = { b = 5, c = 10, d = 5 }
local result = table.sub_values(table1, table2)
print(dump(result)) -- { a = 10, b = 15, c = 20, d = -5 }table.mul_values(table1, table2, empty_value?, overwrite?)
Multiplies values with the same keys from two tables.
local table1 = { a = 2, b = 3 }
local table2 = { b = 4, c = 5 }
local result = table.mul_values(table1, table2)
print(dump(result)) -- { a = 2, b = 12, c = 5 }table.div_values(table1, table2, empty_value?, overwrite?)
Divides table1 values by table2 values for the same keys.
local table1 = { a = 10, b = 20, c = 30 }
local table2 = { b = 5, c = 10, d = 2 }
local result = table.div_values(table1, table2)
print(dump(result)) -- { a = 10, b = 4, c = 3, d = 0.5 }Math
math .limit/.clamp(value, min, max)
Limits value to the specified range.
Alternative name: math.clamp().
local health = 150
local max_health = 100
local clamped_health = math.limit(health, 0, max_health)
print(clamped_health) -- 100math.is_within(value, min, max)
Checks that the value is strictly inside the range (min < value < max).
print(math.is_within(5, 1, 10)) -- true
print(math.is_within(1, 1, 10)) -- false (boundaries not included)
print(math.is_within(10, 1, 10)) -- falsemath.is_among(value, min, max)
Checks that the value is inside the range, including boundaries (min <= value <= max).
print(math.is_among(5, 1, 10)) -- true
print(math.is_among(1, 1, 10)) -- true
print(math.is_among(10, 1, 10)) -- truemath.is_in_range(value, min, max)
Checks that the value is inside the half-open interval (min, max] (min < value <= max).
print(math.is_in_range(5, 1, 10)) -- true
print(math.is_in_range(1, 1, 10)) -- false (equals minimum)
print(math.is_in_range(10, 1, 10)) -- true (equals maximum)math.is_near(value, near, gap?)
Checks that the value is close to the specified one (|value - near| <= gap).
print(math.is_near(10, 12)) -- false (gap = 1 by default)
print(math.is_near(11, 12)) -- true
print(math.is_near(10, 12, 3)) -- true (gap = 3)math.quadratic_equation_roots(a, b, c)
Solves quadratic equation of the form y = a*x^2 + b*x + c.
local root1, root2 = math.quadratic_equation_roots(1, -5, 6)
print(root1, root2) -- 3, 2
-- No real roots
local r1, r2 = math.quadratic_equation_roots(1, 0, 1)
print(r1, r2) -- nil, nilmath.point_on_circle(radius, angle)
Calculates a point on a circle.
local x, z = math.point_on_circle(5, math.pi/4)
print(x, z) -- coordinates of a point on a circle with radius 5 at angle 45°Debug
__FILE__(depth?, full?)
Returns the name of the current file or the calling function's file depending on the stack depth.
depth?- call stack depth (default0)full?- show full path (defaultfalse)
-- Get relative file path
print(__FILE__()) -- 'mods/Voxrame/helpers/src/lua_ext/debug.lua'
-- Get full file path
print(__FILE__(0, true)) -- '/home/alek13/projects/my-game/mods/Voxrame/helpers/src/lua_ext/debug.lua'
-- Get file 2 levels up the call stack
print(__FILE__(2)) -- file of the function that called the calling function__LINE__(depth?)
Returns the current line number or the calling function's line number depending on the stack depth.
print(__LINE__()) -- 57
-- Get calling function's line number
print(__LINE__(1)) -- line number in the calling file__FILE_LINE__(depth?, full?)
Returns file and line in format "relative/path/to/file:line".
print(__FILE_LINE__()) -- 'mods/Voxrame/helpers/src/lua_ext/debug.lua:65'
-- With full path
print(__FILE_LINE__(0, true)) -- '/home/alek13/projects/my-game/mods/Voxrame/helpers/src/lua_ext/debug.lua:65'__DIR__(depth?)
Returns the directory of the current file or the calling function's file depending on the stack depth.
print(__DIR__()) -- 'mods/Voxrame/helpers/src/lua_ext/'__FUNC__(depth?)
Returns the name of the current function.
function my_function()
print(__FUNC__()) -- 'my_function'
endprint_dump(depth, with_trace, ...)
Outputs the contents of all passed parameters ....
Before output, adds file name and line @ <file>:<line>.
If with_trace is true, additionally outputs stack trace.
NOTE
If your terminal supports links, each @ <file>:<line> will be linked to open IDE, see readme.md for setup.
TIP
Use pd() and pdt() for more concise syntax. See examples below.
pd(...)
Abbreviation for print_dump. Calls print_dump with with_trace == false.
local player_name = 'test_player'
local position = { x = 10, y = 20, z = 30 }
pd(player_name, position)output:
[93m@ [0m[33mmods/lord/Core/map/src/map/Corridor.lua[0m[97m:[0m[32m14[0m
[36mplayer_name:[0m "test_player"
[36m position:[0m {
x = 10,
y = 20,
z = 30,
}pdt(...)
Abbreviation for print_dump traced. Calls print_dump with with_trace == true.
local player_name = 'test_player'
local position = { x = 10, y = 20, z = 30 }
pdt(player_name, position)output:
[93m@ [0m[33mmods/lord/Core/map/src/map/Corridor.lua[0m[97m:[0m[32m14[0m
[3m[2m 1 [0m[93m@[0m [C][36m: in dofile[0m
[3m[2m 2 [0m[93m@ [0m[33mmods/lord/Core/builtin_ext/src/mod/require.lua[0m[97m:[0m[32m29[0m[36m: in require[0m
[3m[2m 3 [0m[93m@ [0m[33mmods/lord/Core/map/src/map.lua[0m[97m:[0m[32m9[0m[36m: in main[0m
[3m[2m 4 [0m[93m@ [0m [C][36m: in dofile[0m
[3m[2m 5 [0m[93m@ [0m[33mmods/lord/Core/builtin_ext/src/mod/require.lua[0m[97m:[0m[32m29[0m[36m: in require[0m
[3m[2m 6 [0m[93m@ [0m[33mmods/lord/Core/map/init.lua[0m[97m:[0m[32m4[0m[36m: in mod_init_function[0m
[3m[2m 7 [0m[93m@ [0m[33mmods/lord/Core/builtin_ext/src/mod.lua[0m[97m:[0m[32m80[0m[36m: in mod[0m
[3m[2m 8 [0m[93m@ [0m[33mmods/lord/Core/map/init.lua[0m[97m:[0m[32m3[0m[36m: in main[0m
[36mplayer_name:[0m "test_player"
[36m position:[0m {
x = 10,
y = 20,
z = 30,
}debug.measure(name, callback, print_result?)
Measures function execution time.
local function heavy_calculation()
local sum = 0
for i = 1, 2000000 do
sum = sum + math.random(1, 100) + math.sin(i)
end
end
-- Measure with result output
debug.measure('calculation', heavy_calculation, true)
debug.measure('calculation', heavy_calculation, true)
debug.measure('calculation', heavy_calculation, true)
debug.measure('calculation', heavy_calculation, true)
debug.measure('calculation', heavy_calculation, true)
-- output:
-- Measure of [calculation]: Time: 42 ms ; Average: 42 ms
-- Measure of [calculation]: Time: 58 ms ; Average: 50 ms
-- Measure of [calculation]: Time: 58 ms ; Average: 53 ms
-- Measure of [calculation]: Time: 35 ms ; Average: 48 ms
-- Measure of [calculation]: Time: 33 ms ; Average: 45 ms
-- Measure without output
local time, avg, count = debug.measure('calculation', heavy_calculation)
print(time, avg, count)
local time, avg, count = debug.measure('calculation', heavy_calculation)
print(time, avg, count)
-- output:
-- 33.075 43.170833333333 6
-- 33.135 41.737142857143 7debug.mesure_print(name)
Outputs statistics of previous measurements name obtained by calls to debug.measure(name, ....).
debug.mesure_print('calculation')
-- output:
-- Measure of [calculation]: Average time: 42 ms ; Last time: 33 ms ; Count of mesures: 7Global
errorf(message, ...)
Similar to error() in Lua, but message can be a template string with specified parameters, similar to string.format().
local player_name = nil
errorf('Player '%s' not found', player_name)
-- Error: Player 'nil' not foundoutput (when debug is enabled):
[93m@ [0m[33mmods/lord/Core/map/src/map/Corridor.lua[0m[97m:[0m[32m9[0m
[32m++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[0m
[1m[91mERROR:[0m
[91m Player `nil` not found[0m
[1m[91mStack trace:[0m
[3m[2m 1 [0m[93m@[0m [C][36m: in error[0m
[3m[2m 2 [0m[93m@ [0m[33mmods/lord/Core/helpers/src/lua_ext/global.lua[0m[97m:[0m[32m9[0m[36m: in errorf[0m
[3m[2m 3 [0m[93m@ [0m[33mmods/lord/Core/map/src/map/Corridor.lua[0m[97m:[0m[32m12[0m[36m: in main[0m
[3m[2m 4 [0m[93m@ [0m [C][36m: in dofile[0m
[3m[2m 5 [0m[93m@ [0m[33mmods/lord/Core/builtin_ext/src/mod/require.lua[0m[97m:[0m[32m29[0m[36m: in require[0m
[3m[2m 6 [0m[93m@ [0m[33mmods/lord/Core/map/src/map.lua[0m[97m:[0m[32m9[0m[36m: in main[0m
[3m[2m 7 [0m[93m@ [0m [C][36m: in dofile[0m
[3m[2m 8 [0m[93m@ [0m[33mmods/lord/Core/builtin_ext/src/mod/require.lua[0m[97m:[0m[32m29[0m[36m: in require[0m
[3m[2m 9 [0m[93m@ [0m[33mmods/lord/Core/map/init.lua[0m[97m:[0m[32m4[0m[36m: in mod_init_function[0m
[3m[2m 10 [0m[93m@ [0m[33mmods/lord/Core/builtin_ext/src/mod.lua[0m[97m:[0m[32m80[0m[36m: in mod[0m
[3m[2m 11 [0m[93m@ [0m[33mmods/lord/Core/map/init.lua[0m[97m:[0m[32m3[0m[36m: in main[0m
[32m++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[0mError handling in Luanti
WARNING
The stack trace will contain extra lines, because Luanti doesn't pass the specified level to core.error_handler().
errorlf(message, level, ...)
Like in errorf() you can pass a format string and parameters, and also specify the trace level.
errorlf('Error in data', 3, 'value: %d', 42)
-- Error with trace 3 levels upassertf(condition, message, ...)
Similar to assert() in Lua, but as message you can specify a format string with parameters, similar to string.format().
Calls
errorf()if the conditionconditionis not met.
-- Continues execution if player exists, otherwise error
local player = nil
assertf(player, 'Player `%s` not found', player)IO
io.file_exists(name)
Checks if a file exists.
if io.file_exists('config.txt') then
print('Configuration file found')
else
print('Configuration file missing')
endio.dirname(path)
Returns the directory from the path.
print(io.dirname('/home/user/project/file.txt')) -- '/home/user/project'
print(io.dirname('relative/path/file.txt')) -- 'relative/path'
print(io.dirname('file.txt')) -- '.'io.write_to_file(filepath, content, mode?)
Writes content to a file.
local success, error_code, error_message = io.write_to_file('data.txt', 'Hello, World!')
if success then
print('Data successfully written')
else
print('Write error:', error_code, error_message)
end
-- Append to end of file
io.write_to_file('log.txt', 'New entry\n', 'a')io.read_from_file(filepath, mode?)
Reads all content from a file.
Returns a string with file content on success or false, error_code, error_message on error.
local content = io.read_from_file('config.json')
if content then
print('File content:', content)
else
print('File read error')
endError handling
local success, error_code, error_message = io.read_from_file('nonexistent.txt')
if not success then
print('Error:', error_code, error_message)
end
-- Error: 2 nonexistent.txt: No such file or directoryor:
local success = io.read_from_file('nonexistent.txt')
if not success then
local error_code, error_message = io.get_file_error()
print('Error:', error_code, error_message)
end
-- Error: 2 nonexistent.txt: No such file or directoryReading in binary mode
local binary_content = io.read_from_file('image.png', 'rb')
if binary_content then
print('File size:', #binary_content, 'bytes')
endio.get_file_error()
Returns the code and message of the last error from io.read_from_file() or io.write_to_file() functions.
local success = io.read_from_file('nonexistent.txt')
if not success then
local error_code, error_message = io.get_file_error()
print('Error:', error_code, error_message)
end
-- Output: Error: 2 nonexistent.txt: No such file or directorylocal success = io.write_to_file('/readonly/file.txt', 'data')
if not success then
local error_code, error_message = io.get_file_error()
print('Write error:', error_code, error_message)
end
-- Output: Write error: 13 /readonly/file.txt: Permission deniedOS
os.DIRECTORY_SEPARATOR
Directory separator for the current OS.
local DS = os.DIRECTORY_SEPARATOR
print(DS) -- '/' on Linux/Mac, '\' on Windows
print('folder' .. DS .. 'file.txt')
-- on Linux/Mac: 'folder/file.txt'
-- on Windows: 'folder\file.txt'