Added in-game mesh management and new priv to limit deletions to trusted players

master
entuland 2018-06-08 17:22:56 +02:00
parent 293b2788fd
commit bbab7a8694
16 changed files with 246 additions and 40 deletions

View File

@ -1,4 +1,5 @@
# Woolen Meshes (wesh)
An in-game mesh creator for Minetest
Developed and tested on Minetest 0.4.16 - try in other versions at your own risk :)
@ -11,11 +12,11 @@ WIP MOD forum thread: https://forum.minetest.net/viewtopic.php?f=9&t=20115
W = any wool block
I = inner ingredient (see list below)
WWW
WIW
WWW
wesh:canvas02 (steel ingot)
wesh:canvas04 (copper ingot)
wesh:canvas08 (tin ingot)
@ -33,13 +34,14 @@ This has been added to the mod just to let everybody (in particular the devs, if
# How to use
Place down a Canvas block, you'll see that it extends beyond its node space marking a cube (available in sizes 2, 4, 8, 16 and 32)
The following screenshots show the old texture of the canvas (the one and only 16x16x16) now replaced by a new texture marked 16 just like the ones you can here below:
The following screenshots shows a sample of how the canvases look when placed in the world (you can read the size on the top face), held in hand and in the quickbar (last slot):
![Canvas sizes](/screenshots/canvas-sizes.png)
So here is the example using the canvas with size 16 marking its capture space:
Here is the example using the canvas with size 16 marking its capture space:
![Empty canvas](/screenshots/canvas-empty.png)
@ -47,20 +49,24 @@ In this space you can build anything you like by using colored wool blocks or mo
![Building inside the canvas](/screenshots/canvas-build.png)
Once you're done with your build, go to the Canvas block and right click it: you'll be asked to provide a name for your mesh (you can type any text in there, with uppercases and any symbol):
Once you're done with your build, go to the Canvas block and right click it: you'll be asked to provide a name for your mesh (you can type any text in there, with uppercases and any symbol).
Here you can also decide whether or not to generate a backup matrix and you can specify what variants you want your mesh to be available in.
Backup matrices are additional files that record your build's colors. The mod doesn't use them as of now, and even if it did, it would only allow you to rebuild your creation using wool blocks. These files can be safely omitted if you're not worried about rebuilding your creations (that is, if you don't dismantle them or if you don't care about recapturing them).
This capture interface also gives you access to the `Manage Meshes` and the `Giveme Meshes` interfaces, which will be covered later on in this documentation:
![Request for name](/screenshots/prompt-name.png)
When you confirm such name (you can cancel it by hitting the ESC key) you'll likely get a confirmation like this:
When you confirm the name for your capture (you can cancel it by hitting the ESC key) you'll get a confirmation in the chat:
![Save confirmation](/screenshots/save-confirm.png)
If you confirm the name by hitting ENTER you may not be presented with the above confirmation. It will appear in the chat as well just in case.
Upon saving a few temporary files will be created in the `/mod_storage/wesh` subfolder in your world's folder:
Upon saving a few temporary files will be created in the `/mod_storage/wesh_temp_obj_files` subfolder in your world's folder:
- the `.obj` file will contain a model with your build scaled down to fit exactly one block
- the `.obj.dat` file will contain the original name you have chosen for your mesh, along with some other data (read the section about using custom textures below)
- the `.obj.matrix.dat` file will contain a serialized version of your build, that may eventually get used to rebuild / reimport it in the game allowing you to alter it (right now you can't import them, so make sure you don't dismantle your build if you want to alter and capture it again)
- if you have selected the `Generate backup matrix`, you'll also find a `.obj.matrix.dat` file which will contain a serialized version of your build, that may eventually get used to rebuild / reimport it in the game allowing you to alter it (as mentioned above, right now you can't import them and it only records your build's colors, so make sure you don't dismantle your build if you want to alter and capture it again)
The above files are saved there only temporarily because mods don't have writing permission in their own folder while the world is running. In order to use your new meshes in the game you need to restart the world.
@ -68,33 +74,74 @@ During world startup the mod will move all the temporary files to the `/models`
You can't have two meshes with the same name (during the saving process the mod checks both the temporary meshes that haven't been loaded yet and those that have been already moved to the mod's folder).
If you want to delete a mesh you need to delete its files from either the world's folder (if you haven't restarted it since you captured the mesh you want to delete) or from the mod's folder (if such mesh has been already moved by a world reload).
By default, four versions of each mesh will be available (which you can toggle in the interface for each capture):
By default, four versions of each mesh will be available:
- plain versions: they use flat colors averaged from the colors of each wool block, with a bordered variant
![Plain version](/screenshots/version-plain.png)
![Plain bordered compare](/screenshots/plain-bordered-compare.png)
- wool versions: they will use the actual textures used by the wool blocks, with a bordered variant
![Wool version](/screenshots/version-wool.png)
![Wool bordered compare](/screenshots/wool-bordered-compare.png)
Sample of natural terrain capture:
![Non wool capture](/screenshots/non-wool-capture.png)
Collision boxes will be built automatically depending on the extent of your mesh:
![Auto collision box](/screenshots/auto-collision-box.png)
Up to 8 collision boxes will be created according to the mesh geometry allowing you to create stairs, slabs, frames, carpets and so forth, collision boxes will be merged into larger ones when fitting.
Up to 8 collision boxes will be created according to the mesh geometry allowing you to create stairs, slabs, frames, carpets and so forth, collision boxes will be merged into larger ones when fitting:
Such new blocks can't be crafted (I plan to make sort of a crafting station where you put some material and chose the model you want to craft), so you either need to give them to yourself or to find them in the Creative inventory. All such meshes show up if you filter for either `wesh` or `mesh`.
![Auto collision box 2](/screenshots/auto-collision-box-2.png)
Such new blocks can't be crafted but you can obtain as many as you want by clicking on the "Giveme Meshes" button of the capture interface, which will show you something like this (remember that you need to restart the world for new meshes to appear there):
![Giveme mesh](/screenshots/giveme-mesh.png)
The above names can be used with the `/give` and `/giveme` commands as well
If you're playing in creative mode all such meshes, including all canvases, show up if you filter for either `wesh` or `mesh`:
![Creative search](/screenshots/creative-search.png)
Looking at the filename (or knowing how the name gets converted) you can also work out the actual nodename to be used in your `/give` or `/giveme` chat command, for example:
- chosen name: `Test One!`
- resulting filename: `mesh_test_one.obj`
- resulting nodename: `wesh:mesh_test_one_VERSION` where `VERSION` will actually be something like `wool` or `plainborder` or whatever other variant has been enabled (see the following section for details about this)
# Privileges
Three separate privileges are available:
- `wesh_capture` limits the ability to create new meshes
- `wesh_place` limits the ability to place created meshes in the world
- `wesh_delete` limits the ability to delete meshes from disk
All of those privileges are granted to `singleplayer` by default.
Since canvases can be crafted and since the canvas interface allows players to get meshes for free, creative mode isn't necessary in order to use this mod.
# Managing Meshes
Temporary meshes (the ones captured in the current playing session, waiting to be moved to the mod's folder) can be deleted right away from "Manage meshes" interface: *there will be NO confirmation when deleting temporary captures!*
![Delete temporary now](/screenshots/delete-temporary-now.png)
Meshes that have already been moved to the mod's folder can't be deleted right away and need to be marked for deletion:
![Mark for deletion](/screenshots/mark-for-deletion.png)
Pending deletions can be canceled:
![Cancel pending deletion](/screenshots/cancel-pending-deletion.png)
## Deletions when playing on multiple worlds
When meshes get marked for deletion that information will go into the mod's storage _associated to that specific world_ - this means that in order for deletions to happen you need to exit the world and enter _the same world again_.
Those deletions will not be performed until you enter _that_ world again.
All meshes will be finally stored in the mod's folder - this means that _all_ worlds will end up sharing the _same_ meshes. If you delete any mesh in a world it will disappear for all worlds.
# Specifying custom properties
In the `.obj.dat` file of each mesh you'll find something like this:
@ -111,9 +158,15 @@ In the `.obj.dat` file of each mesh you'll find something like this:
(please consider that the number `16` here above indicates the size of the texture, it has nothing to do with the size of the canvas you use to capture your build)
The variants used in each `.obj.dat` file depend on the ones you select in the interface at capture time.
Default variants are stored in the file [default.nodevariants.lua](/default.nodevariants.lua) which gets copied over to `nodevariants.lua` when starting up the mod if no such file exists.
In order to add a new variant simply add a line with your texture name and make sure you save such texture file in the `/textures` folder of the mod. You can also remove the lines you're not interested in and the mod will not generate those variants. You can do this operation either on the `nodevariants.lua` file (it will affect all new captures) or in the `.obj.dat` file associated to each mesh (will affect only that mesh).
Those variants will be the ones shown in the capture interface.
In order to add a new variant simply add a line with your texture name and make sure you save such texture file in the `/textures` folder of the mod. You can also remove the lines you're not interested in and the mod will not generate those variants.
You can do the above operation either on the `nodevariants.lua` file (it will affect all new captures) or in the `.obj.dat` file associated to each mesh (will affect only that mesh).
For example, here we remove all but the `plain` version and add a custom one:
@ -125,12 +178,14 @@ For example, here we remove all but the `plain` version and add a custom one:
},
}
The above doesn't depend on variants available in `nodevariants.lua` - as long as you're using a different key name and an existing texture file you'll be fine.
Have a look at `wool-72.png` to see where each color goes, or use the included [textures-72.xcf](/textures/textures-72.xcf) file (GIMP format) which has layers for adding the borders as well.
You can as well override any property you would normally pass to node_register(), such as `walkable`, `groups`, `collision_box`, `selection_box` and so forth. The only property that doesn't get really overridden but just _mangled_ according to the variants is the `description` one. You shouldn't even be overriding `tiles` cause they will be built according to the `variants` property (property which is specific to this mod's `.obj.dat` files).
A couple considerations:
- the bottom-right transparent area never gets used
- the bottom-right transparent area is never used
- the used texture for each face will actually be a bit smaller (in the `wool-72.png` file the squares are 18 pixels in side, but the texture will only use a 16x16 square inside of it)
- you're not forced to use any particular size for your texture as long as it's square (I guess, let me know if you find any problems)

176
init.lua
View File

@ -1,6 +1,7 @@
wesh = {
name = "wesh",
temp_foldername = "wesh_temp_obj_files",
modpath = minetest.get_modpath(minetest.get_current_modname()),
vt_size = 72,
player_canvas = {},
@ -17,8 +18,15 @@ minetest.register_privilege("wesh_place", {
give_to_singleplayer = true,
})
minetest.register_privilege("wesh_delete", {
description = "Can delete captured meshes",
give_to_singleplayer = true,
})
local smartfs = dofile(wesh.modpath .. "/smartfs.lua")
local storage = dofile(wesh.modpath .. "/storage.lua")
wesh.forms.capture = smartfs.create("wesh.forms.capture", function(state)
state:size(6, 6)
@ -29,19 +37,23 @@ wesh.forms.capture = smartfs.create("wesh.forms.capture", function(state)
local capture_button = state:button(4, 0.2, 2, 1, "capture", "Capture")
capture_button:onClick(wesh.mesh_capture_confirmed)
-- local delete_button = state:button(4, 3.2, 2, 0, "delete", "Delete\nMeshes")
-- delete_button:onClick(function(_, state)
-- minetest.after(0, function(playername)
-- wesh.forms.delete_meshes:show(playername)
-- end, state.player)
-- end)
-- delete_button:setClose(true)
local delete_button = state:button(4, 3.2, 2, 0, "delete", "Manage\nMeshes")
delete_button:onClick(function(_, state)
if not minetest.get_player_privs(state.player).wesh_delete then
wesh.notify(state.player, "Insufficient privileges to manage meshes")
return
end
minetest.after(0, function()
wesh.forms.delete_meshes:show(state.player)
end)
end)
delete_button:setClose(true)
local give_button = state:button(4, 4.2, 2, 0, "give", "Giveme\nMeshes")
give_button:onClick(function(_, state)
minetest.after(0, function(playername)
wesh.forms.giveme_meshes:show(playername)
end, state.player)
minetest.after(0, function()
wesh.forms.giveme_meshes:show(state.player)
end)
end)
give_button:setClose(true)
@ -61,34 +73,118 @@ wesh.forms.capture = smartfs.create("wesh.forms.capture", function(state)
end
end)
-- wesh.forms.delete_meshes = smartfs.create("wesh.forms.delete_meshes", function(state)
function wesh.get_all_obj_files()
local stored_obj_files = wesh.filter_non_obj(wesh.get_stored_files())
local temp_obj_files = wesh.filter_non_obj(wesh.get_temp_files())
local marked_objs = wesh.retrieve_marked_objs()
local result = {}
-- end)
for _, obj_filename in pairs(stored_obj_files) do
table.insert(result, {
filename = obj_filename,
type = marked_objs[obj_filename] and "pending deletion" or "stored",
})
end
for _, obj_filename in pairs(temp_obj_files) do
table.insert(result, {
filename = obj_filename,
type = "temporary",
})
end
return result
end
wesh.forms.delete_meshes = smartfs.create("wesh.forms.delete_meshes", function(state)
state:size(8, 8)
local all_obj_files_list = state:listbox(0.5, 0.5, 7, 5.5, "all_obj_files_list")
local label = state:label(0.5, 6.3, "label", "No OBJ selected")
local action_button = state:button(0.5, 7.2, 5, 1, "action_button", "[disabled]")
local done_button = state:button(6, 7.2, 2, 1, "done", "Done")
done_button:setClose(true)
local obj_files = nil
local function update_button(obj)
if not obj or not obj.type then
action_button:setText("[disabled]")
elseif obj.type == "stored" then
action_button:setText("Mark for deletion")
elseif obj.type == "pending deletion" then
action_button:setText("Cancel pending deletion")
elseif obj.type == "temporary" then
action_button:setText("Delete selected temporary NOW!")
end
end
local function fill_list()
obj_files = wesh.get_all_obj_files()
all_obj_files_list:clearItems()
for index, obj in ipairs(obj_files) do
local item = obj.filename .. (obj.type ~= "" and (" (" .. obj.type .. ")") or "")
all_obj_files_list:addItem(item)
end
all_obj_files_list:setSelected("")
label:setText("No OBJ selected")
end
all_obj_files_list:onClick(function(self, state)
local index = self:getSelected()
if index then
label:setText("Selected:\n" .. self:getItem(index))
else
label:setText("No OBJ selected")
end
update_button(obj_files[index])
end)
action_button:onClick(function(_, state)
local index = all_obj_files_list:getSelected()
local obj = obj_files[index]
if not obj or not obj.type then
return
elseif obj.type == "stored" then
wesh.mark_obj_for_deletion(obj.filename)
elseif obj.type == "pending deletion" then
wesh.unmark_obj_for_deletion(obj.filename)
elseif obj.type == "temporary" then
wesh.delete_temp_obj(obj.filename)
end
fill_list()
update_button()
end)
fill_list()
end)
wesh.forms.giveme_meshes = smartfs.create("wesh.forms.giveme_meshes", function(state)
state:size(6, 6)
state:size(8, 8)
local stored_obj_files = wesh.filter_non_obj(wesh.get_stored_files())
local stored_list = state:listbox(0.5, 0.5, 5, 4, "stored_list")
local stored_variants = state:listbox(0.5, 0.5, 7, 6.5, "stored_variants")
for _, obj_filename in pairs(stored_obj_files) do
local data = wesh.get_obj_filedata(obj_filename)
if not data.variants then break end
for variant, _ in pairs(data.variants) do
stored_list:addItem(wesh.create_nodename(obj_filename, variant))
stored_variants:addItem(wesh.create_nodename(obj_filename, variant))
end
end
stored_list:onDoubleClick(wesh.give_mesh_callback)
stored_variants:onDoubleClick(wesh.give_mesh_callback)
local give_button = state:button(0.5, 5.2, 3, 1, "give", "Giveme selected")
local give_button = state:button(0.5, 7.2, 3, 1, "give", "Giveme selected")
give_button:onClick(wesh.give_mesh_callback)
local done_button = state:button(4, 5.2, 2, 1, "done", "Done")
local done_button = state:button(4, 7.2, 2, 1, "done", "Done")
done_button:setClose(true)
end)
function wesh.give_mesh_callback(_, state)
local nodename = state:get("stored_list"):getSelectedItem()
local nodename = state:get("stored_variants"):getSelectedItem()
if not nodename then return end
local player_inv = minetest.get_player_by_name(state.player):get_inventory()
player_inv:add_item("main", {name = nodename, count = 1})
wesh.notify(state.player, nodename .. " added to inventory")
@ -99,7 +195,7 @@ end
-- ========================================================================
function wesh._init()
wesh.temp_path = minetest.get_worldpath() .. "/mod_storage/" .. wesh.name
wesh.temp_path = minetest.get_worldpath() .. "/mod_storage/" .. wesh.temp_foldername
wesh.gen_prefix = "mesh_"
if not minetest.mkdir(wesh.temp_path) then
@ -109,6 +205,7 @@ function wesh._init()
wesh._init_colors()
wesh._init_geometry()
wesh._init_variants()
wesh._delete_marked_objs()
wesh._move_temp_files()
wesh._load_mod_meshes()
wesh._register_canvas_nodes()
@ -535,6 +632,45 @@ function wesh.filter_non_obj(filelist)
return list
end
function wesh.retrieve_marked_objs()
local marked_objs = minetest.deserialize(storage:get_string("marked_objs"))
return type(marked_objs) == "table" and marked_objs or {}
end
function wesh.store_marked_objs(marked_objs)
storage:set_string("marked_objs", minetest.serialize(marked_objs))
end
function wesh.mark_obj_for_deletion(obj_filename)
local marked_objs = wesh.retrieve_marked_objs()
marked_objs[obj_filename] = 1
wesh.store_marked_objs(marked_objs)
end
function wesh.unmark_obj_for_deletion(obj_filename)
local marked_objs = wesh.retrieve_marked_objs()
marked_objs[obj_filename] = nil
wesh.store_marked_objs(marked_objs)
end
function wesh.delete_temp_obj(obj_filename)
local full_obj_filename = wesh.temp_path .. "/" .. obj_filename
wesh._delete_obj_fileset(full_obj_filename)
end
function wesh._delete_obj_fileset(full_obj_filename)
os.remove(full_obj_filename)
os.remove(full_obj_filename .. ".dat")
os.remove(full_obj_filename .. ".matrix.dat")
end
function wesh._delete_marked_objs()
for obj_filename, _ in pairs(wesh.retrieve_marked_objs()) do
wesh._delete_obj_fileset(wesh.modpath .. "/models/" .. obj_filename)
end
storage:set_string("marked_objs", "")
end
function wesh._move_temp_files()
local meshes = wesh.get_temp_files()
for _, filename in ipairs(meshes) do

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 393 KiB

After

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 310 KiB

After

Width:  |  Height:  |  Size: 424 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 KiB

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 681 KiB

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 KiB

BIN
screenshots/giveme-mesh.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 KiB

After

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 KiB

After

Width:  |  Height:  |  Size: 263 KiB

15
storage.lua Normal file
View File

@ -0,0 +1,15 @@
local storage_folder = minetest.get_worldpath() .. "/mod_storage/"
local dirs = minetest.get_dir_list(storage_folder, true)
for _, dir in ipairs(dirs) do
if dir == wesh.name then
os.rename(storage_folder .. dir, storage_folder .. wesh.temp_foldername)
break
end
end
local storage = minetest.get_mod_storage()
return storage