diff options
Diffstat (limited to 'games')
32 files changed, 1118 insertions, 155 deletions
diff --git a/games/devtest/menu/background.png b/games/devtest/menu/background.png Binary files differindex 415bb3d14..89c45fcd5 100644 --- a/games/devtest/menu/background.png +++ b/games/devtest/menu/background.png diff --git a/games/devtest/mods/basetools/init.lua b/games/devtest/mods/basetools/init.lua index bd7480030..fd83b82eb 100644 --- a/games/devtest/mods/basetools/init.lua +++ b/games/devtest/mods/basetools/init.lua @@ -16,11 +16,11 @@ Tool types: Tool materials: -* Dirt: dig nodes of rating 3, one use only * Wood: dig nodes of rating 3 * Stone: dig nodes of rating 3 or 2 * Steel: dig nodes of rating 3, 2 or 1 * Mese: dig "everything" instantly +* n-Uses: can be used n times before breaking ]] -- The hand @@ -92,20 +92,6 @@ minetest.register_tool("basetools:pick_mese", { -- Pickaxes: Dig cracky -- --- This should break after only 1 use -minetest.register_tool("basetools:pick_dirt", { - description = "Dirt Pickaxe".."\n".. - "Digs cracky=3".."\n".. - "1 use only", - inventory_image = "basetools_dirtpick.png", - tool_capabilities = { - max_drop_level=0, - groupcaps={ - cracky={times={[3]=2.00}, uses=1, maxlevel=0} - }, - }, -}) - minetest.register_tool("basetools:pick_wood", { description = "Wooden Pickaxe".."\n".. "Digs cracky=3", @@ -348,3 +334,31 @@ minetest.register_tool("basetools:dagger_steel", { damage_groups = {fleshy=2}, } }) + +-- Test tool uses and punch_attack_uses +local uses = { 1, 2, 3, 5, 10, 50, 100, 1000, 10000, 65535 } +for i=1, #uses do + local u = uses[i] + local color = string.format("#FF00%02X", math.floor(((i-1)/#uses) * 255)) + minetest.register_tool("basetools:pick_uses_"..string.format("%05d", u), { + description = u.."-Uses Pickaxe".."\n".. + "Digs cracky=3", + inventory_image = "basetools_steelpick.png^[colorize:"..color..":127", + tool_capabilities = { + max_drop_level=0, + groupcaps={ + cracky={times={[3]=0.1, [2]=0.2, [1]=0.3}, uses=u, maxlevel=0} + }, + }, + }) + + minetest.register_tool("basetools:sword_uses_"..string.format("%05d", u), { + description = u.."-Uses Sword".."\n".. + "Damage: fleshy=1", + inventory_image = "basetools_woodsword.png^[colorize:"..color..":127", + tool_capabilities = { + damage_groups = {fleshy=1}, + punch_attack_uses = u, + }, + }) +end diff --git a/games/devtest/mods/basetools/textures/basetools_dirtpick.png b/games/devtest/mods/basetools/textures/basetools_dirtpick.png Binary files differdeleted file mode 100644 index 20a021d72..000000000 --- a/games/devtest/mods/basetools/textures/basetools_dirtpick.png +++ /dev/null diff --git a/games/devtest/mods/broken/init.lua b/games/devtest/mods/broken/init.lua new file mode 100644 index 000000000..04993ca16 --- /dev/null +++ b/games/devtest/mods/broken/init.lua @@ -0,0 +1,11 @@ +-- Register stuff with empty definitions to test if Minetest fallback options +-- for these things work properly. + +-- The itemstrings are deliberately kept descriptive to keep them easy to +-- recognize. + +minetest.register_node("broken:node_with_empty_definition", {}) +minetest.register_tool("broken:tool_with_empty_definition", {}) +minetest.register_craftitem("broken:craftitem_with_empty_definition", {}) + +minetest.register_entity("broken:entity_with_empty_definition", {}) diff --git a/games/devtest/mods/broken/mod.conf b/games/devtest/mods/broken/mod.conf new file mode 100644 index 000000000..a24378a34 --- /dev/null +++ b/games/devtest/mods/broken/mod.conf @@ -0,0 +1,2 @@ +name = broken +description = Register items and an entity with empty definitions to test fallback diff --git a/games/devtest/mods/experimental/commands.lua b/games/devtest/mods/experimental/commands.lua index 132b08b0b..e42ae954d 100644 --- a/games/devtest/mods/experimental/commands.lua +++ b/games/devtest/mods/experimental/commands.lua @@ -131,10 +131,11 @@ local function place_nodes(param) p2_max = 63 elseif def.paramtype2 == "leveled" then p2_max = 127 - elseif def.paramtype2 == "degrotate" and def.drawtype == "plantlike" then - p2_max = 179 + elseif def.paramtype2 == "degrotate" and (def.drawtype == "plantlike" or def.drawtype == "mesh") then + p2_max = 239 elseif def.paramtype2 == "colorfacedir" or def.paramtype2 == "colorwallmounted" or + def.paramtype2 == "colordegrotate" or def.paramtype2 == "color" then p2_max = 255 end @@ -143,7 +144,8 @@ local function place_nodes(param) -- Skip undefined param2 values if not ((def.paramtype2 == "meshoptions" and p2 % 8 > 4) or (def.paramtype2 == "colorwallmounted" and p2 % 8 > 5) or - (def.paramtype2 == "colorfacedir" and p2 % 32 > 23)) then + ((def.paramtype2 == "colorfacedir" or def.paramtype2 == "colordegrotate") + and p2 % 32 > 23)) then minetest.set_node(pos, { name = itemstring, param2 = p2 }) nodes_placed = nodes_placed + 1 @@ -215,5 +217,5 @@ minetest.register_chatcommand("test_place_nodes", { }) core.register_on_chatcommand(function(name, command, params) - minetest.log("caught command '"..command.."', issued by '"..name.."'. Parameters: '"..params.."'") + minetest.log("action", "caught command '"..command.."', issued by '"..name.."'. Parameters: '"..params.."'") end) diff --git a/games/devtest/mods/testformspec/formspec.lua b/games/devtest/mods/testformspec/formspec.lua index 2a2bdad60..c0db695b7 100644 --- a/games/devtest/mods/testformspec/formspec.lua +++ b/games/devtest/mods/testformspec/formspec.lua @@ -270,6 +270,16 @@ local scroll_fs = --style_type[label;border=;bgcolor=] --label[0.75,2;Reset] +local window = { + sizex = 12, + sizey = 13, + positionx = 0.5, + positiony = 0.5, + anchorx = 0.5, + anchory = 0.5, + paddingx = 0.05, + paddingy = 0.05 +} local pages = { -- Real Coordinates @@ -341,9 +351,28 @@ local pages = { "size[12,13]real_coordinates[true]" .. "container[0.5,1.5]" .. tabheaders_fs .. "container_end[]", - -- Inv + -- Inv "size[12,13]real_coordinates[true]" .. inv_style_fs, + -- Window + function() + return "formspec_version[3]" .. + string.format("size[%s,%s]position[%s,%s]anchor[%s,%s]padding[%s,%s]", + window.sizex, window.sizey, window.positionx, window.positiony, + window.anchorx, window.anchory, window.paddingx, window.paddingy) .. + string.format("field[0.5,0.5;2.5,0.5;sizex;X Size;%s]field[3.5,0.5;2.5,0.5;sizey;Y Size;%s]" .. + "field[0.5,1.5;2.5,0.5;positionx;X Position;%s]field[3.5,1.5;2.5,0.5;positiony;Y Position;%s]" .. + "field[0.5,2.5;2.5,0.5;anchorx;X Anchor;%s]field[3.5,2.5;2.5,0.5;anchory;Y Anchor;%s]" .. + "field[0.5,3.5;2.5,0.5;paddingx;X Padding;%s]field[3.5,3.5;2.5,0.5;paddingy;Y Padding;%s]" .. + "button[2,4.5;2.5,0.5;submit_window;Submit]", + window.sizex, window.sizey, window.positionx, window.positiony, + window.anchorx, window.anchory, window.paddingx, window.paddingy) .. + "field_close_on_enter[sizex;false]field_close_on_enter[sizey;false]" .. + "field_close_on_enter[positionx;false]field_close_on_enter[positiony;false]" .. + "field_close_on_enter[anchorx;false]field_close_on_enter[anchory;false]" .. + "field_close_on_enter[paddingx;false]field_close_on_enter[paddingy;false]" + end, + -- Animation [[ formspec_version[3] @@ -362,20 +391,55 @@ Number] animated_image[5.5,0.5;5,2;;testformspec_animation.png;4;100] animated_image[5.5,2.75;5,2;;testformspec_animation.jpg;4;100] + ]], + + -- Model + [[ + formspec_version[3] + size[12,13] style[m1;bgcolor=black] - model[0.5,6;4,4;m1;testformspec_character.b3d;testformspec_character.png] - model[5,6;4,4;m2;testformspec_chest.obj;default_chest_top.png,default_chest_top.png,default_chest_side.png,default_chest_side.png,default_chest_front.png,default_chest_inside.png;30,1;true;true] + style[m2;bgcolor=black] + label[5,1;all defaults] + label[5,5.1;angle = 0, 180 +continuous = false +mouse control = false +frame loop range = 0,30] + label[5,9.2;continuous = true +mouse control = true] + model[0.5,0.1;4,4;m1;testformspec_character.b3d;testformspec_character.png] + model[0.5,4.2;4,4;m2;testformspec_character.b3d;testformspec_character.png;0,180;false;false;0,30] + model[0.5,8.3;4,4;m3;testformspec_chest.obj;default_chest_top.png,default_chest_top.png,default_chest_side.png,default_chest_side.png,default_chest_front.png,default_chest_inside.png;30,1;true;true] ]], -- Scroll containers "formspec_version[3]size[12,13]" .. scroll_fs, + + -- Sound + [[ + formspec_version[3] + size[12,13] + style[snd_btn;sound=soundstuff_mono] + style[snd_ibtn;sound=soundstuff_mono] + style[snd_drp;sound=soundstuff_mono] + style[snd_chk;sound=soundstuff_mono] + style[snd_tab;sound=soundstuff_mono] + button[0.5,0.5;2,1;snd_btn;Sound] + image_button[0.5,2;2,1;testformspec_item.png;snd_ibtn;Sound] + dropdown[0.5,4;4;snd_drp;Sound,Two,Three;] + checkbox[0.5,5.5.5;snd_chk;Sound;] + tabheader[0.5,7;8,0.65;snd_tab;Soundtab1,Soundtab2,Soundtab3;1;false;false] + ]], } -local function show_test_formspec(pname, page_id) - page_id = page_id or 2 +local page_id = 2 +local function show_test_formspec(pname) + local page = pages[page_id] + if type(page) == "function" then + page = page() + end - local fs = pages[page_id] .. "tabheader[0,0;8,0.65;maintabs;Real Coord,Styles,Noclip,Hypertext,Tabs,Invs,Anim,ScrollC;" .. page_id .. ";false;false]" + local fs = page .. "tabheader[0,0;8,0.65;maintabs;Real Coord,Styles,Noclip,Hypertext,Tabs,Invs,Window,Anim,Model,ScrollC,Sound;" .. page_id .. ";false;false]" minetest.show_formspec(pname, "testformspec:formspec", fs) end @@ -385,9 +449,9 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) return false end - if fields.maintabs then - show_test_formspec(player:get_player_name(), tonumber(fields.maintabs)) + page_id = tonumber(fields.maintabs) + show_test_formspec(player:get_player_name()) return true end @@ -403,6 +467,26 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) minetest.chat_send_player(player:get_player_name(), "Hypertext action received: " .. tostring(fields.hypertext)) return true end + + for name, value in pairs(fields) do + if window[name] then + print(name, window[name]) + local num_val = tonumber(value) or 0 + + if name == "sizex" and num_val < 4 then + num_val = 6.5 + elseif name == "sizey" and num_val < 5 then + num_val = 5.5 + end + + window[name] = num_val + print(name, window[name]) + end + end + + if fields.submit_window then + show_test_formspec(player:get_player_name()) + end end) minetest.register_chatcommand("test_formspec", { diff --git a/games/devtest/mods/testhud/init.lua b/games/devtest/mods/testhud/init.lua new file mode 100644 index 000000000..2fa12fd71 --- /dev/null +++ b/games/devtest/mods/testhud/init.lua @@ -0,0 +1,81 @@ +local player_huds = {} + +local states = { + {0, "Normal font"}, + {1, "Bold font"}, + {2, "Italic font"}, + {3, "Bold and italic font"}, + {4, "Monospace font"}, + {5, "Bold and monospace font"}, + {7, "ZOMG all the font styles"}, +} + + +local default_def = { + hud_elem_type = "text", + position = {x = 0.5, y = 0.5}, + scale = {x = 2, y = 2}, + alignment = { x = 0, y = 0 }, +} + +local function add_hud(player, state) + local def = table.copy(default_def) + local statetbl = states[state] + def.offset = {x = 0, y = 32 * state} + def.style = statetbl[1] + def.text = statetbl[2] + return player:hud_add(def) +end + +minetest.register_on_leaveplayer(function(player) + player_huds[player:get_player_name()] = nil +end) + +local etime = 0 +local state = 0 + +minetest.register_globalstep(function(dtime) + etime = etime + dtime + if etime < 1 then + return + end + etime = 0 + for _, player in ipairs(minetest.get_connected_players()) do + local huds = player_huds[player:get_player_name()] + if huds then + for i, hud_id in ipairs(huds) do + local statetbl = states[(state + i) % #states + 1] + player:hud_change(hud_id, "style", statetbl[1]) + player:hud_change(hud_id, "text", statetbl[2]) + end + end + end + state = state + 1 +end) + +minetest.register_chatcommand("hudfonts", { + params = "", + description = "Show/Hide some text on the HUD with various font options", + func = function(name, param) + local player = minetest.get_player_by_name(name) + local param = tonumber(param) or 0 + param = math.min(math.max(param, 1), #states) + if player_huds[name] == nil then + player_huds[name] = {} + for i = 1, param do + table.insert(player_huds[name], add_hud(player, i)) + end + minetest.chat_send_player(name, ("%d HUD element(s) added."):format(param)) + else + local huds = player_huds[name] + if huds then + for _, hud_id in ipairs(huds) do + player:hud_remove(hud_id) + end + minetest.chat_send_player(name, "All HUD elements removed.") + end + player_huds[name] = nil + end + return true + end, +}) diff --git a/games/devtest/mods/testhud/mod.conf b/games/devtest/mods/testhud/mod.conf new file mode 100644 index 000000000..ed9f65c59 --- /dev/null +++ b/games/devtest/mods/testhud/mod.conf @@ -0,0 +1,2 @@ +name = testhud +description = For testing HUD functionality diff --git a/games/devtest/mods/testnodes/drawtypes.lua b/games/devtest/mods/testnodes/drawtypes.lua index ff970144d..208774f6c 100644 --- a/games/devtest/mods/testnodes/drawtypes.lua +++ b/games/devtest/mods/testnodes/drawtypes.lua @@ -15,22 +15,6 @@ testing this node easier and more convenient. local S = minetest.get_translator("testnodes") --- If set to true, will show an inventory image for nodes that have no inventory image as of Minetest 5.1.0. --- This is due to <https://github.com/minetest/minetest/issues/9209>. --- This is only added to make the items more visible to avoid confusion, but you will no longer see --- the default inventory images for these items. When you want to test the default inventory image of drawtypes, --- this should be turned off. --- TODO: Remove support for fallback inventory image as soon #9209 is fixed. -local SHOW_FALLBACK_IMAGE = minetest.settings:get_bool("testnodes_show_fallback_image", false) - -local fallback_image = function(img) - if SHOW_FALLBACK_IMAGE then - return img - else - return nil - end -end - -- A regular cube minetest.register_node("testnodes:normal", { description = S("Normal Drawtype Test Node"), @@ -145,20 +129,15 @@ minetest.register_node("testnodes:fencelike", { }) minetest.register_node("testnodes:torchlike", { - description = S("Torchlike Drawtype Test Node"), + description = S("Floor Torchlike Drawtype Test Node"), drawtype = "torchlike", paramtype = "light", - tiles = { - "testnodes_torchlike_floor.png", - "testnodes_torchlike_ceiling.png", - "testnodes_torchlike_wall.png", - }, + tiles = { "testnodes_torchlike_floor.png^[colorize:#FF0000:64" }, walkable = false, sunlight_propagates = true, groups = { dig_immediate = 3 }, - inventory_image = fallback_image("testnodes_torchlike_floor.png"), }) minetest.register_node("testnodes:torchlike_wallmounted", { @@ -176,12 +155,22 @@ minetest.register_node("testnodes:torchlike_wallmounted", { walkable = false, sunlight_propagates = true, groups = { dig_immediate = 3 }, - inventory_image = fallback_image("testnodes_torchlike_floor.png"), }) +minetest.register_node("testnodes:signlike", { + description = S("Floor Signlike Drawtype Test Node"), + drawtype = "signlike", + paramtype = "light", + tiles = { "testnodes_signlike.png^[colorize:#FF0000:64" }, -minetest.register_node("testnodes:signlike", { + walkable = false, + groups = { dig_immediate = 3 }, + sunlight_propagates = true, +}) + + +minetest.register_node("testnodes:signlike_wallmounted", { description = S("Wallmounted Signlike Drawtype Test Node"), drawtype = "signlike", paramtype = "light", @@ -192,7 +181,6 @@ minetest.register_node("testnodes:signlike", { walkable = false, groups = { dig_immediate = 3 }, sunlight_propagates = true, - inventory_image = fallback_image("testnodes_signlike.png"), }) minetest.register_node("testnodes:plantlike", { @@ -220,9 +208,46 @@ minetest.register_node("testnodes:plantlike_waving", { groups = { dig_immediate = 3 }, }) +minetest.register_node("testnodes:plantlike_wallmounted", { + description = S("Wallmounted Plantlike Drawtype Test Node"), + drawtype = "plantlike", + paramtype = "light", + paramtype2 = "wallmounted", + tiles = { "testnodes_plantlike_wallmounted.png" }, + leveled = 1, + + + walkable = false, + sunlight_propagates = true, + groups = { dig_immediate = 3 }, +}) -- param2 will rotate +local function rotate_on_rightclick(pos, node, clicker) + local def = minetest.registered_nodes[node.name] + local aux1 = clicker:get_player_control().aux1 + + local deg, deg_max + local color, color_mult = 0, 0 + if def.paramtype2 == "degrotate" then + deg = node.param2 + deg_max = 240 + elseif def.paramtype2 == "colordegrotate" then + -- MSB [3x color, 5x rotation] LSB + deg = node.param2 % 2^5 + deg_max = 24 + color_mult = 2^5 + color = math.floor(node.param2 / color_mult) + end + + deg = (deg + (aux1 and 10 or 1)) % deg_max + node.param2 = color * color_mult + deg + minetest.swap_node(pos, node) + minetest.chat_send_player(clicker:get_player_name(), + "Rotation is now " .. deg .. " / " .. deg_max) +end + minetest.register_node("testnodes:plantlike_degrotate", { description = S("Degrotate Plantlike Drawtype Test Node"), drawtype = "plantlike", @@ -230,12 +255,43 @@ minetest.register_node("testnodes:plantlike_degrotate", { paramtype2 = "degrotate", tiles = { "testnodes_plantlike_degrotate.png" }, - + on_rightclick = rotate_on_rightclick, + place_param2 = 7, walkable = false, sunlight_propagates = true, groups = { dig_immediate = 3 }, }) +minetest.register_node("testnodes:mesh_degrotate", { + description = S("Degrotate Mesh Drawtype Test Node"), + drawtype = "mesh", + paramtype = "light", + paramtype2 = "degrotate", + mesh = "testnodes_ocorner.obj", + tiles = { "testnodes_mesh_stripes2.png" }, + + on_rightclick = rotate_on_rightclick, + place_param2 = 10, -- 15° + sunlight_propagates = true, + groups = { dig_immediate = 3 }, +}) + +minetest.register_node("testnodes:mesh_colordegrotate", { + description = S("Color Degrotate Mesh Drawtype Test Node"), + drawtype = "mesh", + paramtype = "light", + paramtype2 = "colordegrotate", + palette = "testnodes_palette_facedir.png", + mesh = "testnodes_ocorner.obj", + tiles = { "testnodes_mesh_stripes3.png" }, + + on_rightclick = rotate_on_rightclick, + -- color index 1, 1 step (=15°) rotated + place_param2 = 1 * 2^5 + 1, + sunlight_propagates = true, + groups = { dig_immediate = 3 }, +}) + -- param2 will change height minetest.register_node("testnodes:plantlike_leveled", { description = S("Leveled Plantlike Drawtype Test Node"), @@ -277,6 +333,20 @@ minetest.register_node("testnodes:plantlike_rooted", { groups = { dig_immediate = 3 }, }) +minetest.register_node("testnodes:plantlike_rooted_wallmounted", { + description = S("Wallmounted Rooted Plantlike Drawtype Test Node"), + drawtype = "plantlike_rooted", + paramtype = "light", + paramtype2 = "wallmounted", + tiles = { + "testnodes_plantlike_rooted_base.png", + "testnodes_plantlike_rooted_base.png", + "testnodes_plantlike_rooted_base_side_wallmounted.png" }, + special_tiles = { "testnodes_plantlike_rooted_wallmounted.png" }, + + groups = { dig_immediate = 3 }, +}) + minetest.register_node("testnodes:plantlike_rooted_waving", { description = S("Waving Rooted Plantlike Drawtype Test Node"), drawtype = "plantlike_rooted", @@ -456,7 +526,6 @@ minetest.register_node("testnodes:airlike", { walkable = false, groups = { dig_immediate = 3 }, sunlight_propagates = true, - inventory_image = fallback_image("testnodes_airlike.png"), }) -- param2 changes liquid height @@ -546,10 +615,13 @@ scale("allfaces_optional_waving", scale("plantlike", S("Double-sized Plantlike Drawtype Test Node"), S("Half-sized Plantlike Drawtype Test Node")) +scale("plantlike_wallmounted", + S("Double-sized Wallmounted Plantlike Drawtype Test Node"), + S("Half-sized Wallmounted Plantlike Drawtype Test Node")) scale("torchlike_wallmounted", S("Double-sized Wallmounted Torchlike Drawtype Test Node"), S("Half-sized Wallmounted Torchlike Drawtype Test Node")) -scale("signlike", +scale("signlike_wallmounted", S("Double-sized Wallmounted Signlike Drawtype Test Node"), S("Half-sized Wallmounted Signlike Drawtype Test Node")) scale("firelike", diff --git a/games/devtest/mods/testnodes/liquids.lua b/games/devtest/mods/testnodes/liquids.lua index 3d2ea17f5..be33814af 100644 --- a/games/devtest/mods/testnodes/liquids.lua +++ b/games/devtest/mods/testnodes/liquids.lua @@ -40,9 +40,11 @@ for d=0, 8 do liquid_range = d, }) + if d <= 7 then + local mod = "^[colorize:#000000:127" minetest.register_node("testnodes:vliquid_"..d, { - description = "Test Liquid Source, Viscosity "..d, + description = "Test Liquid Source, Viscosity/Resistance "..d, drawtype = "liquid", tiles = {"testnodes_liquidsource_r"..d..".png"..mod}, special_tiles = { @@ -61,7 +63,7 @@ for d=0, 8 do }) minetest.register_node("testnodes:vliquid_flowing_"..d, { - description = "Flowing Test Liquid, Viscosity "..d, + description = "Flowing Test Liquid, Viscosity/Resistance "..d, drawtype = "flowingliquid", tiles = {"testnodes_liquidflowing_r"..d..".png"..mod}, special_tiles = { @@ -80,4 +82,53 @@ for d=0, 8 do liquid_viscosity = d, }) + mod = "^[colorize:#000000:192" + local v = 4 + minetest.register_node("testnodes:vrliquid_"..d, { + description = "Test Liquid Source, Viscosity "..v..", Resistance "..d, + drawtype = "liquid", + tiles = {"testnodes_liquidsource_r"..d..".png"..mod}, + special_tiles = { + {name = "testnodes_liquidsource_r"..d..".png"..mod, backface_culling = false}, + {name = "testnodes_liquidsource_r"..d..".png"..mod, backface_culling = true}, + }, + use_texture_alpha = "blend", + paramtype = "light", + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + is_ground_content = false, + liquidtype = "source", + liquid_alternative_flowing = "testnodes:vrliquid_flowing_"..d, + liquid_alternative_source = "testnodes:vrliquid_"..d, + liquid_viscosity = v, + move_resistance = d, + }) + + minetest.register_node("testnodes:vrliquid_flowing_"..d, { + description = "Flowing Test Liquid, Viscosity "..v..", Resistance "..d, + drawtype = "flowingliquid", + tiles = {"testnodes_liquidflowing_r"..d..".png"..mod}, + special_tiles = { + {name = "testnodes_liquidflowing_r"..d..".png"..mod, backface_culling = false}, + {name = "testnodes_liquidflowing_r"..d..".png"..mod, backface_culling = false}, + }, + use_texture_alpha = "blend", + paramtype = "light", + paramtype2 = "flowingliquid", + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + is_ground_content = false, + liquidtype = "flowing", + liquid_alternative_flowing = "testnodes:vrliquid_flowing_"..d, + liquid_alternative_source = "testnodes:vrliquid_"..d, + liquid_viscosity = v, + move_resistance = d, + }) + + end + end diff --git a/games/devtest/mods/testnodes/properties.lua b/games/devtest/mods/testnodes/properties.lua index a52cd1d6f..51f703d7c 100644 --- a/games/devtest/mods/testnodes/properties.lua +++ b/games/devtest/mods/testnodes/properties.lua @@ -152,6 +152,66 @@ minetest.register_node("testnodes:liquidflowing_nojump", { post_effect_color = {a = 70, r = 255, g = 0, b = 200}, }) +-- A liquid which doesn't have liquid movement physics (source variant) +minetest.register_node("testnodes:liquid_noswim", { + description = S("No-swim Liquid Source Node"), + liquidtype = "source", + liquid_range = 1, + liquid_viscosity = 0, + liquid_alternative_flowing = "testnodes:liquidflowing_noswim", + liquid_alternative_source = "testnodes:liquid_noswim", + liquid_renewable = false, + liquid_move_physics = false, + groups = {dig_immediate=3}, + walkable = false, + + drawtype = "liquid", + tiles = {"testnodes_liquidsource.png^[colorize:#FF00FF:127"}, + special_tiles = { + {name = "testnodes_liquidsource.png^[colorize:#FF00FF:127", backface_culling = false}, + {name = "testnodes_liquidsource.png^[colorize:#FF00FF:127", backface_culling = true}, + }, + use_texture_alpha = "blend", + paramtype = "light", + pointable = false, + liquids_pointable = true, + buildable_to = true, + is_ground_content = false, + post_effect_color = {a = 70, r = 255, g = 200, b = 200}, +}) + +-- A liquid which doen't have liquid movement physics (flowing variant) +minetest.register_node("testnodes:liquidflowing_noswim", { + description = S("No-swim Flowing Liquid Node"), + liquidtype = "flowing", + liquid_range = 1, + liquid_viscosity = 0, + liquid_alternative_flowing = "testnodes:liquidflowing_noswim", + liquid_alternative_source = "testnodes:liquid_noswim", + liquid_renewable = false, + liquid_move_physics = false, + groups = {dig_immediate=3}, + walkable = false, + + + drawtype = "flowingliquid", + tiles = {"testnodes_liquidflowing.png^[colorize:#FF00FF:127"}, + special_tiles = { + {name = "testnodes_liquidflowing.png^[colorize:#FF00FF:127", backface_culling = false}, + {name = "testnodes_liquidflowing.png^[colorize:#FF00FF:127", backface_culling = false}, + }, + use_texture_alpha = "blend", + paramtype = "light", + paramtype2 = "flowingliquid", + pointable = false, + liquids_pointable = true, + buildable_to = true, + is_ground_content = false, + post_effect_color = {a = 70, r = 255, g = 200, b = 200}, +}) + + + -- Nodes that modify fall damage (various damage modifiers) for i=-100, 100, 25 do if i ~= 0 then @@ -216,6 +276,54 @@ for i=1, 5 do }) end +-- Move resistance nodes (various resistance levels) +for r=0, 7 do + if r > 0 then + minetest.register_node("testnodes:move_resistance"..r, { + description = S("Move-resistant Node (@1)", r), + walkable = false, + move_resistance = r, + + drawtype = "glasslike", + paramtype = "light", + sunlight_propagates = true, + tiles = { "testnodes_move_resistance.png" }, + is_ground_content = false, + groups = { dig_immediate = 3 }, + color = { b = 0, g = 255, r = math.floor((r/7)*255), a = 255 }, + }) + end + + minetest.register_node("testnodes:move_resistance_liquidlike"..r, { + description = S("Move-resistant Node, liquidlike (@1)", r), + walkable = false, + move_resistance = r, + liquid_move_physics = true, + + drawtype = "glasslike", + paramtype = "light", + sunlight_propagates = true, + tiles = { "testnodes_move_resistance.png" }, + is_ground_content = false, + groups = { dig_immediate = 3 }, + color = { b = 255, g = 0, r = math.floor((r/7)*255), a = 255 }, + }) +end + +minetest.register_node("testnodes:climbable_move_resistance_4", { + description = S("Climbable Move-resistant Node (4)"), + walkable = false, + climbable = true, + move_resistance = 4, + + drawtype = "glasslike", + paramtype = "light", + sunlight_propagates = true, + tiles = {"testnodes_climbable_resistance_side.png"}, + is_ground_content = false, + groups = { dig_immediate = 3 }, +}) + -- By placing something on the node, the node itself will be replaced minetest.register_node("testnodes:buildable_to", { description = S("Replacable Node"), diff --git a/games/devtest/mods/testnodes/settingtypes.txt b/games/devtest/mods/testnodes/settingtypes.txt deleted file mode 100644 index 7f753bf3e..000000000 --- a/games/devtest/mods/testnodes/settingtypes.txt +++ /dev/null @@ -1,4 +0,0 @@ -# If set to true, will show an inventory image for nodes that have no inventory image as of Minetest 5.1.0. -# This is due to <https://github.com/minetest/minetest/issues/9209>. -# This is only added to make the items more visible to avoid confusion, but you will no longer see the default inventory images for these items. When you want to test the default inventory image of drawtypes, this should be turned off. -testnodes_show_fallback_image (Use fallback inventory images) bool false diff --git a/games/devtest/mods/testnodes/textures.lua b/games/devtest/mods/testnodes/textures.lua index f6e6a0c2a..dc581b0c7 100644 --- a/games/devtest/mods/testnodes/textures.lua +++ b/games/devtest/mods/testnodes/textures.lua @@ -65,3 +65,109 @@ for a=1,#alphas do }) end +-- Generate PNG textures + +local function mandelbrot(w, h, iterations) + local r = {} + for y=0, h-1 do + for x=0, w-1 do + local re = (x - w/2) * 4/w + local im = (y - h/2) * 4/h + -- zoom in on a nice view + re = re / 128 - 0.23 + im = im / 128 - 0.82 + + local px, py = 0, 0 + local i = 0 + while px*px + py*py <= 4 and i < iterations do + px, py = px*px - py*py + re, 2 * px * py + im + i = i + 1 + end + r[w*y+x+1] = i / iterations + end + end + return r +end + +local function gen_checkers(w, h, tile) + local r = {} + for y=0, h-1 do + for x=0, w-1 do + local hori = math.floor(x / tile) % 2 == 0 + local vert = math.floor(y / tile) % 2 == 0 + r[w*y+x+1] = hori ~= vert and 1 or 0 + end + end + return r +end + +local fractal = mandelbrot(512, 512, 128) +local frac_emb = mandelbrot(64, 64, 64) +local checker = gen_checkers(512, 512, 32) + +local floor = math.floor +local abs = math.abs +local data_emb = {} +local data_mb = {} +local data_ck = {} +for i=1, #frac_emb do + data_emb[i] = { + r = floor(abs(frac_emb[i] * 2 - 1) * 255), + g = floor(abs(1 - frac_emb[i]) * 255), + b = floor(frac_emb[i] * 255), + a = frac_emb[i] < 0.95 and 255 or 0, + } +end +for i=1, #fractal do + data_mb[i] = { + r = floor(fractal[i] * 255), + g = floor(abs(fractal[i] * 2 - 1) * 255), + b = floor(abs(1 - fractal[i]) * 255), + a = 255, + } + data_ck[i] = checker[i] > 0 and "#F80" or "#000" +end + +local textures_path = minetest.get_modpath( minetest.get_current_modname() ) .. "/textures/" +minetest.safe_file_write( + textures_path .. "testnodes_generated_mb.png", + minetest.encode_png(512,512,data_mb) +) +minetest.safe_file_write( + textures_path .. "testnodes_generated_ck.png", + minetest.encode_png(512,512,data_ck) +) + +minetest.register_node("testnodes:generated_png_mb", { + description = S("Generated Mandelbrot PNG Test Node"), + tiles = { "testnodes_generated_mb.png" }, + + groups = { dig_immediate = 2 }, +}) +minetest.register_node("testnodes:generated_png_ck", { + description = S("Generated Checker PNG Test Node"), + tiles = { "testnodes_generated_ck.png" }, + + groups = { dig_immediate = 2 }, +}) + +local png_emb = "[png:" .. minetest.encode_base64(minetest.encode_png(64,64,data_emb)) + +minetest.register_node("testnodes:generated_png_emb", { + description = S("Generated In-Band Mandelbrot PNG Test Node"), + tiles = { png_emb }, + + groups = { dig_immediate = 2 }, +}) +minetest.register_node("testnodes:generated_png_src_emb", { + description = S("Generated In-Band Source Blit Mandelbrot PNG Test Node"), + tiles = { png_emb .. "^testnodes_damage_neg.png" }, + + groups = { dig_immediate = 2 }, +}) +minetest.register_node("testnodes:generated_png_dst_emb", { + description = S("Generated In-Band Dest Blit Mandelbrot PNG Test Node"), + tiles = { "testnodes_generated_ck.png^" .. png_emb }, + + groups = { dig_immediate = 2 }, +}) diff --git a/games/devtest/mods/testnodes/textures/testnodes_climbable_resistance_side.png b/games/devtest/mods/testnodes/textures/testnodes_climbable_resistance_side.png Binary files differnew file mode 100644 index 000000000..be01583e6 --- /dev/null +++ b/games/devtest/mods/testnodes/textures/testnodes_climbable_resistance_side.png diff --git a/games/devtest/mods/testnodes/textures/testnodes_move_resistance.png b/games/devtest/mods/testnodes/textures/testnodes_move_resistance.png Binary files differnew file mode 100644 index 000000000..cac3944bf --- /dev/null +++ b/games/devtest/mods/testnodes/textures/testnodes_move_resistance.png diff --git a/games/devtest/mods/testnodes/textures/testnodes_plantlike_rooted_base_side_wallmounted.png b/games/devtest/mods/testnodes/textures/testnodes_plantlike_rooted_base_side_wallmounted.png Binary files differnew file mode 100644 index 000000000..b0be8d077 --- /dev/null +++ b/games/devtest/mods/testnodes/textures/testnodes_plantlike_rooted_base_side_wallmounted.png diff --git a/games/devtest/mods/testnodes/textures/testnodes_plantlike_rooted_wallmounted.png b/games/devtest/mods/testnodes/textures/testnodes_plantlike_rooted_wallmounted.png Binary files differnew file mode 100644 index 000000000..421466407 --- /dev/null +++ b/games/devtest/mods/testnodes/textures/testnodes_plantlike_rooted_wallmounted.png diff --git a/games/devtest/mods/testnodes/textures/testnodes_plantlike_wallmounted.png b/games/devtest/mods/testnodes/textures/testnodes_plantlike_wallmounted.png Binary files differnew file mode 100644 index 000000000..c89b29e30 --- /dev/null +++ b/games/devtest/mods/testnodes/textures/testnodes_plantlike_wallmounted.png diff --git a/games/devtest/mods/testtools/README.md b/games/devtest/mods/testtools/README.md index 9cfe29ea4..a1eb95ed7 100644 --- a/games/devtest/mods/testtools/README.md +++ b/games/devtest/mods/testtools/README.md @@ -33,6 +33,13 @@ Usage: * Punch node: Make it fall * Place: Try to teleport up to 2 units upwards, then make it fall +## Node Meta Editor +Edit and view metadata of nodes. + +Usage: + +* Punch: Open node metadata editor + ## Entity Rotator Changes the entity rotation (with `set_rotation`). @@ -90,6 +97,13 @@ Usage: * Place: Increase move distance * Sneak+place: Decrease move distance +## Children Getter +Shows list of objects that are attached to an object (aka "children") in chat. + +Usage: +* Punch object: Show children of punched object +* Punch air: Show your own children + ## Entity Visual Scaler Change visual size of entities @@ -97,3 +111,10 @@ Usage: * Punch entity to increase visual size * Sneak+punch entity to decrease visual size + +## Light Tool +Show light level of node. + +Usage: +* Punch: Show light info of node in front of the punched node's side +* Place: Show light info of the node that you touched diff --git a/games/devtest/mods/testtools/init.lua b/games/devtest/mods/testtools/init.lua index d578b264a..1041acdeb 100644 --- a/games/devtest/mods/testtools/init.lua +++ b/games/devtest/mods/testtools/init.lua @@ -3,8 +3,6 @@ local F = minetest.formspec_escape dofile(minetest.get_modpath("testtools") .. "/light.lua") --- TODO: Add a Node Metadata tool - minetest.register_tool("testtools:param2tool", { description = S("Param2 Tool") .."\n".. S("Modify param2 value of nodes") .."\n".. @@ -111,25 +109,6 @@ minetest.register_tool("testtools:node_setter", { end, }) -minetest.register_on_player_receive_fields(function(player, formname, fields) - if formname == "testtools:node_setter" then - local playername = player:get_player_name() - local witem = player:get_wielded_item() - if witem:get_name() == "testtools:node_setter" then - if fields.nodename and fields.param2 then - local param2 = tonumber(fields.param2) - if not param2 then - return - end - local meta = witem:get_meta() - meta:set_string("node", fields.nodename) - meta:set_int("node_param2", param2) - player:set_wielded_item(witem) - end - end - end -end) - minetest.register_tool("testtools:remover", { description = S("Remover") .."\n".. S("Punch: Remove pointed node or object"), @@ -634,6 +613,54 @@ minetest.register_tool("testtools:object_attacher", { end, }) +local function print_object(obj) + if obj:is_player() then + return "player '"..obj:get_player_name().."'" + elseif obj:get_luaentity() then + return "LuaEntity '"..obj:get_luaentity().name.."'" + else + return "object" + end +end + +minetest.register_tool("testtools:children_getter", { + description = S("Children Getter") .."\n".. + S("Shows list of objects attached to object") .."\n".. + S("Punch object to show its 'children'") .."\n".. + S("Punch air to show your own 'children'"), + inventory_image = "testtools_children_getter.png", + groups = { testtool = 1, disable_repair = 1 }, + on_use = function(itemstack, user, pointed_thing) + if user and user:is_player() then + local name = user:get_player_name() + local selected_object + local self_name + if pointed_thing.type == "object" then + selected_object = pointed_thing.ref + elseif pointed_thing.type == "nothing" then + selected_object = user + else + return + end + self_name = print_object(selected_object) + local children = selected_object:get_children() + local ret = "" + for c=1, #children do + ret = ret .. "* " .. print_object(children[c]) + if c < #children then + ret = ret .. "\n" + end + end + if ret == "" then + ret = S("No children attached to @1.", self_name) + else + ret = S("Children of @1:", self_name) .. "\n" .. ret + end + minetest.chat_send_player(user:get_player_name(), ret) + end + end, +}) + -- Use loadstring to parse param as a Lua value local function use_loadstring(param, player) -- For security reasons, require 'server' priv, just in case @@ -666,6 +693,68 @@ local function use_loadstring(param, player) return true, errOrResult end +-- Node Meta Editor +local node_meta_posses = {} +local node_meta_latest_keylist = {} + +local function show_node_meta_formspec(user, pos, key, value, keylist) + local textlist + if keylist then + textlist = "textlist[0,0.5;2.5,6.5;keylist;"..keylist.."]" + else + textlist = "" + end + minetest.show_formspec(user:get_player_name(), + "testtools:node_meta_editor", + "size[15,9]".. + "label[0,0;"..F(S("Current keys:")).."]".. + textlist.. + "field[3,0.5;12,1;key;"..F(S("Key"))..";"..F(key).."]".. + "textarea[3,1.5;12,6;value;"..F(S("Value (use empty value to delete key)"))..";"..F(value).."]".. + "button[0,8;3,1;get;"..F(S("Get value")).."]".. + "button[4,8;3,1;set;"..F(S("Set value")).."]".. + "label[0,7.2;"..F(S("pos = @1", minetest.pos_to_string(pos))).."]") +end + +local function get_node_meta_keylist(meta, playername, escaped) + local keys = {} + local ekeys = {} + local mtable = meta:to_table() + for k,_ in pairs(mtable.fields) do + table.insert(keys, k) + if escaped then + table.insert(ekeys, F(k)) + else + table.insert(ekeys, k) + end + end + if playername then + node_meta_latest_keylist[playername] = keys + end + return table.concat(ekeys, ",") +end + +minetest.register_tool("testtools:node_meta_editor", { + description = S("Node Meta Editor") .. "\n" .. + S("Place: Edit node metadata"), + inventory_image = "testtools_node_meta_editor.png", + groups = { testtool = 1, disable_repair = 1 }, + on_place = function(itemstack, user, pointed_thing) + if pointed_thing.type ~= "node" then + return itemstack + end + if not user:is_player() then + return itemstack + end + local pos = pointed_thing.under + node_meta_posses[user:get_player_name()] = pos + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + show_node_meta_formspec(user, pos, "", "", get_node_meta_keylist(meta, user:get_player_name(), true)) + return itemstack + end, +}) + minetest.register_on_player_receive_fields(function(player, formname, fields) if not (player and player:is_player()) then return @@ -728,5 +817,70 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) editor_formspec(name, selected_objects[name], prop_to_string(props[key]), sel) return end + elseif formname == "testtools:node_setter" then + local playername = player:get_player_name() + local witem = player:get_wielded_item() + if witem:get_name() == "testtools:node_setter" then + if fields.nodename and fields.param2 then + local param2 = tonumber(fields.param2) + if not param2 then + return + end + local meta = witem:get_meta() + meta:set_string("node", fields.nodename) + meta:set_int("node_param2", param2) + player:set_wielded_item(witem) + end + end + elseif formname == "testtools:node_meta_editor" then + local name = player:get_player_name() + local pos = node_meta_posses[name] + if fields.keylist then + local evnt = minetest.explode_textlist_event(fields.keylist) + if evnt.type == "DCL" or evnt.type == "CHG" then + local keylist_table = node_meta_latest_keylist[name] + if not pos then + return + end + local meta = minetest.get_meta(pos) + if not keylist_table then + return + end + if #keylist_table == 0 then + return + end + local key = keylist_table[evnt.index] + local value = meta:get_string(key) + local keylist_escaped = {} + for k,v in pairs(keylist_table) do + keylist_escaped[k] = F(v) + end + local keylist = table.concat(keylist_escaped, ",") + show_node_meta_formspec(player, pos, key, value, keylist) + return + end + elseif fields.key and fields.key ~= "" and fields.value then + if not pos then + return + end + local meta = minetest.get_meta(pos) + if fields.get then + local value = meta:get_string(fields.key) + show_node_meta_formspec(player, pos, fields.key, value, + get_node_meta_keylist(meta, name, true)) + return + elseif fields.set then + meta:set_string(fields.key, fields.value) + show_node_meta_formspec(player, pos, fields.key, fields.value, + get_node_meta_keylist(meta, name, true)) + return + end + end end end) + +minetest.register_on_leaveplayer(function(player) + local name = player:get_player_name() + node_meta_latest_keylist[name] = nil + node_meta_posses[name] = nil +end) diff --git a/games/devtest/mods/testtools/light.lua b/games/devtest/mods/testtools/light.lua index a9458ca6b..afca9a489 100644 --- a/games/devtest/mods/testtools/light.lua +++ b/games/devtest/mods/testtools/light.lua @@ -1,22 +1,37 @@ local S = minetest.get_translator("testtools") -minetest.register_tool("testtools:lighttool", { - description = S("Light tool"), - inventory_image = "testtools_lighttool.png", - groups = { testtool = 1, disable_repair = 1 }, - on_use = function(itemstack, user, pointed_thing) - local pos = pointed_thing.above +local function get_func(is_place) + return function(itemstack, user, pointed_thing) + local pos + if is_place then + pos = pointed_thing.under + else + pos = pointed_thing.above + end if pointed_thing.type ~= "node" or not pos then return end local node = minetest.get_node(pos) + local pstr = minetest.pos_to_string(pos) local time = minetest.get_timeofday() local sunlight = minetest.get_natural_light(pos) local artificial = minetest.get_artificial_light(node.param1) - local message = ("param1 0x%02x | time %.5f | sunlight %d | artificial %d") - :format(node.param1, time, sunlight, artificial) + local message = ("pos=%s | param1=0x%02x | " .. + "sunlight=%d | artificial=%d | timeofday=%.5f" ) + :format(pstr, node.param1, sunlight, artificial, time) minetest.chat_send_player(user:get_player_name(), message) end +end + +minetest.register_tool("testtools:lighttool", { + description = S("Light Tool") .. "\n" .. + S("Show light values of node") .. "\n" .. + S("Punch: Light of node above touched node") .. "\n" .. + S("Place: Light of touched node itself"), + inventory_image = "testtools_lighttool.png", + groups = { testtool = 1, disable_repair = 1 }, + on_use = get_func(false), + on_place = get_func(true), }) diff --git a/games/devtest/mods/testtools/textures/testtools_children_getter.png b/games/devtest/mods/testtools/textures/testtools_children_getter.png Binary files differnew file mode 100644 index 000000000..b7fa34025 --- /dev/null +++ b/games/devtest/mods/testtools/textures/testtools_children_getter.png diff --git a/games/devtest/mods/testtools/textures/testtools_node_meta_editor.png b/games/devtest/mods/testtools/textures/testtools_node_meta_editor.png Binary files differnew file mode 100644 index 000000000..89eafd65c --- /dev/null +++ b/games/devtest/mods/testtools/textures/testtools_node_meta_editor.png diff --git a/games/devtest/mods/unittests/crafting.lua b/games/devtest/mods/unittests/crafting.lua index eff13ce09..8c16d3efb 100644 --- a/games/devtest/mods/unittests/crafting.lua +++ b/games/devtest/mods/unittests/crafting.lua @@ -1,6 +1,7 @@ +dofile(core.get_modpath(core.get_current_modname()) .. "/crafting_prepare.lua") + -- Test minetest.clear_craft function local function test_clear_craft() - minetest.log("info", "[unittests] Testing minetest.clear_craft") -- Clearing by output minetest.register_craft({ output = "foo", @@ -22,11 +23,10 @@ local function test_clear_craft() minetest.clear_craft({recipe={{"foo", "bar"}}}) assert(minetest.get_all_craft_recipes("foo") == nil) end +unittests.register("test_clear_craft", test_clear_craft) -- Test minetest.get_craft_result function local function test_get_craft_result() - minetest.log("info", "[unittests] Testing minetest.get_craft_result") - -- normal local input = { method = "normal", @@ -107,14 +107,6 @@ local function test_get_craft_result() assert(output.item) minetest.log("info", "[unittests] unrepairable tool crafting output.item:to_table(): "..dump(output.item:to_table())) -- unrepairable tool must not yield any output - assert(output.item:get_name() == "") - + assert(output.item:is_empty()) end - -function unittests.test_crafting() - test_clear_craft() - test_get_craft_result() - minetest.log("action", "[unittests] Crafting tests passed!") - return true -end - +unittests.register("test_get_craft_result", test_get_craft_result) diff --git a/games/devtest/mods/unittests/init.lua b/games/devtest/mods/unittests/init.lua index 12c67f78b..0754d507f 100644 --- a/games/devtest/mods/unittests/init.lua +++ b/games/devtest/mods/unittests/init.lua @@ -1,18 +1,199 @@ unittests = {} +unittests.list = {} + +-- name: Name of the test +-- func: +-- for sync: function(player, pos), should error on failure +-- for async: function(callback, player, pos) +-- MUST call callback() or callback("error msg") in case of error once test is finished +-- this means you cannot use assert() in the test implementation +-- opts: { +-- player = false, -- Does test require a player? +-- map = false, -- Does test require map access? +-- async = false, -- Does the test run asynchronously? (read notes above!) +-- } +function unittests.register(name, func, opts) + local def = table.copy(opts or {}) + def.name = name + def.func = func + table.insert(unittests.list, def) +end + +function unittests.on_finished(all_passed) + -- free to override +end + +-- Calls invoke with a callback as argument +-- Suspends coroutine until that callback is called +-- Return values are passed through +local function await(invoke) + local co = coroutine.running() + assert(co) + local called_early = true + invoke(function(...) + if called_early == true then + called_early = {...} + else + coroutine.resume(co, ...) + end + end) + if called_early ~= true then + -- callback was already called before yielding + return unpack(called_early) + end + called_early = nil + return coroutine.yield() +end + +function unittests.run_one(idx, counters, out_callback, player, pos) + local def = unittests.list[idx] + if not def.player then + player = nil + elseif player == nil then + out_callback(false) + return false + end + if not def.map then + pos = nil + elseif pos == nil then + out_callback(false) + return false + end + + local tbegin = core.get_us_time() + local function done(status, err) + local tend = core.get_us_time() + local ms_taken = (tend - tbegin) / 1000 + + if not status then + core.log("error", err) + end + print(string.format("[%s] %s - %dms", + status and "PASS" or "FAIL", def.name, ms_taken)) + counters.time = counters.time + ms_taken + counters.total = counters.total + 1 + if status then + counters.passed = counters.passed + 1 + end + end + + if def.async then + core.log("info", "[unittest] running " .. def.name .. " (async)") + def.func(function(err) + done(err == nil, err) + out_callback(true) + end, player, pos) + else + core.log("info", "[unittest] running " .. def.name) + local status, err = pcall(def.func, player, pos) + done(status, err) + out_callback(true) + end + + return true +end + +local function wait_for_player(callback) + if #core.get_connected_players() > 0 then + return callback(core.get_connected_players()[1]) + end + local first = true + core.register_on_joinplayer(function(player) + if first then + callback(player) + first = false + end + end) +end + +local function wait_for_map(player, callback) + local check = function() + if core.get_node_or_nil(player:get_pos()) ~= nil then + callback() + else + minetest.after(0, check) + end + end + check() +end + +function unittests.run_all() + -- This runs in a coroutine so it uses await(). + local counters = { time = 0, total = 0, passed = 0 } + + -- Run standalone tests first + for idx = 1, #unittests.list do + local def = unittests.list[idx] + def.done = await(function(cb) + unittests.run_one(idx, counters, cb, nil, nil) + end) + end + + -- Wait for a player to join, run tests that require a player + local player = await(wait_for_player) + for idx = 1, #unittests.list do + local def = unittests.list[idx] + if not def.done then + def.done = await(function(cb) + unittests.run_one(idx, counters, cb, player, nil) + end) + end + end + + -- Wait for the world to generate/load, run tests that require map access + await(function(cb) + wait_for_map(player, cb) + end) + local pos = vector.round(player:get_pos()) + for idx = 1, #unittests.list do + local def = unittests.list[idx] + if not def.done then + def.done = await(function(cb) + unittests.run_one(idx, counters, cb, player, pos) + end) + end + end + + -- Print stats + assert(#unittests.list == counters.total) + print(string.rep("+", 80)) + print(string.format("Unit Test Results: %s", + counters.total == counters.passed and "PASSED" or "FAILED")) + print(string.format(" %d / %d failed tests.", + counters.total - counters.passed, counters.total)) + print(string.format(" Testing took %dms total.", counters.time)) + print(string.rep("+", 80)) + unittests.on_finished(counters.total == counters.passed) + return counters.total == counters.passed +end + +-------------- + local modpath = minetest.get_modpath("unittests") -dofile(modpath .. "/random.lua") +dofile(modpath .. "/misc.lua") dofile(modpath .. "/player.lua") -dofile(modpath .. "/crafting_prepare.lua") dofile(modpath .. "/crafting.lua") dofile(modpath .. "/itemdescription.lua") -if minetest.settings:get_bool("devtest_unittests_autostart", false) then - unittests.test_random() - unittests.test_crafting() - unittests.test_short_desc() - minetest.register_on_joinplayer(function(player) - unittests.test_player(player) +-------------- + +if core.settings:get_bool("devtest_unittests_autostart", false) then + core.after(0, function() + coroutine.wrap(unittests.run_all)() end) +else + minetest.register_chatcommand("unittests", { + privs = {basic_privs=true}, + description = "Runs devtest unittests (may modify player or map state)", + func = function(name, param) + unittests.on_finished = function(ok) + core.chat_send_player(name, + (ok and "All tests passed." or "There were test failures.") .. + " Check the console for detailed output.") + end + coroutine.wrap(unittests.run_all)() + return true, "" + end, + }) end - diff --git a/games/devtest/mods/unittests/itemdescription.lua b/games/devtest/mods/unittests/itemdescription.lua index d6ee6551a..dc62de7f0 100644 --- a/games/devtest/mods/unittests/itemdescription.lua +++ b/games/devtest/mods/unittests/itemdescription.lua @@ -25,7 +25,7 @@ minetest.register_chatcommand("item_description", { end }) -function unittests.test_short_desc() +local function test_short_desc() local function get_short_description(item) return ItemStack(item):get_short_description() end @@ -49,3 +49,4 @@ function unittests.test_short_desc() return true end +unittests.register("test_short_desc", test_short_desc) diff --git a/games/devtest/mods/unittests/misc.lua b/games/devtest/mods/unittests/misc.lua new file mode 100644 index 000000000..cf4f92cfa --- /dev/null +++ b/games/devtest/mods/unittests/misc.lua @@ -0,0 +1,38 @@ +local function test_random() + -- Try out PseudoRandom + local pseudo = PseudoRandom(13) + assert(pseudo:next() == 22290) + assert(pseudo:next() == 13854) +end +unittests.register("test_random", test_random) + +local function test_dynamic_media(cb, player) + if core.get_player_information(player:get_player_name()).protocol_version < 40 then + core.log("warning", "test_dynamic_media: Client too old, skipping test.") + return cb() + end + + -- Check that the client acknowledges media transfers + local path = core.get_worldpath() .. "/test_media.obj" + local f = io.open(path, "w") + f:write("# contents don't matter\n") + f:close() + + local call_ok = false + local ok = core.dynamic_add_media({ + filepath = path, + to_player = player:get_player_name(), + }, function(name) + if not call_ok then + cb("impossible condition") + end + cb() + end) + if not ok then + return cb("dynamic_add_media() returned error") + end + call_ok = true + + -- if the callback isn't called this test will just hang :shrug: +end +unittests.register("test_dynamic_media", test_dynamic_media, {async=true, player=true}) diff --git a/games/devtest/mods/unittests/player.lua b/games/devtest/mods/unittests/player.lua index 4a681310d..fa0557960 100644 --- a/games/devtest/mods/unittests/player.lua +++ b/games/devtest/mods/unittests/player.lua @@ -2,6 +2,21 @@ -- HP Change Reasons -- local expect = nil +minetest.register_on_player_hpchange(function(player, hp, reason) + if expect == nil then + return + end + + for key, value in pairs(reason) do + assert(expect[key] == value) + end + for key, value in pairs(expect) do + assert(reason[key] == value) + end + + expect = nil +end) + local function run_hpchangereason_tests(player) local old_hp = player:get_hp() @@ -20,7 +35,11 @@ local function run_hpchangereason_tests(player) player:set_hp(old_hp) end +unittests.register("test_hpchangereason", run_hpchangereason_tests, {player=true}) +-- +-- Player meta +-- local function run_player_meta_tests(player) local meta = player:get_meta() meta:set_string("foo", "bar") @@ -48,29 +67,4 @@ local function run_player_meta_tests(player) assert(meta:get_string("foo") == "") assert(meta:equals(meta2)) end - -function unittests.test_player(player) - minetest.register_on_player_hpchange(function(player, hp, reason) - if not expect then - return - end - - for key, value in pairs(reason) do - assert(expect[key] == value) - end - - for key, value in pairs(expect) do - assert(reason[key] == value) - end - - expect = nil - end) - - run_hpchangereason_tests(player) - run_player_meta_tests(player) - local msg = "Player tests passed for player '"..player:get_player_name().."'!" - minetest.chat_send_all(msg) - minetest.log("action", "[unittests] "..msg) - return true -end - +unittests.register("test_player_meta", run_player_meta_tests, {player=true}) diff --git a/games/devtest/mods/unittests/random.lua b/games/devtest/mods/unittests/random.lua deleted file mode 100644 index f94f0a88e..000000000 --- a/games/devtest/mods/unittests/random.lua +++ /dev/null @@ -1,10 +0,0 @@ -function unittests.test_random() - -- Try out PseudoRandom - minetest.log("action", "[unittests] Testing PseudoRandom ...") - local pseudo = PseudoRandom(13) - assert(pseudo:next() == 22290) - assert(pseudo:next() == 13854) - minetest.log("action", "[unittests] PseudoRandom test passed!") - return true -end - diff --git a/games/devtest/mods/util_commands/init.lua b/games/devtest/mods/util_commands/init.lua index ca5dca2d9..79acaa0d0 100644 --- a/games/devtest/mods/util_commands/init.lua +++ b/games/devtest/mods/util_commands/init.lua @@ -114,6 +114,59 @@ minetest.register_chatcommand("detach", { end, }) +minetest.register_chatcommand("use_tool", { + params = "(dig <group> <leveldiff>) | (hit <damage_group> <time_from_last_punch>) [<uses>]", + description = "Apply tool wear a number of times, as if it were used for digging", + func = function(name, param) + local player = minetest.get_player_by_name(name) + if not player then + return false, "No player." + end + local mode, group, level, uses = string.match(param, "([a-z]+) ([a-z0-9]+) (-?%d+) (%d+)") + if not mode then + mode, group, level = string.match(param, "([a-z]+) ([a-z0-9]+) (-?%d+)") + uses = 1 + end + if not mode or not group or not level then + return false + end + if mode ~= "dig" and mode ~= "hit" then + return false + end + local tool = player:get_wielded_item() + local caps = tool:get_tool_capabilities() + if not caps or tool:get_count() == 0 then + return false, "No tool in hand." + end + local actual_uses = 0 + for u=1, uses do + local wear = tool:get_wear() + local dp + if mode == "dig" then + dp = minetest.get_dig_params({[group]=3, level=level}, caps, wear) + else + dp = minetest.get_hit_params({[group]=100}, caps, level, wear) + end + tool:add_wear(dp.wear) + actual_uses = actual_uses + 1 + if tool:get_count() == 0 then + break + end + end + player:set_wielded_item(tool) + if tool:get_count() == 0 then + return true, string.format("Tool used %d time(s). ".. + "The tool broke after %d use(s).", uses, actual_uses) + else + local wear = tool:get_wear() + return true, string.format("Tool used %d time(s). ".. + "Final wear=%d", uses, wear) + end + end, +}) + + + -- Use this to test waypoint capabilities minetest.register_chatcommand("test_waypoints", { params = "[change_immediate]", diff --git a/games/devtest/settingtypes.txt b/games/devtest/settingtypes.txt index 40ee5845b..c4365643e 100644 --- a/games/devtest/settingtypes.txt +++ b/games/devtest/settingtypes.txt @@ -30,8 +30,3 @@ devtest_dungeon_mossycobble (Generate mossy cobblestone) bool false # If enabled, some very basic biomes will be registered. devtest_register_biomes (Register biomes) bool true - -# If set to true, will show an inventory image for nodes that have no inventory image as of Minetest 5.1.0. -# This is due to <https://github.com/minetest/minetest/issues/9209>. -# This is only added to make the items more visible to avoid confusion, but you will no longer see the default inventory images for these items. When you want to test the default inventory image of drawtypes, this should be turned off. -testnodes_show_fallback_image (Use fallback inventory images) bool false |