From e5cb5a5689238ad2e85277751398a1b1494614be Mon Sep 17 00:00:00 2001 From: Xertis Date: Thu, 2 Jan 2025 23:16:53 +0300 Subject: [PATCH 01/24] redesign content --- res/layouts/pages/content.xml | 39 ++++++++++--- res/layouts/pages/content.xml.lua | 95 +++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 9 deletions(-) diff --git a/res/layouts/pages/content.xml b/res/layouts/pages/content.xml index c4ff39e2..d0353a45 100644 --- a/res/layouts/pages/content.xml +++ b/res/layouts/pages/content.xml @@ -1,13 +1,34 @@ - - - + + + + + + + - - - - - + + + + + + + + + + + + + + + + diff --git a/res/layouts/pages/content.xml.lua b/res/layouts/pages/content.xml.lua index d03c6e80..e480b490 100644 --- a/res/layouts/pages/content.xml.lua +++ b/res/layouts/pages/content.xml.lua @@ -8,6 +8,21 @@ end add_packs = {} rem_packs = {} +packs_included = {} +packs_excluded = {} + +packs_info = {} + +local function include(id, is_include) + if is_include then + table.insert(packs_included, id) + table.remove_value(packs_excluded, id) + else + table.insert(packs_excluded, id) + table.remove_value(packs_included, id) + end +end + function apply() core.reconfig_packs(add_packs, rem_packs) if mode ~= "world" then @@ -15,8 +30,37 @@ function apply() end end +function refresh_search() + local search_text = document.search_textbox.text:lower() + local interval = 2 + local step = -1 + + for _, packs in ipairs({packs_excluded, packs_included}) do + local visible = 0 + + for i, v in ipairs(packs) do + local info = packs_info[v] + + local id = info[1] + local title = info[2] + + local content = document["pack_" .. id] + local pos = content.pos + local size = content.size + + if title:lower():find(search_text) or search_text == '' then + content.pos = {pos[1], visible * (size[2] + interval) - step} + visible = visible + 1 + else + content.pos = {pos[1], (visible + #packs - i) * (size[2] + interval) - step} + end + end + end +end + function refresh_changes() document.apply_btn.enabled = (#add_packs>0) or (#rem_packs>0) + refresh_search() end function move_pack(id) @@ -24,22 +68,60 @@ function move_pack(id) if table.has(add_packs, id) then document["pack_"..id]:moveInto(document.packs_add) table.remove_value(add_packs, id) + include(id, false) -- cancel pack removal elseif table.has(rem_packs, id) then document["pack_"..id]:moveInto(document.packs_cur) table.remove_value(rem_packs, id) + include(id, true) -- add pack elseif table.has(packs_installed, id) then document["pack_"..id]:moveInto(document.packs_add) table.insert(rem_packs, id) + include(id, false) -- remove pack else document["pack_"..id]:moveInto(document.packs_cur) table.insert(add_packs, id) + include(id, true) end refresh_changes() end +function move_left() + for _, id in pairs(table.copy(packs_excluded)) do + if not document["pack_"..id].enabled then goto continue end + + include(id, true) + table.insert(add_packs, id) + table.remove_value(rem_packs, id) + document["pack_"..id]:moveInto(document.packs_cur) + + ::continue:: + end + + refresh_changes() +end + +function move_right() + for _, id in pairs(table.copy(packs_included)) do + if not document["pack_"..id].enabled then goto continue end + + include(id, false) + + if table.has(packs_installed, id) then + table.insert(rem_packs, id) + end + + table.remove_value(add_packs, id) + document["pack_"..id]:moveInto(document.packs_add) + + ::continue:: + end + + refresh_changes() +end + function place_pack(panel, packinfo, callback) if packinfo.error then callback = nil @@ -115,12 +197,25 @@ function refresh() place_pack(packs_add, packinfo, callback) end + for _,id in ipairs(base_packs) do + local packinfo = pack.get_info(id) + packs_info[id] = {packinfo.id, packinfo.title} + end + + for _,id in ipairs(packs_all) do + local packinfo = pack.get_info(id) + packs_info[id] = {packinfo.id, packinfo.title} + end + for i,id in ipairs(packs_installed) do if table.has(required, id) then document["pack_"..id].enabled = false end end + if #packs_excluded == 0 then packs_excluded = table.copy(packs_available) end + if #packs_included == 0 then packs_included = table.copy(packs_installed) end + apply_movements(packs_cur, packs_add) refresh_changes() end From a1a4fc97fca8472a21d166d6de7b6063fc6763b0 Mon Sep 17 00:00:00 2001 From: Xertis Date: Thu, 2 Jan 2025 23:31:15 +0300 Subject: [PATCH 02/24] add icons --- res/preload.json | 5 ++++- res/textures/gui/left_arrow.png | Bin 0 -> 202 bytes res/textures/gui/loupe.png | Bin 0 -> 201 bytes res/textures/gui/right_arrow.png | Bin 0 -> 201 bytes 4 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 res/textures/gui/left_arrow.png create mode 100644 res/textures/gui/loupe.png create mode 100644 res/textures/gui/right_arrow.png diff --git a/res/preload.json b/res/preload.json index 5efe0162..ec256e0d 100644 --- a/res/preload.json +++ b/res/preload.json @@ -19,7 +19,10 @@ "gui/cross", "gui/refresh", "gui/folder_icon", - "gui/settings_icon" + "gui/settings_icon", + "gui/loupe", + "gui/left_arrow", + "gui/right_arrow" ], "fonts": [ { diff --git a/res/textures/gui/left_arrow.png b/res/textures/gui/left_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..0d719ff2bc27cf47a743fb18da1be82a8d5ee44e GIT binary patch literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G}E0G|-o|Ns93nW2X+*8wS}k|4j}{|ryJ8+ZYEoCO|{#S9F5hd`K7RKu$Q zC}{8L;uxY4+?-TKNtW2 literal 0 HcmV?d00001 diff --git a/res/textures/gui/loupe.png b/res/textures/gui/loupe.png new file mode 100644 index 0000000000000000000000000000000000000000..1b8a946b138acaed6b4696a2e723e6fc4f3df6d4 GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G}E0G|-o|Ns93nW2X+*8wS}k|4j}{|ryJ8+ZYEoCO|{#S9F5he4R}c>anM zprDNS%G}E0G|-o|Ns93nW2X+*8wS}k|4j}{|ryJ8+ZYEoCO|{#S9F5he4R}c>anM zprDbglh-C-t=Oc+w8KlW9t2k o|GC}1ReRNn&&xbn@P(b-zl&k=j3nppK$93eUHx3vIVCg!06tkhga7~l literal 0 HcmV?d00001 From a5aaddb379bdcab533af499dac458b76ae0e45b6 Mon Sep 17 00:00:00 2001 From: Xertis Date: Fri, 3 Jan 2025 18:07:01 +0300 Subject: [PATCH 03/24] add suggestive signs --- res/layouts/pages/content.xml | 67 ++++++++++++++++++-------------- res/preload.json | 2 +- res/textures/gui/check_mark.png | Bin 0 -> 560 bytes 3 files changed, 38 insertions(+), 31 deletions(-) create mode 100644 res/textures/gui/check_mark.png diff --git a/res/layouts/pages/content.xml b/res/layouts/pages/content.xml index d0353a45..e7482225 100644 --- a/res/layouts/pages/content.xml +++ b/res/layouts/pages/content.xml @@ -1,34 +1,41 @@ - - - + + + - - - + - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/preload.json b/res/preload.json index ec256e0d..558cbba0 100644 --- a/res/preload.json +++ b/res/preload.json @@ -20,7 +20,7 @@ "gui/refresh", "gui/folder_icon", "gui/settings_icon", - "gui/loupe", + "gui/check_mark", "gui/left_arrow", "gui/right_arrow" ], diff --git a/res/textures/gui/check_mark.png b/res/textures/gui/check_mark.png new file mode 100644 index 0000000000000000000000000000000000000000..61e6171184e39316f0e733419fecb5819de51b76 GIT binary patch literal 560 zcmV-00?+-4P)4Tx04UFukv&MmKpe$iQ%gl!9PA+CkfAzR5ET(zY!!-7A=C=3I+$Gg1x*@~ z6c z;Ji;9VI^55J|`YC>4L?&Kfh)c3uQq_0Ptxmc zEqVm>Zvz+CZB5w&E_Z;zCqp)6SMt*m3I*W(jJ_!c4BP^JYhG`yeVjf38R{x^0~{Oz zqeaSI@AB^6&ffk!)9UXBrv`GoY$vfO00009a7bBm000XU000XU0RWnu7ytkOQb|NX zR5(xNkU0*3Fc1T0`Tw6yLl7)BL6k66c$gBnKuKca#bzPNc1w}0%vqm yW`Cr>%$o+CcH| Date: Fri, 3 Jan 2025 18:34:13 +0300 Subject: [PATCH 04/24] content.xml fixing + fixing old bug in new_world.lua --- res/layouts/pages/content.xml | 4 ++-- res/layouts/pages/new_world.xml.lua | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/res/layouts/pages/content.xml b/res/layouts/pages/content.xml index e7482225..37025c69 100644 --- a/res/layouts/pages/content.xml +++ b/res/layouts/pages/content.xml @@ -4,11 +4,11 @@ diff --git a/res/layouts/pages/new_world.xml.lua b/res/layouts/pages/new_world.xml.lua index 25e6f424..0dafa505 100644 --- a/res/layouts/pages/new_world.xml.lua +++ b/res/layouts/pages/new_world.xml.lua @@ -28,7 +28,8 @@ function on_open() document.content_btn.text = string.format( "%s [%s]", gui.str("Content", "menu"), #pack.get_installed() ) - if settings.generator == nil then + + if settings.generator == nil or generation.get_generators()[settings.generator] == nil then settings.generator = generation.get_default_generator() end document.generator_btn.text = string.format( From 866173207a90a70dcb3067c03d283eee27f301a8 Mon Sep 17 00:00:00 2001 From: Xertis <118364459+Xertis@users.noreply.github.com> Date: Fri, 3 Jan 2025 20:19:17 +0300 Subject: [PATCH 05/24] adding comments --- res/layouts/pages/content.xml.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/res/layouts/pages/content.xml.lua b/res/layouts/pages/content.xml.lua index e480b490..73b7d54e 100644 --- a/res/layouts/pages/content.xml.lua +++ b/res/layouts/pages/content.xml.lua @@ -5,9 +5,13 @@ function on_open(params) refresh() end +-- add - packs to be added to the world (after apply) +-- rem - packs that should be removed from the world (after apply) add_packs = {} rem_packs = {} +-- included - connected packs to the world +-- excluded - packs that are not connected to the world packs_included = {} packs_excluded = {} From 6c3dd941ce97ed88649e16146f089a48e18419af Mon Sep 17 00:00:00 2001 From: Xertis Date: Sun, 19 Jan 2025 12:53:32 +0300 Subject: [PATCH 06/24] refact search --- doc/ru/scripting/extensions.md | 5 ++ res/layouts/pages/content.xml.lua | 99 ++++++++++++++++++++----------- res/layouts/templates/pack.xml | 2 +- res/scripts/stdmin.lua | 12 ++++ 4 files changed, 81 insertions(+), 37 deletions(-) diff --git a/doc/ru/scripting/extensions.md b/doc/ru/scripting/extensions.md index ca165b0c..b72b8b09 100644 --- a/doc/ru/scripting/extensions.md +++ b/doc/ru/scripting/extensions.md @@ -52,6 +52,11 @@ table.shuffle(t: table) -> table Перемешивает значения в таблице. +```lua +table.merge(t1: table, t2: table) -> table +``` + +Возвращает объединённую таблицу t1 с t2. ```lua table.tostring(t: table) -> string diff --git a/res/layouts/pages/content.xml.lua b/res/layouts/pages/content.xml.lua index 7270c542..29b8ff68 100644 --- a/res/layouts/pages/content.xml.lua +++ b/res/layouts/pages/content.xml.lua @@ -34,34 +34,60 @@ function apply() end end -function refresh_search() +function reposition_func(pack) + local INTERVAL = 2 + local STEP = -1 + local SIZE = 80 + + local tbl = packs_excluded + if table.has(packs_included, pack) then + tbl = packs_included + end + + local indx = table.index(tbl, pack) - 1 + local pos = {0, (SIZE + INTERVAL) * indx - STEP} + + return pos +end + + +function refresh_search() local search_text = document.search_textbox.text:lower() - local interval = 2 - local step = -1 - for _, packs in ipairs({packs_excluded, packs_included}) do - local visible = 0 + local new_included = table.copy(packs_included) + local new_excluded = table.copy(packs_excluded) - for i, v in ipairs(packs) do - local info = packs_info[v] + local function score(package_name) + if package_name:lower():find(search_text) then + return 1 + end + return 0 + end - local id = info[1] - local title = info[2] + local function sorting(a, b) + local score_a = score(packs_info[a][2]) + local score_b = score(packs_info[b][2]) - local content = document["pack_" .. id] - local pos = content.pos - local size = content.size - - if title:lower():find(search_text) or search_text == '' then - content.pos = {pos[1], visible * (size[2] + interval) - step} - visible = visible + 1 - else - content.pos = {pos[1], (visible + #packs - i) * (size[2] + interval) - step} - end + if score_a ~= score_b then + return score_a > score_b + else + return packs_info[a][2] < packs_info[b][2] end end + + table.sort(new_included, sorting) + table.sort(new_excluded, sorting) + + packs_included = new_included + packs_excluded = new_excluded + + for _, id in ipairs(table.merge(table.copy(packs_included), packs_excluded)) do + local content = document["pack_" .. id] + content:reposition() + end end + function refresh_changes() document.apply_btn.enabled = (#add_packs>0) or (#rem_packs>0) refresh_search() @@ -126,7 +152,7 @@ function move_right() refresh_changes() end -function place_pack(panel, packinfo, callback) +function place_pack(panel, packinfo, callback, position_func) if packinfo.error then callback = nil end @@ -136,6 +162,7 @@ function place_pack(panel, packinfo, callback) packinfo.id_verbose = packinfo.id end packinfo.callback = callback + packinfo.position_func = position_func or function () end panel:add(gui.template("pack", packinfo)) if not callback then document["pack_"..packinfo.id].enabled = false @@ -191,22 +218,6 @@ function refresh() end local packinfos = pack.get_info(packids) - for i,id in ipairs(packs_installed) do - local packinfo = packinfos[id] - packinfo.index = i - callback = not table.has(base_packs, id) and string.format('move_pack("%s")', id) or nil - packinfo.error = check_dependencies(packinfo) - place_pack(packs_cur, packinfo, callback) - end - - for i,id in ipairs(packs_available) do - local packinfo = packinfos[id] - packinfo.index = i - callback = string.format('move_pack("%s")', id) - packinfo.error = check_dependencies(packinfo) - place_pack(packs_add, packinfo, callback) - end - for _,id in ipairs(base_packs) do local packinfo = pack.get_info(id) packs_info[id] = {packinfo.id, packinfo.title} @@ -226,6 +237,22 @@ function refresh() if #packs_excluded == 0 then packs_excluded = table.copy(packs_available) end if #packs_included == 0 then packs_included = table.copy(packs_installed) end + for i,id in ipairs(packs_installed) do + local packinfo = packinfos[id] + packinfo.index = i + callback = not table.has(base_packs, id) and string.format('move_pack("%s")', id) or nil + packinfo.error = check_dependencies(packinfo) + place_pack(packs_cur, packinfo, callback, string.format('reposition_func("%s")[1],reposition_func("%s")[2]', packinfo.id, packinfo.id)) + end + + for i,id in ipairs(packs_available) do + local packinfo = packinfos[id] + packinfo.index = i + callback = string.format('move_pack("%s")', id) + packinfo.error = check_dependencies(packinfo) + place_pack(packs_add, packinfo, callback, string.format('reposition_func("%s")[1],reposition_func("%s")[2]', packinfo.id, packinfo.id)) + end + apply_movements(packs_cur, packs_add) refresh_changes() end diff --git a/res/layouts/templates/pack.xml b/res/layouts/templates/pack.xml index 5eb2f74c..55a52d61 100644 --- a/res/layouts/templates/pack.xml +++ b/res/layouts/templates/pack.xml @@ -1,4 +1,4 @@ - + diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index 5da1b347..c993af6a 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -113,6 +113,18 @@ function table.shuffle(t) return t end +function table.merge(t1, t2) + for i, v in pairs(t2) do + if type(i) == "number" then + t1[#t1 + 1] = v + elseif t1[i] == nil then + t1[i] = v + end + end + + return t1 +end + ---------------------------------------------- local pattern_escape_replacements = { From fde61647bd1010d77ed4206e0b669a41113f0ba4 Mon Sep 17 00:00:00 2001 From: Xertis Date: Sun, 19 Jan 2025 12:59:26 +0300 Subject: [PATCH 07/24] minor update --- res/layouts/pages/content.xml.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/res/layouts/pages/content.xml.lua b/res/layouts/pages/content.xml.lua index 29b8ff68..5a8f9342 100644 --- a/res/layouts/pages/content.xml.lua +++ b/res/layouts/pages/content.xml.lua @@ -36,7 +36,7 @@ end function reposition_func(pack) local INTERVAL = 2 - local STEP = -1 + local STEP = 1 local SIZE = 80 local tbl = packs_excluded @@ -45,20 +45,20 @@ function reposition_func(pack) end local indx = table.index(tbl, pack) - 1 - local pos = {0, (SIZE + INTERVAL) * indx - STEP} + local pos = {0, (SIZE + INTERVAL) * indx + STEP} return pos end -function refresh_search() +function refresh_search() local search_text = document.search_textbox.text:lower() local new_included = table.copy(packs_included) local new_excluded = table.copy(packs_excluded) - local function score(package_name) - if package_name:lower():find(search_text) then + local function score(pack_name) + if pack_name:lower():find(search_text) then return 1 end return 0 From 3c0b64bc52badcaddf33f372ed3d50b0c9a660ed Mon Sep 17 00:00:00 2001 From: Xertis Date: Sun, 19 Jan 2025 13:42:16 +0300 Subject: [PATCH 08/24] minor update --- res/layouts/pages/content.xml.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/res/layouts/pages/content.xml.lua b/res/layouts/pages/content.xml.lua index 5a8f9342..d588bd8e 100644 --- a/res/layouts/pages/content.xml.lua +++ b/res/layouts/pages/content.xml.lua @@ -47,7 +47,7 @@ function reposition_func(pack) local indx = table.index(tbl, pack) - 1 local pos = {0, (SIZE + INTERVAL) * indx + STEP} - return pos + return pos[1], pos[2] end @@ -242,7 +242,7 @@ function refresh() packinfo.index = i callback = not table.has(base_packs, id) and string.format('move_pack("%s")', id) or nil packinfo.error = check_dependencies(packinfo) - place_pack(packs_cur, packinfo, callback, string.format('reposition_func("%s")[1],reposition_func("%s")[2]', packinfo.id, packinfo.id)) + place_pack(packs_cur, packinfo, callback, string.format('reposition_func("%s")', packinfo.id)) end for i,id in ipairs(packs_available) do @@ -250,7 +250,7 @@ function refresh() packinfo.index = i callback = string.format('move_pack("%s")', id) packinfo.error = check_dependencies(packinfo) - place_pack(packs_add, packinfo, callback, string.format('reposition_func("%s")[1],reposition_func("%s")[2]', packinfo.id, packinfo.id)) + place_pack(packs_add, packinfo, callback, string.format('reposition_func("%s")', packinfo.id)) end apply_movements(packs_cur, packs_add) From d2ceb991b9c776f493a720b7936e39477df7f570 Mon Sep 17 00:00:00 2001 From: Xertis <118364459+Xertis@users.noreply.github.com> Date: Sat, 25 Jan 2025 22:09:09 +0300 Subject: [PATCH 09/24] bug fix --- res/layouts/templates/pack.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/layouts/templates/pack.xml b/res/layouts/templates/pack.xml index 55a52d61..3b24aa96 100644 --- a/res/layouts/templates/pack.xml +++ b/res/layouts/templates/pack.xml @@ -1,4 +1,4 @@ - + From 2c29e77f82b6484885b918d795d59c2f76993982 Mon Sep 17 00:00:00 2001 From: Xertis <118364459+Xertis@users.noreply.github.com> Date: Tue, 28 Jan 2025 20:23:41 +0300 Subject: [PATCH 10/24] refresh bug fix --- res/layouts/pages/content.xml.lua | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/res/layouts/pages/content.xml.lua b/res/layouts/pages/content.xml.lua index d588bd8e..50fbcc65 100644 --- a/res/layouts/pages/content.xml.lua +++ b/res/layouts/pages/content.xml.lua @@ -34,17 +34,22 @@ function apply() end end -function reposition_func(pack) +function reposition_func(_pack) local INTERVAL = 2 local STEP = 1 local SIZE = 80 - local tbl = packs_excluded - if table.has(packs_included, pack) then + local tbl = nil + if table.has(packs_included, _pack) then tbl = packs_included + elseif table.has(packs_excluded, _pack) then + tbl = packs_excluded + else + tbl = packs_excluded + table.insert(packs_excluded, pack.get_info(_pack)) end - local indx = table.index(tbl, pack) - 1 + local indx = table.index(tbl, _pack) - 1 local pos = {0, (SIZE + INTERVAL) * indx + STEP} return pos[1], pos[2] From e205182da6c9572f7f5d6c27eff8e840dcc8fe2e Mon Sep 17 00:00:00 2001 From: Xertis <118364459+Xertis@users.noreply.github.com> Date: Tue, 28 Jan 2025 20:39:00 +0300 Subject: [PATCH 11/24] bug fix refresh bug fix --- res/layouts/pages/content.xml.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/res/layouts/pages/content.xml.lua b/res/layouts/pages/content.xml.lua index 50fbcc65..6e013902 100644 --- a/res/layouts/pages/content.xml.lua +++ b/res/layouts/pages/content.xml.lua @@ -46,7 +46,9 @@ function reposition_func(_pack) tbl = packs_excluded else tbl = packs_excluded - table.insert(packs_excluded, pack.get_info(_pack)) + local packinfo = pack.get_info(_pack) + packinfo[packinfo.id] = {packinfo.id, packinfo.title} + table.insert(packs_excluded, packinfo.id) end local indx = table.index(tbl, _pack) - 1 From efdd0226f60a2bc3fdf1aef3a844c6cd8ddc4871 Mon Sep 17 00:00:00 2001 From: Xertis <118364459+Xertis@users.noreply.github.com> Date: Tue, 28 Jan 2025 23:59:24 +0300 Subject: [PATCH 12/24] improved menu stability --- res/layouts/pages/content.xml.lua | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/res/layouts/pages/content.xml.lua b/res/layouts/pages/content.xml.lua index 6e013902..3f1903f3 100644 --- a/res/layouts/pages/content.xml.lua +++ b/res/layouts/pages/content.xml.lua @@ -196,15 +196,30 @@ function check_dependencies(packinfo) return end +function check_deleted() + for i = 1, math.max(#packs_included, #packs_excluded) do + local pack = packs_included[i] + if pack and not table.has(packs_all, pack) then + table.remove(packs_included, i) + table.insert(rem_packs, pack) + end + + pack = packs_excluded[i] + if pack and not table.has(packs_all, pack) then + table.remove(packs_excluded, i) + table.insert(rem_packs, pack) + end + end +end + function refresh() packs_installed = pack.get_installed() packs_available = pack.get_available() base_packs = pack.get_base_packs() packs_all = {unpack(packs_installed)} required = {} - for i,k in ipairs(packs_available) do - table.insert(packs_all, k) - end + + table.merge(packs_all, packs_available) local packs_cur = document.packs_cur local packs_add = document.packs_add @@ -260,6 +275,7 @@ function refresh() place_pack(packs_add, packinfo, callback, string.format('reposition_func("%s")', packinfo.id)) end + check_deleted() apply_movements(packs_cur, packs_add) refresh_changes() end From 5607e0f039fa50c6ed2fc65af2866d60ac64bf8b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 26 Feb 2025 16:35:23 +0300 Subject: [PATCH 13/24] fix: entity:despawn in on_save not preventing entity save --- src/objects/Entities.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 09bb7dbb..4e9f8894 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -370,11 +370,14 @@ dv::value Entities::serialize(const Entity& entity) { dv::value Entities::serialize(const std::vector& entities) { auto list = dv::list(); for (auto& entity : entities) { - if (!entity.getDef().save.enabled) { + const EntityId& eid = entity.getID(); + if (!entity.getDef().save.enabled || eid.destroyFlag) { continue; } level.entities->onSave(entity); - list.add(level.entities->serialize(entity)); + if (!eid.destroyFlag) { + list.add(level.entities->serialize(entity)); + } } return list; } @@ -597,9 +600,9 @@ bool Entities::hasBlockingInside(AABB aabb) { std::vector Entities::getAllInside(AABB aabb) { std::vector collected; - auto view = registry.view(); - for (auto [entity, transform] : view.each()) { - if (aabb.contains(transform.pos)) { + auto view = registry.view(); + for (auto [entity, eid, transform] : view.each()) { + if (!eid.destroyFlag && aabb.contains(transform.pos)) { const auto& found = uids.find(entity); if (found == uids.end()) { continue; From 6e6800aef580d9d69a053fe3d0b7467b6f6ce120 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 28 Feb 2025 14:03:05 +0300 Subject: [PATCH 14/24] Revert "sort packs in PacksManager::assemble" This reverts commit 3fb16ab50eedf433c6dce25fb24f760808a0bd19. --- src/content/PacksManager.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/content/PacksManager.cpp b/src/content/PacksManager.cpp index 4eeaa5eb..49c3014c 100644 --- a/src/content/PacksManager.cpp +++ b/src/content/PacksManager.cpp @@ -2,7 +2,6 @@ #include #include -#include #include "util/listutil.hpp" @@ -125,9 +124,7 @@ std::vector PacksManager::assemble( std::queue queue; std::queue queue2; - std::sort(allNames.begin(), allNames.end()); - - for (auto& name : allNames) { + for (auto& name : names) { auto found = packs.find(name); if (found == packs.end()) { throw contentpack_error(name, io::path(), "pack not found"); From a203e406889bdf556eb0c6feb353d8ba30c1b4ff Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 1 Mar 2025 12:18:20 +0300 Subject: [PATCH 15/24] change AppImage user directory from ~/.voxeng to ~/.config/voxelcore --- dev/AppImageBuilder.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/AppImageBuilder.yml b/dev/AppImageBuilder.yml index f4ffe233..16fcf57a 100644 --- a/dev/AppImageBuilder.yml +++ b/dev/AppImageBuilder.yml @@ -7,7 +7,7 @@ AppDir: icon: VoxelCore version: latest exec: usr/bin/VoxelEngine - exec_args: --dir $HOME/.voxeng --res $APPDIR/usr/share/VoxelCore/res $@ + exec_args: --dir $HOME/.config/voxelcore --res $APPDIR/usr/share/VoxelCore/res $@ apt: arch: amd64 sources: From 0d9cd6fefce079309211c1a0125c3fb206ce2784 Mon Sep 17 00:00:00 2001 From: Xertis <118364459+Xertis@users.noreply.github.com> Date: Thu, 6 Mar 2025 15:54:40 +0300 Subject: [PATCH 16/24] table.filter bug fix --- res/scripts/stdmin.lua | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index 5ebf0176..ce592277 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -151,9 +151,27 @@ function table.map(t, func) end function table.filter(t, func) + + for i = #t, 1, -1 do + if not func(i, t[i]) then + table.remove(t, i) + end + end + + local size = #t + for i, v in pairs(t) do - if not func(i, v) then - t[i] = nil + local i_type = type(i) + if i_type == "number" then + if i < 1 or i > size then + if not func(i, v) then + t[i] = nil + end + end + else + if not func(i, v) then + t[i] = nil + end end end From ee3fbc68314865384d2a7d78a22221b5987979a4 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 7 Mar 2025 20:15:25 +0300 Subject: [PATCH 17/24] fix: incorrect canvas Y direction --- src/graphics/ui/elements/Canvas.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphics/ui/elements/Canvas.cpp b/src/graphics/ui/elements/Canvas.cpp index dd69ce22..be3fc145 100644 --- a/src/graphics/ui/elements/Canvas.cpp +++ b/src/graphics/ui/elements/Canvas.cpp @@ -16,5 +16,5 @@ void gui::Canvas::draw(const DrawContext& pctx, const Assets& assets) { auto batch = pctx.getBatch2D(); batch->texture(mTexture.get()); - batch->rect(pos.x, pos.y, size.x, size.y, 0, 0, 0, {}, false, true, col); + batch->rect(pos.x, pos.y, size.x, size.y, 0, 0, 0, {}, false, false, col); } From dca4f283ccb08fa4c4e0b3fb47c3a5b703b1d319 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 7 Mar 2025 20:15:59 +0300 Subject: [PATCH 18/24] add canvas:clear(...), canvas:line(...) --- .../lua/usertypes/lua_type_canvas.cpp | 147 +++++++++++++++--- 1 file changed, 129 insertions(+), 18 deletions(-) diff --git a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp index acba361d..ad79f0ca 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp @@ -17,6 +17,7 @@ union RGBA { struct { uint8_t r, g, b, a; }; + uint8_t arr[4]; uint32_t rgba; }; @@ -49,32 +50,140 @@ static int l_at(State* L) { return 0; } +static RGBA get_rgba(State* L, int first) { + RGBA rgba {}; + rgba.a = 255; + switch (gettop(L) - first) { + case 0: + rgba.rgba = static_cast(tointeger(L, first)); + break; + case 3: + rgba.a = static_cast(tointeger(L, first + 3)); + [[fallthrough]]; + case 2: + rgba.r = static_cast(tointeger(L, first)); + rgba.g = static_cast(tointeger(L, first + 1)); + rgba.b = static_cast(tointeger(L, first + 2)); + break; + } + return rgba; +} + static int l_set(State* L) { auto x = static_cast(tointeger(L, 2)); auto y = static_cast(tointeger(L, 3)); if (auto pixel = get_at(L, x, y)) { - switch (gettop(L)) { - case 4: - pixel->rgba = static_cast(tointeger(L, 4)); - return 1; - case 6: - pixel->r = static_cast(tointeger(L, 4)); - pixel->g = static_cast(tointeger(L, 5)); - pixel->b = static_cast(tointeger(L, 6)); - pixel->a = 255; - return 1; - case 7: - pixel->r = static_cast(tointeger(L, 4)); - pixel->g = static_cast(tointeger(L, 5)); - pixel->b = static_cast(tointeger(L, 6)); - pixel->a = static_cast(tointeger(L, 7)); - return 1; - default: - return 0; + *pixel = get_rgba(L, 4); + } + return 0; +} + +static int l_clear(State* L) { + RGBA rgba {}; + if (gettop(L) > 1) { + rgba = get_rgba(L, 2); + } + if (auto canvas = touserdata(L, 1)) { + auto& image = canvas->data(); + ubyte* data = image.getData(); + size_t pixels = image.getWidth() * image.getHeight(); + const size_t channels = 4; + for (size_t i = 0; i < pixels * channels; i++) { + data[i] = rgba.arr[i % channels]; } } + return 0; +} +bool clip_line(int& x1, int& y1, int& x2, int& y2, int width, int height) { + const int left = 0; + const int right = width; + const int bottom = 0; + const int top = height; + + int dx = x2 - x1; + int dy = y2 - y1; + + float t0 = 0.0f; + float t1 = 1.0f; + + auto clip = [](int p, int q, float& t0, float& t1) { + if (p == 0) { + return q >= 0; + } + float t = static_cast(q) / p; + if (p < 0) { + if (t > t1) return false; + if (t > t0) t0 = t; + } else { + if (t < t0) return false; + if (t < t1) t1 = t; + } + return true; + }; + + if (!clip(-dx, x1 - left, t0, t1)) return false; + if (!clip( dx, right - x1, t0, t1)) return false; + if (!clip(-dy, y1 - bottom, t0, t1)) return false; + if (!clip( dy, top - y1, t0, t1)) return false; + + if (t1 < 1.0f) { + x2 = x1 + static_cast(std::round(t1 * dx)); + y2 = y1 + static_cast(std::round(t1 * dy)); + } + if (t0 > 0.0f) { + x1 = x1 + static_cast(std::round(t0 * dx)); + y1 = y1 + static_cast(std::round(t0 * dy)); + } + return true; +} + +static int l_line(State* L) { + int x1 = tointeger(L, 2); + int y1 = tointeger(L, 3); + + int x2 = tointeger(L, 4); + int y2 = tointeger(L, 5); + + RGBA rgba = get_rgba(L, 6); + if (auto canvas = touserdata(L, 1)) { + auto& image = canvas->data(); + ubyte* data = image.getData(); + uint width = image.getWidth(); + uint height = image.getHeight(); + const uint channels = 4; + + if ((x1 < 0 || x1 >= width || x2 < 0 || x2 >= width || + y1 < 0 || y1 >= height || y2 < 0 || y2 >= height) && + !clip_line(x1, y1, x2, y2, width, height)) { + return 0; + } + + int dx = glm::abs(x2 - x1); + int dy = -glm::abs(y2 - y1); + int sx = x1 < x2 ? 1 : -1; + int sy = y1 < y2 ? 1 : -1; + int err = dx + dy; + + while (true) { + size_t pos = (y1 * width + x1) * channels; + for (int i = 0; i < channels; i++) { + data[pos + i] = rgba.arr[i]; + } + if (x1 == x2 && y1 == y2) break; + + int e2 = 2 * err; + if (e2 >= dy) { + err += dy; + x1 += sx; + } + if (e2 <= dx) { + err += dx; + y1 += sy; + } + } + } return 0; } @@ -88,6 +197,8 @@ static int l_update(State* L) { static std::unordered_map methods { {"at", lua::wrap}, {"set", lua::wrap}, + {"clear", lua::wrap}, + {"line", lua::wrap}, {"update", lua::wrap} }; From e8c6c9a7b3f1799462231bcf4c120f994a7914b6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 7 Mar 2025 20:31:50 +0300 Subject: [PATCH 19/24] update doc/*/scripting/ui.md --- doc/en/scripting/ui.md | 12 +++++++++--- doc/ru/scripting/ui.md | 12 +++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/doc/en/scripting/ui.md b/doc/en/scripting/ui.md index 441115c8..debbad4c 100644 --- a/doc/en/scripting/ui.md +++ b/doc/en/scripting/ui.md @@ -166,12 +166,18 @@ Properties: Methods: +Here, *color* can be specified in the following ways: +- rgba: int +- r: int, g: int, b: int +- r: int, g: int, b: int, a: int + | Method | Description | |----------------------------------------------------------|---------------------------------------------------------| | data:at(x: int, y: int) | returns an RGBA pixel at the given coordinates | -| data:set(x: int, y: int, rgba: int) | updates an RGBA pixel at the given coordinates | -| data:set(x: int, y: int, r: int, g: int, b: int) | updates an RGBA pixel at the given coordinates | -| data:set(x: int, y: int, r: int, g: int, b: int, a: int) | updates an RGBA pixel at the given coordinates | +| data:set(x: int, y: int, *color*) | updates an RGBA pixel at the given coordinates | +| data:line(x1: int, y1: int, x2: int, y2: int, *color*) | draws a line with the specified RGBA color | +| data:clear() | clears the canvas | +| data:clear(*color*) | fills the canvas with the specified RGBA color | | data:update() | applies changes to the canvas and uploads it to the GPU | diff --git a/doc/ru/scripting/ui.md b/doc/ru/scripting/ui.md index cca9790e..3422e972 100644 --- a/doc/ru/scripting/ui.md +++ b/doc/ru/scripting/ui.md @@ -167,12 +167,18 @@ document["worlds-panel"]:clear() Методы: +Здесь *цвет* может быть указан следующими способами: +- rgba: int +- r: int, g: int, b: int +- r: int, g: int, b: int, a: int + | Метод | Описание | |----------------------------------------------------------|-----------------------------------------------------| | data:at(x: int, y: int) | возвращает RGBA пиксель по указанным координатам | -| data:set(x: int, y: int, rgba: int) | изменяет RGBA пиксель по указанным координатам | -| data:set(x: int, y: int, r: int, g: int, b: int) | изменяет RGBA пиксель по указанным координатам | -| data:set(x: int, y: int, r: int, g: int, b: int, a: int) | изменяет RGBA пиксель по указанным координатам | +| data:set(x: int, y: int, *цвет*) | изменяет RGBA пиксель по указанным координатам | +| data:line(x1: int, y1: int, x2: int, y2: int, *цвет*) | рисует линию с указанным RGBA цветом | +| data:clear() | очищает холст | +| data:clear(*цвет*) | заполняет холст указанным RGBA цветом | | data:update() | применяет изменения и загружает холст в видеопамять | ## Inventory (inventory) From 5bbba7f39d1da5a76c4ace96a7102d6f78d9fa8e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 8 Mar 2025 13:36:43 +0300 Subject: [PATCH 20/24] add ImageData.drawLine --- src/graphics/core/ImageData.cpp | 94 +++++++++++++++ src/graphics/core/ImageData.hpp | 7 ++ .../lua/usertypes/lua_type_canvas.cpp | 113 ++++-------------- 3 files changed, 121 insertions(+), 93 deletions(-) diff --git a/src/graphics/core/ImageData.cpp b/src/graphics/core/ImageData.cpp index d6617c57..b5e05d1c 100644 --- a/src/graphics/core/ImageData.cpp +++ b/src/graphics/core/ImageData.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include ImageData::ImageData(ImageFormat format, uint width, uint height) @@ -93,6 +94,99 @@ void ImageData::blit(const ImageData* image, int x, int y) { throw std::runtime_error("mismatching format"); } +static bool clip_line(int& x1, int& y1, int& x2, int& y2, int width, int height) { + const int left = 0; + const int right = width; + const int bottom = 0; + const int top = height; + + int dx = x2 - x1; + int dy = y2 - y1; + + float t0 = 0.0f; + float t1 = 1.0f; + + auto clip = [](int p, int q, float& t0, float& t1) { + if (p == 0) { + return q >= 0; + } + float t = static_cast(q) / p; + if (p < 0) { + if (t > t1) return false; + if (t > t0) t0 = t; + } else { + if (t < t0) return false; + if (t < t1) t1 = t; + } + return true; + }; + + if (!clip(-dx, x1 - left, t0, t1)) return false; + if (!clip( dx, right - x1, t0, t1)) return false; + if (!clip(-dy, y1 - bottom, t0, t1)) return false; + if (!clip( dy, top - y1, t0, t1)) return false; + + if (t1 < 1.0f) { + x2 = x1 + static_cast(std::round(t1 * dx)); + y2 = y1 + static_cast(std::round(t1 * dy)); + } + if (t0 > 0.0f) { + x1 = x1 + static_cast(std::round(t0 * dx)); + y1 = y1 + static_cast(std::round(t0 * dy)); + } + return true; +} + +template +static void draw_line(ImageData& image, int x1, int y1, int x2, int y2, const glm::ivec4& color) { + ubyte* data = image.getData(); + uint width = image.getWidth(); + uint height = image.getHeight(); + + if ((x1 < 0 || x1 >= width || x2 < 0 || x2 >= width || + y1 < 0 || y1 >= height || y2 < 0 || y2 >= height) && + !clip_line(x1, y1, x2, y2, width, height)) { + return; + } + + int dx = std::abs(x2 - x1); + int dy = -std::abs(y2 - y1); + int sx = x1 < x2 ? 1 : -1; + int sy = y1 < y2 ? 1 : -1; + int err = dx + dy; + + while (true) { + size_t pos = (y1 * width + x1) * channels; + for (int i = 0; i < channels; i++) { + data[pos + i] = color[i]; + } + if (x1 == x2 && y1 == y2) break; + + int e2 = 2 * err; + if (e2 >= dy) { + err += dy; + x1 += sx; + } + if (e2 <= dx) { + err += dx; + y1 += sy; + } + } +} + +void ImageData::drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color) { + switch (format) { + case ImageFormat::rgb888: + draw_line<3>(*this, x1, y1, x2, y2, color); + break; + case ImageFormat::rgba8888: + draw_line<4>(*this, x1, y1, x2, y2, color); + break; + default: + break; + } +} + void ImageData::blitRGB_on_RGBA(const ImageData* image, int x, int y) { ubyte* source = image->getData(); uint srcwidth = image->getWidth(); diff --git a/src/graphics/core/ImageData.hpp b/src/graphics/core/ImageData.hpp index 328ff896..00e59f6a 100644 --- a/src/graphics/core/ImageData.hpp +++ b/src/graphics/core/ImageData.hpp @@ -2,6 +2,7 @@ #include "typedefs.hpp" +#include #include enum class ImageFormat { @@ -23,6 +24,7 @@ public: void flipX(); void flipY(); + void drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color); void blitRGB_on_RGBA(const ImageData* image, int x, int y); void blitMatchingFormat(const ImageData* image, int x, int y); void blit(const ImageData* image, int x, int y); @@ -44,6 +46,11 @@ public: uint getHeight() const { return height; } + + size_t getDataSize() const { + size_t channels = 3 + (format == ImageFormat::rgba8888); + return width * height * channels; + } }; std::unique_ptr add_atlas_margins(ImageData* image, int grid_size); diff --git a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp index ad79f0ca..f3950c23 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp @@ -80,65 +80,26 @@ static int l_set(State* L) { } static int l_clear(State* L) { - RGBA rgba {}; - if (gettop(L) > 1) { - rgba = get_rgba(L, 2); + auto canvas = touserdata(L, 1); + if (canvas == nullptr) { + throw std::runtime_error("used canvas.clear instead of canvas:clear"); } - if (auto canvas = touserdata(L, 1)) { - auto& image = canvas->data(); - ubyte* data = image.getData(); - size_t pixels = image.getWidth() * image.getHeight(); - const size_t channels = 4; - for (size_t i = 0; i < pixels * channels; i++) { - data[i] = rgba.arr[i % channels]; - } + auto& image = canvas->data(); + ubyte* data = image.getData(); + RGBA rgba {}; + if (gettop(L) == 1) { + std::fill(data, data + image.getDataSize(), 0); + return 0; + } + rgba = get_rgba(L, 2); + size_t pixels = image.getWidth() * image.getHeight(); + const size_t channels = 4; + for (size_t i = 0; i < pixels * channels; i++) { + data[i] = rgba.arr[i % channels]; } return 0; } -bool clip_line(int& x1, int& y1, int& x2, int& y2, int width, int height) { - const int left = 0; - const int right = width; - const int bottom = 0; - const int top = height; - - int dx = x2 - x1; - int dy = y2 - y1; - - float t0 = 0.0f; - float t1 = 1.0f; - - auto clip = [](int p, int q, float& t0, float& t1) { - if (p == 0) { - return q >= 0; - } - float t = static_cast(q) / p; - if (p < 0) { - if (t > t1) return false; - if (t > t0) t0 = t; - } else { - if (t < t0) return false; - if (t < t1) t1 = t; - } - return true; - }; - - if (!clip(-dx, x1 - left, t0, t1)) return false; - if (!clip( dx, right - x1, t0, t1)) return false; - if (!clip(-dy, y1 - bottom, t0, t1)) return false; - if (!clip( dy, top - y1, t0, t1)) return false; - - if (t1 < 1.0f) { - x2 = x1 + static_cast(std::round(t1 * dx)); - y2 = y1 + static_cast(std::round(t1 * dy)); - } - if (t0 > 0.0f) { - x1 = x1 + static_cast(std::round(t0 * dx)); - y1 = y1 + static_cast(std::round(t0 * dy)); - } - return true; -} - static int l_line(State* L) { int x1 = tointeger(L, 2); int y1 = tointeger(L, 3); @@ -149,40 +110,9 @@ static int l_line(State* L) { RGBA rgba = get_rgba(L, 6); if (auto canvas = touserdata(L, 1)) { auto& image = canvas->data(); - ubyte* data = image.getData(); - uint width = image.getWidth(); - uint height = image.getHeight(); - const uint channels = 4; - - if ((x1 < 0 || x1 >= width || x2 < 0 || x2 >= width || - y1 < 0 || y1 >= height || y2 < 0 || y2 >= height) && - !clip_line(x1, y1, x2, y2, width, height)) { - return 0; - } - - int dx = glm::abs(x2 - x1); - int dy = -glm::abs(y2 - y1); - int sx = x1 < x2 ? 1 : -1; - int sy = y1 < y2 ? 1 : -1; - int err = dx + dy; - - while (true) { - size_t pos = (y1 * width + x1) * channels; - for (int i = 0; i < channels; i++) { - data[pos + i] = rgba.arr[i]; - } - if (x1 == x2 && y1 == y2) break; - - int e2 = 2 * err; - if (e2 >= dy) { - err += dy; - x1 += sx; - } - if (e2 <= dx) { - err += dx; - y1 += sy; - } - } + image.drawLine( + x1, y1, x2, y2, glm::ivec4 {rgba.r, rgba.g, rgba.b, rgba.a} + ); } return 0; } @@ -197,10 +127,9 @@ static int l_update(State* L) { static std::unordered_map methods { {"at", lua::wrap}, {"set", lua::wrap}, - {"clear", lua::wrap}, {"line", lua::wrap}, - {"update", lua::wrap} -}; + {"clear", lua::wrap}, + {"update", lua::wrap}}; static int l_meta_index(State* L) { auto texture = touserdata(L, 1); @@ -237,9 +166,7 @@ static int l_meta_newindex(State* L) { if (isnumber(L, 2) && isnumber(L, 3)) { if (auto pixel = get_at(data, static_cast(tointeger(L, 2)))) { pixel->rgba = static_cast(tointeger(L, 3)); - return 1; } - return 1; } return 0; } From 2af55a022716d142a0aa4c349bd9d63efc7cd5d8 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 8 Mar 2025 14:02:57 +0300 Subject: [PATCH 21/24] add Canvas constructor & add canvas:blit --- src/graphics/core/Atlas.cpp | 2 +- src/graphics/core/ImageData.cpp | 32 ++++++------ src/graphics/core/ImageData.hpp | 7 +-- src/logic/scripting/lua/lua_custom_types.hpp | 6 ++- .../lua/usertypes/lua_type_canvas.cpp | 50 ++++++++++++++++--- 5 files changed, 71 insertions(+), 26 deletions(-) diff --git a/src/graphics/core/Atlas.cpp b/src/graphics/core/Atlas.cpp index 039db4d2..be205d1e 100644 --- a/src/graphics/core/Atlas.cpp +++ b/src/graphics/core/Atlas.cpp @@ -97,7 +97,7 @@ std::unique_ptr AtlasBuilder::build(uint extrusion, bool prepare, uint ma uint y = rect.y; uint w = rect.width; uint h = rect.height; - canvas->blit(entry.image.get(), rect.x, rect.y); + canvas->blit(*entry.image, rect.x, rect.y); for (uint j = 0; j < extrusion; j++) { canvas->extrude(x - j, y - j, w + j*2, h + j*2); } diff --git a/src/graphics/core/ImageData.cpp b/src/graphics/core/ImageData.cpp index b5e05d1c..a2b1d524 100644 --- a/src/graphics/core/ImageData.cpp +++ b/src/graphics/core/ImageData.cpp @@ -81,13 +81,13 @@ void ImageData::flipY() { } } -void ImageData::blit(const ImageData* image, int x, int y) { - if (format == image->format) { +void ImageData::blit(const ImageData& image, int x, int y) { + if (format == image.format) { blitMatchingFormat(image, x, y); return; } if (format == ImageFormat::rgba8888 && - image->format == ImageFormat::rgb888) { + image.format == ImageFormat::rgb888) { blitRGB_on_RGBA(image, x, y); return; } @@ -187,10 +187,10 @@ void ImageData::drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color } } -void ImageData::blitRGB_on_RGBA(const ImageData* image, int x, int y) { - ubyte* source = image->getData(); - uint srcwidth = image->getWidth(); - uint srcheight = image->getHeight(); +void ImageData::blitRGB_on_RGBA(const ImageData& image, int x, int y) { + ubyte* source = image.getData(); + uint srcwidth = image.getWidth(); + uint srcheight = image.getHeight(); for (uint srcy = std::max(0, -y); srcy < std::min(srcheight, height - y); @@ -210,7 +210,7 @@ void ImageData::blitRGB_on_RGBA(const ImageData* image, int x, int y) { } } -void ImageData::blitMatchingFormat(const ImageData* image, int x, int y) { +void ImageData::blitMatchingFormat(const ImageData& image, int x, int y) { uint comps; switch (format) { case ImageFormat::rgb888: comps = 3; break; @@ -218,20 +218,24 @@ void ImageData::blitMatchingFormat(const ImageData* image, int x, int y) { default: throw std::runtime_error("only unsigned byte formats supported"); } - ubyte* source = image->getData(); - uint srcwidth = image->getWidth(); - uint srcheight = image->getHeight(); + ubyte* source = image.getData(); + + const uint width = this->width; + const uint height = this->height; + const uint src_width = image.getWidth(); + const uint src_height = image.getHeight(); + ubyte* data = this->data.get(); for (uint srcy = std::max(0, -y); - srcy < std::min(srcheight, height - y); + srcy < std::min(src_height, height - y); srcy++) { for (uint srcx = std::max(0, -x); - srcx < std::min(srcwidth, width - x); + srcx < std::min(src_width, width - x); srcx++) { uint dstx = srcx + x; uint dsty = srcy + y; uint dstidx = (dsty * width + dstx) * comps; - uint srcidx = (srcy * srcwidth + srcx) * comps; + uint srcidx = (srcy * src_width + srcx) * comps; for (uint c = 0; c < comps; c++) { data[dstidx + c] = source[srcidx + c]; } diff --git a/src/graphics/core/ImageData.hpp b/src/graphics/core/ImageData.hpp index 00e59f6a..213a352c 100644 --- a/src/graphics/core/ImageData.hpp +++ b/src/graphics/core/ImageData.hpp @@ -15,6 +15,9 @@ class ImageData { uint width; uint height; std::unique_ptr data; + + void blitRGB_on_RGBA(const ImageData& image, int x, int y); + void blitMatchingFormat(const ImageData& image, int x, int y); public: ImageData(ImageFormat format, uint width, uint height); ImageData(ImageFormat format, uint width, uint height, std::unique_ptr data); @@ -25,9 +28,7 @@ public: void flipY(); void drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color); - void blitRGB_on_RGBA(const ImageData* image, int x, int y); - void blitMatchingFormat(const ImageData* image, int x, int y); - void blit(const ImageData* image, int x, int y); + void blit(const ImageData& image, int x, int y); void extrude(int x, int y, int w, int h); void fixAlphaColor(); diff --git a/src/logic/scripting/lua/lua_custom_types.hpp b/src/logic/scripting/lua/lua_custom_types.hpp index 145cc4d8..a844b1f1 100644 --- a/src/logic/scripting/lua/lua_custom_types.hpp +++ b/src/logic/scripting/lua/lua_custom_types.hpp @@ -114,10 +114,14 @@ namespace lua { return *mData; } + [[nodiscard]] bool hasTexture() const { + return mTexture != nullptr; + } + static int createMetatable(lua::State*); inline static std::string TYPENAME = "Canvas"; private: - std::shared_ptr mTexture; + std::shared_ptr mTexture; // nullable std::shared_ptr mData; }; static_assert(!std::is_abstract()); diff --git a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp index f3950c23..f041c1ff 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp @@ -79,12 +79,18 @@ static int l_set(State* L) { return 0; } -static int l_clear(State* L) { - auto canvas = touserdata(L, 1); - if (canvas == nullptr) { - throw std::runtime_error("used canvas.clear instead of canvas:clear"); +static LuaCanvas& require_canvas(State* L, int idx) { + if (const auto canvas = touserdata(L, idx)) { + return *canvas; } - auto& image = canvas->data(); + throw std::runtime_error( + "canvas expected as argument #" + std::to_string(idx) + ); +} + +static int l_clear(State* L) { + auto& canvas = require_canvas(L, 1); + auto& image = canvas.data(); ubyte* data = image.getData(); RGBA rgba {}; if (gettop(L) == 1) { @@ -117,9 +123,20 @@ static int l_line(State* L) { return 0; } +static int l_blit(State* L) { + auto& dst = require_canvas(L, 1); + auto& src = require_canvas(L, 2); + int dst_x = tointeger(L, 3); + int dst_y = tointeger(L, 4); + dst.data().blit(src.data(), dst_x, dst_y); + return 0; +} + static int l_update(State* L) { if (auto canvas = touserdata(L, 1)) { - canvas->texture().reload(canvas->data()); + if (canvas->hasTexture()) { + canvas->texture().reload(canvas->data()); + } } return 0; } @@ -128,8 +145,10 @@ static std::unordered_map methods { {"at", lua::wrap}, {"set", lua::wrap}, {"line", lua::wrap}, + {"blit", lua::wrap}, {"clear", lua::wrap}, - {"update", lua::wrap}}; + {"update", lua::wrap}, +}; static int l_meta_index(State* L) { auto texture = touserdata(L, 1); @@ -171,11 +190,28 @@ static int l_meta_newindex(State* L) { return 0; } +static int l_meta_meta_call(lua::State* L) { + auto size = glm::ivec2(tovec2(L, 2)); + if (size.x <= 0 || size.y <= 0) { + throw std::runtime_error("size must be positive"); + } + return newuserdata( + L, + nullptr, + std::make_shared(ImageFormat::rgba8888, size.x, size.y) + ); +} + int LuaCanvas::createMetatable(State* L) { createtable(L, 0, 3); pushcfunction(L, lua::wrap); setfield(L, "__index"); pushcfunction(L, lua::wrap); setfield(L, "__newindex"); + + createtable(L, 0, 1); + pushcfunction(L, lua::wrap); + setfield(L, "__call"); + setmetatable(L); return 1; } From 22d49c1c65fca50841e1fd9e4a6df3331199bb17 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 8 Mar 2025 14:08:44 +0300 Subject: [PATCH 22/24] update doc/*/scripting/ui.md --- doc/en/scripting/ui.md | 1 + doc/ru/scripting/ui.md | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/en/scripting/ui.md b/doc/en/scripting/ui.md index debbad4c..0df1ed72 100644 --- a/doc/en/scripting/ui.md +++ b/doc/en/scripting/ui.md @@ -176,6 +176,7 @@ Here, *color* can be specified in the following ways: | data:at(x: int, y: int) | returns an RGBA pixel at the given coordinates | | data:set(x: int, y: int, *color*) | updates an RGBA pixel at the given coordinates | | data:line(x1: int, y1: int, x2: int, y2: int, *color*) | draws a line with the specified RGBA color | +| data:blit(src: Canvas, dst_x: int, dst_y: int) | draws the src canvas at the specified coordinates | | data:clear() | clears the canvas | | data:clear(*color*) | fills the canvas with the specified RGBA color | | data:update() | applies changes to the canvas and uploads it to the GPU | diff --git a/doc/ru/scripting/ui.md b/doc/ru/scripting/ui.md index 3422e972..f3a3c44f 100644 --- a/doc/ru/scripting/ui.md +++ b/doc/ru/scripting/ui.md @@ -177,6 +177,7 @@ document["worlds-panel"]:clear() | data:at(x: int, y: int) | возвращает RGBA пиксель по указанным координатам | | data:set(x: int, y: int, *цвет*) | изменяет RGBA пиксель по указанным координатам | | data:line(x1: int, y1: int, x2: int, y2: int, *цвет*) | рисует линию с указанным RGBA цветом | +| data:blit(src: Canvas, dst_x: int, dst_y: int) | рисует src-холст на указанных координатах | | data:clear() | очищает холст | | data:clear(*цвет*) | заполняет холст указанным RGBA цветом | | data:update() | применяет изменения и загружает холст в видеопамять | From 6078a8802e8dd00149865b592e104a31636ca636 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 8 Mar 2025 14:55:17 +0300 Subject: [PATCH 23/24] add canvas:set_data --- doc/en/scripting/ui.md | 1 + doc/ru/scripting/ui.md | 19 +++++++++--------- src/graphics/ui/elements/Canvas.hpp | 2 +- .../lua/usertypes/lua_type_canvas.cpp | 20 +++++++++++++++++++ 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/doc/en/scripting/ui.md b/doc/en/scripting/ui.md index 0df1ed72..4cac91b7 100644 --- a/doc/en/scripting/ui.md +++ b/doc/en/scripting/ui.md @@ -180,6 +180,7 @@ Here, *color* can be specified in the following ways: | data:clear() | clears the canvas | | data:clear(*color*) | fills the canvas with the specified RGBA color | | data:update() | applies changes to the canvas and uploads it to the GPU | +| data:set_data(data: table) | replaces pixel data (width * height * 4 numbers) | ## Inventory diff --git a/doc/ru/scripting/ui.md b/doc/ru/scripting/ui.md index f3a3c44f..137ff57b 100644 --- a/doc/ru/scripting/ui.md +++ b/doc/ru/scripting/ui.md @@ -172,15 +172,16 @@ document["worlds-panel"]:clear() - r: int, g: int, b: int - r: int, g: int, b: int, a: int -| Метод | Описание | -|----------------------------------------------------------|-----------------------------------------------------| -| data:at(x: int, y: int) | возвращает RGBA пиксель по указанным координатам | -| data:set(x: int, y: int, *цвет*) | изменяет RGBA пиксель по указанным координатам | -| data:line(x1: int, y1: int, x2: int, y2: int, *цвет*) | рисует линию с указанным RGBA цветом | -| data:blit(src: Canvas, dst_x: int, dst_y: int) | рисует src-холст на указанных координатах | -| data:clear() | очищает холст | -| data:clear(*цвет*) | заполняет холст указанным RGBA цветом | -| data:update() | применяет изменения и загружает холст в видеопамять | +| Метод | Описание | +|----------------------------------------------------------|------------------------------------------------------| +| data:at(x: int, y: int) | возвращает RGBA пиксель по указанным координатам | +| data:set(x: int, y: int, *цвет*) | изменяет RGBA пиксель по указанным координатам | +| data:line(x1: int, y1: int, x2: int, y2: int, *цвет*) | рисует линию с указанным RGBA цветом | +| data:blit(src: Canvas, dst_x: int, dst_y: int) | рисует src-холст на указанных координатах | +| data:clear() | очищает холст | +| data:clear(*цвет*) | заполняет холст указанным RGBA цветом | +| data:update() | применяет изменения и загружает холст в видеопамять | +| data:set_data(data: table) | заменяет данные пикселей (ширина * высота * 4 чисел) | ## Inventory (inventory) diff --git a/src/graphics/ui/elements/Canvas.hpp b/src/graphics/ui/elements/Canvas.hpp index ae1f02b0..adfb5e2a 100644 --- a/src/graphics/ui/elements/Canvas.hpp +++ b/src/graphics/ui/elements/Canvas.hpp @@ -26,4 +26,4 @@ namespace gui { std::shared_ptr<::Texture> mTexture; std::shared_ptr mData; }; -} \ No newline at end of file +} diff --git a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp index f041c1ff..944f62a2 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp @@ -132,6 +132,25 @@ static int l_blit(State* L) { return 0; } +static int l_set_data(State* L) { + auto& canvas = require_canvas(L, 1); + auto& image = canvas.data(); + auto data = image.getData(); + int len = objlen(L, 2); + if (len < image.getDataSize()) { + throw std::runtime_error( + "data size mismatch expected " + + std::to_string(image.getDataSize()) + ", got " + std::to_string(len) + ); + } + for (size_t i = 0; i < len; i++) { + rawgeti(L, i + 1, 2); + data[i] = tointeger(L, -1); + pop(L); + } + return 0; +} + static int l_update(State* L) { if (auto canvas = touserdata(L, 1)) { if (canvas->hasTexture()) { @@ -148,6 +167,7 @@ static std::unordered_map methods { {"blit", lua::wrap}, {"clear", lua::wrap}, {"update", lua::wrap}, + {"set_data", lua::wrap}, }; static int l_meta_index(State* L) { From e48216da4016532b096794548f49ab97ef337a99 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 8 Mar 2025 17:33:56 +0300 Subject: [PATCH 24/24] experimental optimize canvas:set_data using ffi --- res/scripts/stdmin.lua | 28 +++++++++++++++++++ src/logic/scripting/lua/lua_engine.cpp | 19 +++++++------ src/logic/scripting/lua/lua_util.cpp | 8 ++++-- src/logic/scripting/lua/lua_wrapper.hpp | 6 +++- .../lua/usertypes/lua_type_canvas.cpp | 11 +++++++- src/logic/scripting/scripting.cpp | 2 +- 6 files changed, 61 insertions(+), 13 deletions(-) diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index ce592277..e2e4d3c7 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -1,3 +1,31 @@ +local _ffi = ffi +ffi = nil + +-- Lua has no parallelizm, also _set_data does not call any lua functions so +-- may be reused one global ffi buffer per lua_State +local canvas_ffi_buffer +local canvas_ffi_buffer_size = 0 + +function __vc_Canvas_set_data(self, data) + if type(data) == "cdata" then + self:_set_data(tostring(_ffi.cast("uintptr_t", data))) + end + local width = self.width + local height = self.height + + local size = width * height * 4 + if size > canvas_ffi_buffer_size then + canvas_ffi_buffer = _ffi.new( + string.format("unsigned char[%s]", size) + ) + canvas_ffi_buffer_size = size + end + for i=0, size - 1 do + canvas_ffi_buffer[i] = data[i + 1] + end + self:_set_data(tostring(_ffi.cast("uintptr_t", canvas_ffi_buffer))) +end + -- Check if given table is an array function is_array(x) if #x > 0 then diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 251f789b..09c0f424 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -88,14 +88,17 @@ static void create_libs(State* L, StateType stateType) { void lua::init_state(State* L, StateType stateType) { // Allowed standard libraries - pop(L, luaopen_base(L)); - pop(L, luaopen_math(L)); - pop(L, luaopen_string(L)); - pop(L, luaopen_table(L)); - pop(L, luaopen_debug(L)); - pop(L, luaopen_jit(L)); - pop(L, luaopen_bit(L)); - pop(L, luaopen_os(L)); + luaL_openlibs(L); + + if (getglobal(L, "require")) { + pushstring(L, "ffi"); + if (call_nothrow(L, 1, 1)) { + setglobal(L, "ffi"); + } + } + pushnil(L); + setglobal(L, "io"); + const char* removed_os[] { "execute", "exit", "remove", "rename", "setlocale", "tmpname", nullptr}; remove_lib_funcs(L, "os", removed_os); diff --git a/src/logic/scripting/lua/lua_util.cpp b/src/logic/scripting/lua/lua_util.cpp index d740165b..8cd77fb1 100644 --- a/src/logic/scripting/lua/lua_util.cpp +++ b/src/logic/scripting/lua/lua_util.cpp @@ -163,20 +163,23 @@ int lua::call(State* L, int argc, int nresults) { int handler_pos = gettop(L) - argc; pushcfunction(L, l_error_handler); insert(L, handler_pos); + int top = gettop(L); if (lua_pcall(L, argc, nresults, handler_pos)) { std::string log = tostring(L, -1); pop(L); remove(L, handler_pos); throw luaerror(log); } + int added = gettop(L) - (top - argc - 1); remove(L, handler_pos); - return nresults == -1 ? 1 : nresults; + return added; } int lua::call_nothrow(State* L, int argc, int nresults) { int handler_pos = gettop(L) - argc; pushcfunction(L, l_error_handler); insert(L, handler_pos); + int top = gettop(L); if (lua_pcall(L, argc, -1, handler_pos)) { auto errorstr = tostring(L, -1); if (errorstr) { @@ -188,8 +191,9 @@ int lua::call_nothrow(State* L, int argc, int nresults) { remove(L, handler_pos); return 0; } + int added = gettop(L) - (top - argc - 1); remove(L, handler_pos); - return 1; + return added; } void lua::dump_stack(State* L) { diff --git a/src/logic/scripting/lua/lua_wrapper.hpp b/src/logic/scripting/lua/lua_wrapper.hpp index 417314cd..f82f5bd4 100644 --- a/src/logic/scripting/lua/lua_wrapper.hpp +++ b/src/logic/scripting/lua/lua_wrapper.hpp @@ -25,7 +25,8 @@ namespace lua { if (n < 0) { abort(); } - if (lua_gettop(L) < n) { + int top = lua_gettop(L); + if (top < n) { abort(); } #endif @@ -63,6 +64,9 @@ namespace lua { inline void rawseti(lua::State* L, int n, int idx = -2) { lua_rawseti(L, idx, n); } + inline void rawset(lua::State* L, int idx = -3) { + lua_rawset(L, idx); + } inline int createtable(lua::State* L, int narr, int nrec) { lua_createtable(L, narr, nrec); diff --git a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp index 944f62a2..a4292f56 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp @@ -136,6 +136,12 @@ static int l_set_data(State* L) { auto& canvas = require_canvas(L, 1); auto& image = canvas.data(); auto data = image.getData(); + + if (lua::isstring(L, 2)) { + auto ptr = reinterpret_cast(std::stoull(lua::tostring(L, 2))); + std::memcpy(data, ptr, image.getDataSize()); + return 0; + } int len = objlen(L, 2); if (len < image.getDataSize()) { throw std::runtime_error( @@ -167,7 +173,7 @@ static std::unordered_map methods { {"blit", lua::wrap}, {"clear", lua::wrap}, {"update", lua::wrap}, - {"set_data", lua::wrap}, + {"_set_data", lua::wrap}, }; static int l_meta_index(State* L) { @@ -189,6 +195,9 @@ static int l_meta_index(State* L) { if (!strcmp(name, "height")) { return pushinteger(L, data.getHeight()); } + if (!strcmp(name, "set_data")) { + return getglobal(L, "__vc_Canvas_set_data"); + } if (auto func = methods.find(tostring(L, 2)); func != methods.end()) { return pushcfunction(L, func->second); } diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 92ed66a2..66606776 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -120,7 +120,7 @@ std::unique_ptr scripting::start_coroutine( lua::loadbuffer(L, 0, source, script.name()); if (lua::call(L, 1)) { int id = lua::tointeger(L, -1); - lua::pop(L, 2); + lua::pop(L, 1); return std::make_unique(L, id); } lua::pop(L);