Use tabs not spaces

This commit is contained in:
Lars Mueller 2021-07-14 11:51:47 +02:00
parent a86b24eef5
commit daa0251632
7 changed files with 395 additions and 394 deletions

@ -44,21 +44,21 @@ Binary Lua object notation. **Experimental.** Handling of subnormal numbers (ver
```lua ```lua
def = { def = {
aux_is_valid = function(object) aux_is_valid = function(object)
return is_valid return is_valid
end, end,
aux_len = function(object) aux_len = function(object)
return length_in_bytes return length_in_bytes
end, end,
-- read type byte, stream providing :read(count), map of references -> id -- read type byte, stream providing :read(count), map of references -> id
aux_read = function(type, stream, references) aux_read = function(type, stream, references)
... = stream:read(...) ... = stream:read(...)
return object return object
end, end,
-- object to be written, stream providing :write(text), list of references -- object to be written, stream providing :write(text), list of references
aux_write = function(object, stream, references) aux_write = function(object, stream, references)
stream:write(...) stream:write(...)
end end
} }
``` ```
@ -128,22 +128,22 @@ assert(modlib.bluon:read(object, rope) == true)
```lua ```lua
-- Serializes all userdata to a constant string: -- Serializes all userdata to a constant string:
local custom_bluon = bluon.new{ local custom_bluon = bluon.new{
aux_is_valid = function(object) aux_is_valid = function(object)
return type(object) == "userdata" return type(object) == "userdata"
end, end,
aux_len = function(object) aux_len = function(object)
return 1 + ("userdata"):len()) return 1 + ("userdata"):len())
end, end,
aux_read = function(type, stream, references) aux_read = function(type, stream, references)
assert(type == 100, "unsupported type") assert(type == 100, "unsupported type")
assert(stream:read(("userdata"):len()) == "userdata") assert(stream:read(("userdata"):len()) == "userdata")
return userdata() return userdata()
end, end,
-- object to be written, stream providing :write(text), list of references -- object to be written, stream providing :write(text), list of references
aux_write = function(object, stream, references) aux_write = function(object, stream, references)
assert(type(object) == "userdata") assert(type(object) == "userdata")
stream:write"\100userdata" stream:write"\100userdata"
end end
} }
-- Write to text -- Write to text
local rope = modlib.table.rope{} local rope = modlib.table.rope{}
@ -255,21 +255,21 @@ A schematic format with support for metadata and baked light data. **Experimenta
* Fixes * Fixes
* Colorspec * Colorspec
* Stricter patterns in `colorspec:from_string` * Stricter patterns in `colorspec:from_string`
* `colorspec:to_string` works now * `colorspec:to_string` works now
* Lua log file * Lua log file
* Patched memory leaks * Patched memory leaks
* Added option to not reference strings * Added option to not reference strings
* Handling of circular tables * Handling of circular tables
* Additions * Additions
* `luon`: Configurable serialization to and from Lua with circular and string reference support * `luon`: Configurable serialization to and from Lua with circular and string reference support
* `minetest.luon` even supports `ItemStack` and `AreaStore` * `minetest.luon` even supports `ItemStack` and `AreaStore`
* `vector.rotate3` * `vector.rotate3`
* `table.deep_foreach_any` * `table.deep_foreach_any`
* `func`: * `func`:
* `func.iterate` * `func.iterate`
* `func.aggregate` * `func.aggregate`
* Functional wrappers for operators * Functional wrappers for operators
* Improvements * Improvements
* Code quality * Code quality
* Performance * Performance

@ -9,82 +9,82 @@ list.metatable = metatable
-- Takes a list -- Takes a list
function list:new() function list:new()
self.head = 0 self.head = 0
self.length = #self self.length = #self
return setmetatable(self, metatable) return setmetatable(self, metatable)
end end
function list:in_bounds(index) function list:in_bounds(index)
return index >= 1 and index <= self.length return index >= 1 and index <= self.length
end end
function list:get(index) function list:get(index)
return self[self.head + index] return self[self.head + index]
end end
function list:set(index, value) function list:set(index, value)
assert(value ~= nil) assert(value ~= nil)
self[self.head + index] = value self[self.head + index] = value
end end
function list:len() function list:len()
return self.length return self.length
end end
function list:ipairs() function list:ipairs()
local index = 1 local index = 1
return function() return function()
if index > self.length then if index > self.length then
return return
end end
return index, self[self.head + index] return index, self[self.head + index]
end end
end end
function list:rpairs() function list:rpairs()
local index = self.length local index = self.length
return function() return function()
if index < 1 then if index < 1 then
return return
end end
return index, self[self.head + index] return index, self[self.head + index]
end end
end end
function list:push_tail(value) function list:push_tail(value)
assert(value ~= nil) assert(value ~= nil)
self.length = self.length + 1 self.length = self.length + 1
self[self.head + self.length] = value self[self.head + self.length] = value
end end
function list:get_tail() function list:get_tail()
return self[self.head + self.length] return self[self.head + self.length]
end end
function list:pop_tail() function list:pop_tail()
if self.length == 0 then return end if self.length == 0 then return end
local value = self:get_tail() local value = self:get_tail()
self[self.head + self.length] = nil self[self.head + self.length] = nil
self.length = self.length - 1 self.length = self.length - 1
return value return value
end end
function list:push_head(value) function list:push_head(value)
self[self.head] = value self[self.head] = value
self.head = self.head - 1 self.head = self.head - 1
self.length = self.length + 1 self.length = self.length + 1
end end
function list:get_head() function list:get_head()
return self[self.head + 1] return self[self.head + 1]
end end
function list:pop_head() function list:pop_head()
if self.length == 0 then return end if self.length == 0 then return end
local value = self:get_head() local value = self:get_head()
self.head = self.head + 1 self.head = self.head + 1
self[self.head] = nil self[self.head] = nil
return value return value
end end
return list return list

279
luon.lua

@ -1,16 +1,16 @@
local assert, next, pairs, pcall, error, type, table_insert, table_concat, string_format, string_match, setmetatable, select, setfenv, math_huge, loadfile, loadstring local assert, next, pairs, pcall, error, type, table_insert, table_concat, string_format, string_match, setmetatable, select, setfenv, math_huge, loadfile, loadstring
= assert, next, pairs, pcall, error, type, table.insert, table.concat, string.format, string.match, setmetatable, select, setfenv, math.huge, loadfile, loadstring = assert, next, pairs, pcall, error, type, table.insert, table.concat, string.format, string.match, setmetatable, select, setfenv, math.huge, loadfile, loadstring
local count_objects = modlib.table.count_objects local count_objects = modlib.table.count_objects
-- Build a table with the succeeding character from A-Z -- Build a table with the succeeding character from A-Z
local succ = {} local succ = {}
for char = ("A"):byte(), ("Z"):byte() - 1 do for char = ("A"):byte(), ("Z"):byte() - 1 do
succ[string.char(char)] = string.char(char + 1) succ[string.char(char)] = string.char(char + 1)
end end
local function quote(string) local function quote(string)
return string_format("%q", string) return string_format("%q", string)
end end
local _ENV = {} local _ENV = {}
@ -19,169 +19,170 @@ local metatable = {__index = _ENV}
_ENV.metatable = metatable _ENV.metatable = metatable
function new(self) function new(self)
return setmetatable(self, metatable) return setmetatable(self, metatable)
end end
function aux_write(_self, _object) function aux_write(_self, _object)
-- returns reader, arguments -- returns reader, arguments
return return
end end
aux_read = {} aux_read = {}
function write(self, value, write) function write(self, value, write)
local reference = {"A"} local reference = {"A"}
local function increment_reference(place) local function increment_reference(place)
if not reference[place] then if not reference[place] then
reference[place] = "B" reference[place] = "B"
elseif reference[place] == "Z" then elseif reference[place] == "Z" then
reference[place] = "A" reference[place] = "A"
return increment_reference(place + 1) return increment_reference(place + 1)
else else
reference[place] = succ[reference[place]] reference[place] = succ[reference[place]]
end end
end end
local references = {} local references = {}
local to_fill = {} local to_fill = {}
-- TODO sort objects by count, give frequently referenced objects shorter references -- TODO sort objects by count, give frequently referenced objects shorter references
for object, count in pairs(count_objects(value)) do for object, count in pairs(count_objects(value)) do
local type_ = type(object) local type_ = type(object)
if count >= 2 and (type_ ~= "string" or #reference + 2 < #object) then if count >= 2 and (type_ ~= "string" or #reference + 2 < #object) then
local ref = table_concat(reference) local ref = table_concat(reference)
write(ref) write(ref)
write"=" write"="
write(type_ == "table" and "{}" or quote(object)) write(type_ == "table" and "{}" or quote(object))
write";" write";"
references[object] = ref references[object] = ref
if type_ == "table" then if type_ == "table" then
to_fill[object] = ref to_fill[object] = ref
end end
increment_reference(1) increment_reference(1)
end end
end end
local function is_short_key(key) local function is_short_key(key)
return not references[key] and type(key) == "string" and string_match(key, "^[%a_][%a%d_]*$") return not references[key] and type(key) == "string" and string_match(key, "^[%a_][%a%d_]*$")
end end
local function dump(value) local function dump(value)
-- Primitive types -- Primitive types
if value == nil then if value == nil then
return write"nil" return write"nil"
end end
if value == true then if value == true then
return write"true" return write"true"
end end
if value == false then if value == false then
return write"false" return write"false"
end end
local type_ = type(value) local type_ = type(value)
if type_ == "number" then if type_ == "number" then
return write(string_format("%.17g", value)) return write(string_format("%.17g", value))
end end
-- Reference types: table and string -- Reference types: table and string
local ref = references[value] local ref = references[value]
if ref then if ref then
-- Referenced -- Referenced
return write(ref) return write(ref)
elseif type_ == "string" then elseif type_ == "string" then
return write(quote(value)) return write(quote(value))
elseif type_ == "table" then elseif type_ == "table" then
local first = true local first = true
write"{" write"{"
local len = #value local len = #value
for i = 1, len do for i = 1, len do
if not first then write";" end if not first then write";" end
dump(value[i]) dump(value[i])
first = false first = false
end end
for k, v in next, value do for k, v in next, value do
if type(k) ~= "number" or k % 1 ~= 0 or k < 1 or k > len then if type(k) ~= "number" or k % 1 ~= 0 or k < 1 or k > len then
if not first then write";" end if not first then write";" end
if is_short_key(k) then if is_short_key(k) then
write(k) write(k)
else else
write"[" write"["
dump(k) dump(k)
write"]" write"]"
end end
write"=" write"="
dump(v) dump(v)
first = false first = false
end end
end end
write"}" write"}"
else else
-- TODO move aux_write to start, to allow dealing with metatables etc.? -- TODO move aux_write to start, to allow dealing with metatables etc.?
(function(func, ...) (function(func, ...)
-- functions are the only way to deal with varargs -- functions are the only way to deal with varargs
if not func then if not func then
return error("unsupported type: " .. type_) return error("unsupported type: " .. type_)
end end
write(func) write(func)
write"(" write"("
local n = select("#", ...) local n = select("#", ...)
local args = {...} local args = {...}
for i = 1, n - 1 do for i = 1, n - 1 do
dump(args[i]) dump(args[i])
write"," write","
end end
if n > 0 then if n > 0 then
write(args[n]) write(args[n])
end end
write")" write")"
end)(self:aux_write(value)) end)(self:aux_write(value))
end end
end end
for table, ref in pairs(to_fill) do for table, ref in pairs(to_fill) do
for k, v in pairs(table) do for k, v in pairs(table) do
write(ref) write(ref)
if is_short_key(k) then if is_short_key(k) then
write"." write"."
write(k) write(k)
else else
write"[" write"["
dump(k) dump(k)
write"]" write"]"
end end
write"=" write"="
dump(v) dump(v)
write";" write";"
end end
end end
write"return " write"return "
dump(value) dump(value)
end end
function write_file(self, value, file) function write_file(self, value, file)
return self:write(value, function(text) return self:write(value, function(text)
file:write(text) file:write(text)
end) end)
end end
function write_string(self, value) function write_string(self, value)
local rope = {} local rope = {}
self:write(value, function(text) self:write(value, function(text)
table_insert(rope, text) table_insert(rope, text)
end) end)
return table_concat(rope) return table_concat(rope)
end end
function read(self, ...) function read(self, ...)
local read = assert(...) local read = assert(...)
-- math.huge is serialized to inf, 0/0 is serialized to -nan -- math.huge is serialized to inf, 0/0 is serialized to -nan
-- TODO verify this is actually the case, see https://en.wikipedia.org/wiki/Printf_format_string
setfenv(read, setmetatable({inf = math_huge, nan = 0/0}, {__index = self.aux_read})) setfenv(read, setmetatable({inf = math_huge, nan = 0/0}, {__index = self.aux_read}))
local success, value_or_err = pcall(read) local success, value_or_err = pcall(read)
if success then if success then
return value_or_err return value_or_err
end end
return nil, value_or_err return nil, value_or_err
end end
function read_file(self, path) function read_file(self, path)
return self:read(loadfile(path)) return self:read(loadfile(path))
end end
function read_string(self, string) function read_string(self, string)
return self:read(loadstring(string)) return self:read(loadstring(string))
end end
return _ENV return _ENV

@ -1,6 +1,6 @@
local _ENV = setmetatable({}, {__index = _G}) local _ENV = setmetatable({}, {__index = _G})
local function load(filename) local function load(filename)
assert(loadfile(modlib.mod.get_resource(modlib.modname, "minetest", filename .. ".lua")))(_ENV) assert(loadfile(modlib.mod.get_resource(modlib.modname, "minetest", filename .. ".lua")))(_ENV)
end end
load"misc" load"misc"
load"collisionboxes" load"collisionboxes"

@ -1,29 +1,29 @@
-- Localize globals -- Localize globals
local getmetatable, AreaStore, ItemStack local getmetatable, AreaStore, ItemStack
= getmetatable, AreaStore, ItemStack = getmetatable, AreaStore, ItemStack
-- Metatable lookup for classes specified in lua_api.txt, section "Class reference" -- Metatable lookup for classes specified in lua_api.txt, section "Class reference"
local AreaStoreMT = getmetatable(AreaStore()) local AreaStoreMT = getmetatable(AreaStore())
local ItemStackMT = getmetatable(ItemStack"") local ItemStackMT = getmetatable(ItemStack"")
local metatables = { local metatables = {
[AreaStoreMT] = {name = "AreaStore", method = AreaStoreMT.to_string}, [AreaStoreMT] = {name = "AreaStore", method = AreaStoreMT.to_string},
[ItemStackMT] = {name = "ItemStack", method = ItemStackMT.to_table}, [ItemStackMT] = {name = "ItemStack", method = ItemStackMT.to_table},
-- TODO expand -- TODO expand
} }
(...).luon = modlib.luon.new{ (...).luon = modlib.luon.new{
aux_write = function(_, value) aux_write = function(_, value)
local type = metatables[getmetatable(value)] local type = metatables[getmetatable(value)]
if type then if type then
return type.name, type.method(value) return type.name, type.method(value)
end end
end, end,
aux_read = { aux_read = {
AreaStore = function(...) AreaStore = function(...)
local store = AreaStore() local store = AreaStore()
store:from_string(...) store:from_string(...)
return store return store
end, end,
ItemStack = ItemStack ItemStack = ItemStack
} }
} }

@ -1,6 +1,6 @@
-- Localize globals -- Localize globals
local VoxelArea, ItemStack, assert, error, io, ipairs, math, minetest, modlib, next, pairs, setmetatable, string, table, type, vector local VoxelArea, ItemStack, assert, error, io, ipairs, math, minetest, modlib, next, pairs, setmetatable, string, table, type, vector
= VoxelArea, ItemStack, assert, error, io, ipairs, math, minetest, modlib, next, pairs, setmetatable, string, table, type, vector = VoxelArea, ItemStack, assert, error, io, ipairs, math, minetest, modlib, next, pairs, setmetatable, string, table, type, vector
-- Set environment -- Set environment
local _ENV = ... local _ENV = ...
@ -10,179 +10,179 @@ schematic = {}
local metatable = {__index = schematic} local metatable = {__index = schematic}
function schematic.setmetatable(self) function schematic.setmetatable(self)
return setmetatable(self, metatable) return setmetatable(self, metatable)
end end
function schematic.create(self, pos_min, pos_max) function schematic.create(self, pos_min, pos_max)
self.size = vector.subtract(pos_max, pos_min) self.size = vector.subtract(pos_max, pos_min)
local voxelmanip = minetest.get_voxel_manip(pos_min, pos_max) local voxelmanip = minetest.get_voxel_manip(pos_min, pos_max)
local emin, emax = voxelmanip:read_from_map(pos_min, pos_max) local emin, emax = voxelmanip:read_from_map(pos_min, pos_max)
local voxelarea = VoxelArea:new{ MinEdge = emin, MaxEdge = emax } local voxelarea = VoxelArea:new{ MinEdge = emin, MaxEdge = emax }
local nodes, light_values, param2s = {}, self.light_values and {}, {} local nodes, light_values, param2s = {}, self.light_values and {}, {}
local vm_nodes, vm_light_values, vm_param2s = voxelmanip:get_data(), light_values and voxelmanip:get_light_data(), voxelmanip:get_param2_data() local vm_nodes, vm_light_values, vm_param2s = voxelmanip:get_data(), light_values and voxelmanip:get_light_data(), voxelmanip:get_param2_data()
local node_names, node_ids = {}, {} local node_names, node_ids = {}, {}
local i = 0 local i = 0
for index in voxelarea:iterp(pos_min, pos_max) do for index in voxelarea:iterp(pos_min, pos_max) do
if nodes[index] == minetest.CONTENT_UNKNOWN or nodes[index] == minetest.CONTENT_IGNORE then if nodes[index] == minetest.CONTENT_UNKNOWN or nodes[index] == minetest.CONTENT_IGNORE then
error("unknown or ignore node at " .. minetest.pos_to_string(voxelarea:position(index))) error("unknown or ignore node at " .. minetest.pos_to_string(voxelarea:position(index)))
end end
local name = minetest.get_name_from_content_id(vm_nodes[index]) local name = minetest.get_name_from_content_id(vm_nodes[index])
local id = node_ids[name] local id = node_ids[name]
if not id then if not id then
table.insert(node_names, name) table.insert(node_names, name)
id = #node_names id = #node_names
node_ids[name] = id node_ids[name] = id
end end
i = i + 1 i = i + 1
nodes[i] = id nodes[i] = id
if self.light_values then if self.light_values then
light_values[i] = vm_light_values[index] light_values[i] = vm_light_values[index]
end end
param2s[i] = vm_param2s[index] param2s[i] = vm_param2s[index]
end end
local metas = self.metas local metas = self.metas
if metas or metas == nil then if metas or metas == nil then
local indexing = vector.add(self.size, 1) local indexing = vector.add(self.size, 1)
metas = {} metas = {}
for _, pos in ipairs(minetest.find_nodes_with_meta(pos_min, pos_max)) do for _, pos in ipairs(minetest.find_nodes_with_meta(pos_min, pos_max)) do
local meta = minetest.get_meta(pos):to_table() local meta = minetest.get_meta(pos):to_table()
if next(meta.fields) ~= nil or next(meta.inventory) ~= nil then if next(meta.fields) ~= nil or next(meta.inventory) ~= nil then
local relative = vector.subtract(pos, pos_min) local relative = vector.subtract(pos, pos_min)
metas[((relative.z * indexing.y) + relative.y) * indexing.x + relative.x] = meta metas[((relative.z * indexing.y) + relative.y) * indexing.x + relative.x] = meta
end end
end end
end end
self.node_names = node_names self.node_names = node_names
self.nodes = nodes self.nodes = nodes
self.light_values = light_values self.light_values = light_values
self.param2s = param2s self.param2s = param2s
self.metas = metas self.metas = metas
return schematic.setmetatable(self) return schematic.setmetatable(self)
end end
function schematic:write_to_voxelmanip(voxelmanip, pos_min) function schematic:write_to_voxelmanip(voxelmanip, pos_min)
local pos_max = vector.add(pos_min, self.size) local pos_max = vector.add(pos_min, self.size)
local emin, emax = voxelmanip:read_from_map(pos_min, pos_max) local emin, emax = voxelmanip:read_from_map(pos_min, pos_max)
local voxelarea = VoxelArea:new{ MinEdge = emin, MaxEdge = emax } local voxelarea = VoxelArea:new{ MinEdge = emin, MaxEdge = emax }
local nodes, light_values, param2s, metas = self.nodes, self.light_values, self.param2s, self.metas local nodes, light_values, param2s, metas = self.nodes, self.light_values, self.param2s, self.metas
local vm_nodes, vm_lights, vm_param2s = voxelmanip:get_data(), light_values and voxelmanip:get_light_data(), voxelmanip:get_param2_data() local vm_nodes, vm_lights, vm_param2s = voxelmanip:get_data(), light_values and voxelmanip:get_light_data(), voxelmanip:get_param2_data()
for _, pos in ipairs(minetest.find_nodes_with_meta(pos_min, pos_max)) do for _, pos in ipairs(minetest.find_nodes_with_meta(pos_min, pos_max)) do
-- Clear all metadata. Due to an engine bug, nodes will actually have empty metadata. -- Clear all metadata. Due to an engine bug, nodes will actually have empty metadata.
minetest.get_meta(pos):from_table{} minetest.get_meta(pos):from_table{}
end end
local content_ids = {} local content_ids = {}
for index, name in ipairs(self.node_names) do for index, name in ipairs(self.node_names) do
content_ids[index] = assert(minetest.get_content_id(name), ("unknown node %q"):format(name)) content_ids[index] = assert(minetest.get_content_id(name), ("unknown node %q"):format(name))
end end
local i = 0 local i = 0
for index in voxelarea:iterp(pos_min, pos_max) do for index in voxelarea:iterp(pos_min, pos_max) do
i = i + 1 i = i + 1
vm_nodes[index] = content_ids[nodes[i]] vm_nodes[index] = content_ids[nodes[i]]
if light_values then if light_values then
vm_lights[index] = light_values[i] vm_lights[index] = light_values[i]
end end
vm_param2s[index] = param2s[i] vm_param2s[index] = param2s[i]
end end
voxelmanip:set_data(vm_nodes) voxelmanip:set_data(vm_nodes)
if light_values then if light_values then
voxelmanip:set_light_data(vm_lights) voxelmanip:set_light_data(vm_lights)
end end
voxelmanip:set_param2_data(vm_param2s) voxelmanip:set_param2_data(vm_param2s)
if metas then if metas then
local indexing = vector.add(self.size, 1) local indexing = vector.add(self.size, 1)
for index, meta in pairs(metas) do for index, meta in pairs(metas) do
local floored = math.floor(index / indexing.x) local floored = math.floor(index / indexing.x)
local relative = { local relative = {
x = index % indexing.x, x = index % indexing.x,
y = floored % indexing.y, y = floored % indexing.y,
z = math.floor(floored / indexing.y) z = math.floor(floored / indexing.y)
} }
minetest.get_meta(vector.add(relative, pos_min)):from_table(meta) minetest.get_meta(vector.add(relative, pos_min)):from_table(meta)
end end
end end
end end
function schematic:place(pos_min) function schematic:place(pos_min)
local pos_max = vector.add(pos_min, self.size) local pos_max = vector.add(pos_min, self.size)
local voxelmanip = minetest.get_voxel_manip(pos_min, pos_max) local voxelmanip = minetest.get_voxel_manip(pos_min, pos_max)
self:write_to_voxelmanip(voxelmanip, pos_min) self:write_to_voxelmanip(voxelmanip, pos_min)
voxelmanip:write_to_map(not self.light_values) voxelmanip:write_to_map(not self.light_values)
return voxelmanip return voxelmanip
end end
local function table_to_byte_string(tab) local function table_to_byte_string(tab)
if not tab then return end if not tab then return end
return table.concat(modlib.table.map(tab, string.char)) return table.concat(modlib.table.map(tab, string.char))
end end
local function write_bluon(self, stream) local function write_bluon(self, stream)
local metas, light_values, param2s = self.metas, self.light_values, self.param2s local metas, light_values, param2s = self.metas, self.light_values, self.param2s
self.metas = modlib.table.copy(metas) self.metas = modlib.table.copy(metas)
for _, meta in pairs(self.metas) do for _, meta in pairs(self.metas) do
for _, list in pairs(meta.inventory) do for _, list in pairs(meta.inventory) do
for index, stack in pairs(list) do for index, stack in pairs(list) do
list[index] = stack:to_string() list[index] = stack:to_string()
end end
end end
end end
self.light_values, self.param2s = table_to_byte_string(light_values), table_to_byte_string(param2s) self.light_values, self.param2s = table_to_byte_string(light_values), table_to_byte_string(param2s)
modlib.bluon:write(self, stream) modlib.bluon:write(self, stream)
self.metas, self.light_values, self.param2s = metas, light_values, param2s self.metas, self.light_values, self.param2s = metas, light_values, param2s
end end
function schematic:write_bluon(path) function schematic:write_bluon(path)
local file = io.open(path, "wb") local file = io.open(path, "wb")
-- Header, short for "ModLib Bluon Schematic" -- Header, short for "ModLib Bluon Schematic"
file:write"MLBS" file:write"MLBS"
write_bluon(self, file) write_bluon(self, file)
file:close() file:close()
end end
local function byte_string_to_table(self, field) local function byte_string_to_table(self, field)
local byte_string = self[field] local byte_string = self[field]
if not byte_string then return end if not byte_string then return end
local tab = {} local tab = {}
for i = 1, #byte_string do for i = 1, #byte_string do
tab[i] = byte_string:byte(i) tab[i] = byte_string:byte(i)
end end
self[field] = tab self[field] = tab
end end
local function read_bluon(file) local function read_bluon(file)
local self = modlib.bluon:read(file) local self = modlib.bluon:read(file)
assert(not file:read(1), "expected EOF") assert(not file:read(1), "expected EOF")
for _, meta in pairs(self.metas) do for _, meta in pairs(self.metas) do
for _, list in pairs(meta.inventory) do for _, list in pairs(meta.inventory) do
for index, itemstring in pairs(list) do for index, itemstring in pairs(list) do
assert(type(itemstring) == "string") assert(type(itemstring) == "string")
list[index] = ItemStack(itemstring) list[index] = ItemStack(itemstring)
end end
end end
end end
byte_string_to_table(self, "light_values") byte_string_to_table(self, "light_values")
byte_string_to_table(self, "param2s") byte_string_to_table(self, "param2s")
return self return self
end end
function schematic.read_bluon(path) function schematic.read_bluon(path)
local file = io.open(path, "rb") local file = io.open(path, "rb")
assert(file:read(4) == "MLBS", "not a modlib bluon schematic") assert(file:read(4) == "MLBS", "not a modlib bluon schematic")
return schematic.setmetatable(read_bluon(file)) return schematic.setmetatable(read_bluon(file))
end end
function schematic:write_zlib_bluon(path, compression) function schematic:write_zlib_bluon(path, compression)
local file = io.open(path, "wb") local file = io.open(path, "wb")
-- Header, short for "ModLib Zlib-compressed-bluon Schematic" -- Header, short for "ModLib Zlib-compressed-bluon Schematic"
file:write"MLZS" file:write"MLZS"
local rope = modlib.table.rope{} local rope = modlib.table.rope{}
write_bluon(self, rope) write_bluon(self, rope)
local text = rope:to_text() local text = rope:to_text()
file:write(minetest.compress(text, "deflate", compression or 9)) file:write(minetest.compress(text, "deflate", compression or 9))
file:close() file:close()
end end
function schematic.read_zlib_bluon(path) function schematic.read_zlib_bluon(path)
local file = io.open(path, "rb") local file = io.open(path, "rb")
assert(file:read(4) == "MLZS", "not a modlib zlib compressed bluon schematic") assert(file:read(4) == "MLZS", "not a modlib zlib compressed bluon schematic")
return schematic.setmetatable(read_bluon(modlib.text.inputstream(minetest.decompress(file:read"*a", "deflate")))) return schematic.setmetatable(read_bluon(modlib.text.inputstream(minetest.decompress(file:read"*a", "deflate"))))
end end

@ -339,25 +339,25 @@ end
-- Recursively counts occurences of objects (non-primitives including strings) in a table. -- Recursively counts occurences of objects (non-primitives including strings) in a table.
function count_objects(value) function count_objects(value)
local counts = {} local counts = {}
if value == nil then if value == nil then
-- Early return for nil -- Early return for nil
return counts return counts
end end
local function count_values(value) local function count_values(value)
local type_ = type(value) local type_ = type(value)
if type_ == "boolean" or type_ == "number" then return end if type_ == "boolean" or type_ == "number" then return end
local count = counts[value] local count = counts[value]
counts[value] = (count or 0) + 1 counts[value] = (count or 0) + 1
if not count and type_ == "table" then if not count and type_ == "table" then
for k, v in pairs(value) do for k, v in pairs(value) do
count_values(k) count_values(k)
count_values(v) count_values(v)
end end
end end
end end
count_values(value) count_values(value)
return counts return counts
end end
function foreach_value(table, func) function foreach_value(table, func)