commit f91d7157fa0857602277815971bc341f039c7835 Author: Aaron Suen Date: Sat Jun 15 19:51:32 2024 -0400 Initial completion diff --git a/.luacheckrc b/.luacheckrc new file mode 100644 index 0000000..c98c5e1 --- /dev/null +++ b/.luacheckrc @@ -0,0 +1,12 @@ +globals = { + "minetest", + "vector", + "ItemStack", + "VoxelArea", + "vector", + "SecureRandom", + "PcgRandom + "profiler", +} +color = false +quiet = 1 diff --git a/.lualocals b/.lualocals new file mode 100644 index 0000000..2374748 --- /dev/null +++ b/.lualocals @@ -0,0 +1,7 @@ +minetest +ItemStack +VoxelArea +vector +SecureRandom +PcgRandom +profiler \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..edbf555 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright (C)2024 Aaron Suen + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject +to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/aaschemlib/alphabet.lua b/aaschemlib/alphabet.lua new file mode 100644 index 0000000..010e967 --- /dev/null +++ b/aaschemlib/alphabet.lua @@ -0,0 +1,22 @@ +-- LUALOCALS < --------------------------------------------------------- +local string + = string +local string_sub + = string.sub +-- LUALOCALS > --------------------------------------------------------- + +-- Define the chars that are allowed to be used in ascii art tables. +-- The size of the alphabet limits the maximum number of unique node +-- definition types that can be used in a single schematic. +local alphabet = {} +do + -- Only use printable 7-bit ascii characters that don't + -- require escaping in a lua string, for compatibility. + local chars = " .abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + .. "0123456789,/;'[]`-=<>?:{}|~!@#$%^&*()_+" + for i = 1, #chars do + alphabet[#alphabet + 1] = string_sub(chars, i, i) + end +end + +return alphabet diff --git a/aaschemlib/api_asciiart_to_lua.lua b/aaschemlib/api_asciiart_to_lua.lua new file mode 100644 index 0000000..3503f5f --- /dev/null +++ b/aaschemlib/api_asciiart_to_lua.lua @@ -0,0 +1,60 @@ +-- LUALOCALS < --------------------------------------------------------- +local dofile, ipairs, minetest, pairs, rawget, rawset, string, table + = dofile, ipairs, minetest, pairs, rawget, rawset, string, table +local string_format, table_concat + = string.format, table.concat +-- LUALOCALS > --------------------------------------------------------- + +local alphabet = dofile(minetest.get_modpath( + minetest.get_current_modname()) .. "/alphabet.lua") + +-- Dump an asciiart lua table in a format optimized for editing. +-- Use loadstring/dofile/etc to reverse. +local function asciiart_to_lua(aa) + local t = { + "return {", + "\tnodes = {", + } + for _, k in pairs(alphabet) do + local v = aa.nodes[k] + if v then + local u = {} + if v.name then u[#u + 1] = string_format("name = %q", v.name) end + if v.param2 then u[#u + 1] = string_format("param2 = %d", v.param2) end + if v.prob then u[#u + 1] = string_format("prob = %d", v.prob) end + if v.force then u[#u + 1] = "force = true" end + t[#t + 1] = string_format("\t\t[%q] = {%s},", k, table_concat(u, ", ")) + end + end + t[#t + 1] = "\t}," + if aa.sliceprob then + t[#t + 1] = "\tsliceprob = {" + for y = 0, aa.size.y - 1 do + local p = aa.sliceprob[y] + if p then + t[#t + 1] = string_format("\t\t[%d] = %d,", y, p) + end + end + t[#t + 1] = "\t}," + end + t[#t + 1] = "\tlayers = {" + for _, layer in ipairs(aa.layers) do + t[#t + 1] = "\t\t{" + local row = 1 + for _, row in ipairs(layer) do + t[#t + 1] = string_format("\t\t\t%q,", row) + end + t[#t + 1] = "\t\t}," + end + t[#t + 1] = "\t}," + t[#t + 1] = "}" + return table_concat(t, "\n") +end + +------------------------------------------------------------------------ + +local modname = minetest.get_current_modname() +local api = rawget(_G, modname) or {} +rawset(_G, modname, api) + +api.asciiart_to_lua = asciiart_to_lua diff --git a/aaschemlib/api_asciiart_to_schematic.lua b/aaschemlib/api_asciiart_to_schematic.lua new file mode 100644 index 0000000..07b18f2 --- /dev/null +++ b/aaschemlib/api_asciiart_to_schematic.lua @@ -0,0 +1,43 @@ +-- LUALOCALS < --------------------------------------------------------- +local error, ipairs, minetest, pairs, rawget, rawset + = error, ipairs, minetest, pairs, rawget, rawset +-- LUALOCALS > --------------------------------------------------------- + +-- Convert an asciiart lua table back into a minetest schematic one, +-- reversing schematic_to_asciiart. +local function asciiart_to_schematic(aa) + local schem = { + size = {y = #aa.layers}, + data = {}, + yslice_prob = {}, + } + for y, ys in ipairs(aa.layers) do + if schem.size.z and schem.size.z ~= #ys then + error("inconsistent z size") + end + schem.size.z = #ys + for z, zs in ipairs(ys) do + if schem.size.x and schem.size.x ~= #zs then + error("inconsistent x size") + end + schem.size.x = #zs + for x = 1, zs:len() do + local node = aa.nodes[zs:sub(x, x)] + schem.data[(schem.size.z - z) * schem.size.x * schem.size.y + + (y - 1) * schem.size.x + x] = node + end + end + end + for k, v in pairs(aa.sliceprob or {}) do + yslice_prob[#yslice_prob + 1] = {ypos = k, prob = v} + end + return schem +end + +------------------------------------------------------------------------ + +local modname = minetest.get_current_modname() +local api = rawget(_G, modname) or {} +rawset(_G, modname, api) + +api.asciiart_to_schematic = asciiart_to_schematic diff --git a/aaschemlib/api_schematic_to_asciiart.lua b/aaschemlib/api_schematic_to_asciiart.lua new file mode 100644 index 0000000..cba6bd0 --- /dev/null +++ b/aaschemlib/api_schematic_to_asciiart.lua @@ -0,0 +1,103 @@ +-- LUALOCALS < --------------------------------------------------------- +local dofile, error, minetest, next, pairs, rawget, rawset, string + = dofile, error, minetest, next, pairs, rawget, rawset, string +local string_sub + = string.sub +-- LUALOCALS > --------------------------------------------------------- + +local alphabet = dofile(minetest.get_modpath( + minetest.get_current_modname()) .. "/alphabet.lua") + +-- Convert a schematic lua table (in the format of minetest.read_schematic) +-- into an asciiart lua table. Use asciiart_to_schematic to reverse. +local function schematic_to_asciiart(mts) + local nodes = { + -- Reserve space for "void" (don't overwrite existing node) + [" "] = {prob = 0}, + -- Reserve period for air (just remove existing node) + ["."] = {name = "air"}, + } + local alphaidx = 3 + local usage = {} + + local layers = {} + local function set(x, y, z, s) + x, y, z = x + 1, y + 1, mts.size.z - z + local l = layers[y] + if not l then + l = {} + layers[y] = l + end + local r = l[z] or "" + while (#r < x) do r = r .. " " end + l[z] = string_sub(r, 1, x - 1) + .. s .. string_sub(r, x + 2) + usage[s] = true + end + + local i = 1 + for z = 0, mts.size.z - 1 do + for y = 0, mts.size.y - 1 do + for x = 0, mts.size.x - 1 do + local v = mts.data[i] + i = i + 1 + local t = { + name = v.name, + param2 = v.param2, + prob = v.prob, + force = v.force_place or nil + } + if t.param2 == 0 then t.param2 = nil end + if t.prob and t.prob >= 254 then t.prob = nil end + if t.prob and t.prob <= 0 then + t.name = nil + t.param2 = nil + end + local added + for nk, nv in pairs(nodes) do + if nv.name == t.name and nv.param2 == t.param2 + and nv.prob == nv.prob and nv.force == t.force then + set(x, y, z, nk) + added = true + break + end + end + if not added then + local s = alphabet[alphaidx] + alphaidx = alphaidx + 1 + if not s then error("too many unique nodes for chars") end + nodes[s] = t + set(x, y, z, s) + end + end + end + end + + local t = {} + for k, v in pairs(mts) do t[k] = v end + t.size = nil + t.data = nil + t.layers = layers + t.nodes = {} + for k in pairs(usage) do + t.nodes[k] = nodes[k] + end + if t.yslice_prob and next(t.yslice_prob) then + local d = {} + for _, v in pairs(t.yslice_prob) do + d[v.ypos] = v.prob + end + t.sliceprob = d + end + t.yslice_prob = nil + + return t +end + +------------------------------------------------------------------------ + +local modname = minetest.get_current_modname() +local api = rawget(_G, modname) or {} +rawset(_G, modname, api) + +api.schematic_to_asciiart = schematic_to_asciiart diff --git a/aaschemlib/init.lua b/aaschemlib/init.lua new file mode 100644 index 0000000..d957cd6 --- /dev/null +++ b/aaschemlib/init.lua @@ -0,0 +1,11 @@ +-- LUALOCALS < --------------------------------------------------------- +local dofile, ipairs, minetest + = dofile, ipairs, minetest +-- LUALOCALS > --------------------------------------------------------- + +local modpath = minetest.get_modpath(minetest.get_current_modname()) +for _, fn in ipairs({ + "api_asciiart_to_lua", + "api_asciiart_to_schematic", + "api_schematic_to_asciiart", + }) do dofile(modpath .. "/" .. fn .. ".lua") end diff --git a/aaschemlib/mod.conf b/aaschemlib/mod.conf new file mode 100644 index 0000000..103b0a7 --- /dev/null +++ b/aaschemlib/mod.conf @@ -0,0 +1,3 @@ +name = aaschemlib +title = ASCII Art Schematic Library +description = Library for converting Minetest schematics to/from ASCII art \ No newline at end of file diff --git a/aaschemlib/screenshot.png b/aaschemlib/screenshot.png new file mode 100644 index 0000000..7a50696 Binary files /dev/null and b/aaschemlib/screenshot.png differ diff --git a/aaschemtools/api_fileconv.lua b/aaschemtools/api_fileconv.lua new file mode 100644 index 0000000..ab89063 --- /dev/null +++ b/aaschemtools/api_fileconv.lua @@ -0,0 +1,53 @@ +-- LUALOCALS < --------------------------------------------------------- +local io, minetest, pairs, pcall, rawget, rawset, string, unpack + = io, minetest, pairs, pcall, rawget, rawset, string, unpack +local io_open, string_format, string_sub + = io.open, string.format, string.sub +-- LUALOCALS > --------------------------------------------------------- + +local function fileconv(func) + return function(_, param) + local src, dest = unpack(param:split(" ")) + if (not src) or (src == "") or (not dest) or (dest == "") then + return false, "incorrect params" + end + + -- Input pathnames can be prefixed with modname: to make the path + -- relative to a loaded mod. Input pathnames can be prefixed with + -- just a bare colon to make the path relative to the loaded world. + for _, n in pairs(minetest.get_modnames()) do + if string_sub(src, 1, #n + 1) == n .. ":" then + src = minetest.get_modpath(n) .. "/" .. string_sub(src, #n + 2) + end + end + if string_sub(src, 1, 1) == ":" then + src = minetest.get_worldpath() .. "/" .. string_sub(src, 2) + end + + -- Output pathnames must always be relative to the world path. + dest = minetest.get_worldpath() .. "/" .. dest + + -- Run the conversion process and check for errors. + local okay, result = pcall(func, src, dest) + if not okay then + return false, string_format("error convertin %q to %q: %s", src, dest, result) + end + + -- Write the output + local f = io_open(dest, "wb") + if not f then + return false, string_format("failed to open %q for writing", dest) + end + f:write(result) + f:close() + return true, string_format("wrote %d bytes to %q", #result, dest) + end +end + +------------------------------------------------------------------------ + +local modname = minetest.get_current_modname() +local api = rawget(_G, modname) or {} +rawset(_G, modname, api) + +api.fileconv = fileconv diff --git a/aaschemtools/cmd_aa2mts.lua b/aaschemtools/cmd_aa2mts.lua new file mode 100644 index 0000000..d522d0d --- /dev/null +++ b/aaschemtools/cmd_aa2mts.lua @@ -0,0 +1,18 @@ +-- LUALOCALS < --------------------------------------------------------- +local dofile, minetest, rawget + = dofile, minetest, rawget +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() +local api = rawget(_G, modname) + +minetest.register_chatcommand("aa2mts", { + privs = {server = true}, + description = "Convert Lua AA schematic to MTS (WARNING: Arbitrary Code Execution)", + params = "modname:path/to/file.lua output.mts", + func = api.fileconv(function(src, dest) + return minetest.serialize_schematic( + aaschemlib.asciiart_to_schematic(dofile(src)), + "mts", {}) + end) + }) diff --git a/aaschemtools/cmd_mts2aa.lua b/aaschemtools/cmd_mts2aa.lua new file mode 100644 index 0000000..63ea156 --- /dev/null +++ b/aaschemtools/cmd_mts2aa.lua @@ -0,0 +1,22 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, rawget + = minetest, rawget +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() +local api = rawget(_G, modname) + +minetest.register_chatcommand("mts2aa", { + privs = {server = true}, + description = "Convert MTS schematic to Lua AA", + params = "modname:path/to/file.mts output.lua", + func = api.fileconv(function(src, dest) + return aaschemlib.asciiart_to_lua( + aaschemlib.schematic_to_asciiart( + minetest.read_schematic(src, { + write_yslice_prob = "low" + }) + ) + ) + end) + }) \ No newline at end of file diff --git a/aaschemtools/init.lua b/aaschemtools/init.lua new file mode 100644 index 0000000..e340bb7 --- /dev/null +++ b/aaschemtools/init.lua @@ -0,0 +1,11 @@ +-- LUALOCALS < --------------------------------------------------------- +local dofile, ipairs, minetest + = dofile, ipairs, minetest +-- LUALOCALS > --------------------------------------------------------- + +local modpath = minetest.get_modpath(minetest.get_current_modname()) +for _, fn in ipairs({ + "api_fileconv", + "cmd_mts2aa", + "cmd_aa2mts", + }) do dofile(modpath .. "/" .. fn .. ".lua") end diff --git a/aaschemtools/mod.conf b/aaschemtools/mod.conf new file mode 100644 index 0000000..b8dfc8e --- /dev/null +++ b/aaschemtools/mod.conf @@ -0,0 +1,4 @@ +name = aaschemtools +title = ASCII Art Schematic Tools +description = Developer tools for converting Minetest schematics to/from ASCII art +depends = aaschemlib \ No newline at end of file diff --git a/aaschemtools/screenshot.png b/aaschemtools/screenshot.png new file mode 100644 index 0000000..7a50696 Binary files /dev/null and b/aaschemtools/screenshot.png differ diff --git a/cdb-screen.webp b/cdb-screen.webp new file mode 100644 index 0000000..3e6b703 Binary files /dev/null and b/cdb-screen.webp differ diff --git a/modpack.conf b/modpack.conf new file mode 100644 index 0000000..db88311 --- /dev/null +++ b/modpack.conf @@ -0,0 +1,2 @@ +title = ASCII Art Schematics +description = Manage Minetest schematics as ASCII art \ No newline at end of file diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..7a50696 Binary files /dev/null and b/screenshot.png differ