1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-07-02 16:38:41 +00:00

Merge branch 'minetest:master' into master

This commit is contained in:
DustyBagel 2024-07-04 22:28:34 -05:00 committed by GitHub
commit a8163f6d25
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 319 additions and 76 deletions

View file

@ -272,3 +272,29 @@ function core.get_globals_to_transfer()
} }
return all return all
end end
do
local function valid_object_iterator(objects)
local i = 0
local function next_valid_object()
i = i + 1
local obj = objects[i]
if obj == nil then
return
end
if obj:is_valid() then
return obj
end
return next_valid_object()
end
return next_valid_object
end
function core.objects_inside_radius(center, radius)
return valid_object_iterator(core.get_objects_inside_radius(center, radius))
end
function core.objects_in_area(min_pos, max_pos)
return valid_object_iterator(core.get_objects_in_area(min_pos, max_pos))
end
end

View file

@ -114,11 +114,11 @@ always_fly_fast (Always fly fast) bool true
# the place button. # the place button.
# #
# Requires: keyboard_mouse # Requires: keyboard_mouse
repeat_place_time (Place repetition interval) float 0.25 0.15 2.0 repeat_place_time (Place repetition interval) float 0.25 0.16 2.0
# The minimum time in seconds it takes between digging nodes when holding # The minimum time in seconds it takes between digging nodes when holding
# the dig button. # the dig button.
repeat_dig_time (Dig repetition interval) float 0.15 0.15 2.0 repeat_dig_time (Minimum dig repetition interval) float 0.0 0.0 2.0
# Automatically jump up single-node obstacles. # Automatically jump up single-node obstacles.
autojump (Automatic jumping) bool false autojump (Automatic jumping) bool false

View file

@ -2352,9 +2352,12 @@ for this group, and unable to dig the rating `1`, which is the toughest.
Unless there is a matching group that enables digging otherwise. Unless there is a matching group that enables digging otherwise.
If the result digging time is 0, a delay of 0.15 seconds is added between If the result digging time is 0, a delay of 0.15 seconds is added between
digging nodes; If the player releases LMB after digging, this delay is set to 0, digging nodes. If the player releases LMB after digging, this delay is set to 0,
i.e. players can more quickly click the nodes away instead of holding LMB. i.e. players can more quickly click the nodes away instead of holding LMB.
This extra delay is not applied in case of a digging time between 0 and 0.15,
so a digging time of 0.01 is actually faster than a digging time of 0.
### Damage groups ### Damage groups
List of damage for groups of entities. See [Entity damage mechanism]. List of damage for groups of entities. See [Entity damage mechanism].
@ -3245,7 +3248,7 @@ Elements
* Types: `text`, `image`, `color`, `indent`, `tree` * Types: `text`, `image`, `color`, `indent`, `tree`
* `text`: show cell contents as text * `text`: show cell contents as text
* `image`: cell contents are an image index, use column options to define * `image`: cell contents are an image index, use column options to define
images. images. images are scaled down to fit the row height if necessary.
* `color`: cell contents are a ColorString and define color of following * `color`: cell contents are a ColorString and define color of following
cell. cell.
* `indent`: cell contents are a number and define indentation of following * `indent`: cell contents are a number and define indentation of following
@ -3266,7 +3269,7 @@ Elements
* `0=<value>` sets image for image index 0 * `0=<value>` sets image for image index 0
* `1=<value>` sets image for image index 1 * `1=<value>` sets image for image index 1
* `2=<value>` sets image for image index 2 * `2=<value>` sets image for image index 2
* and so on; defined indices need not be contiguous empty or * and so on; defined indices need not be contiguous. empty or
non-numeric cells are treated as `0`. non-numeric cells are treated as `0`.
* `color` column options: * `color` column options:
* `span=<value>`: number of following columns to affect * `span=<value>`: number of following columns to affect
@ -6130,12 +6133,24 @@ Environment access
* Items can be added also to unloaded and non-generated blocks. * Items can be added also to unloaded and non-generated blocks.
* `minetest.get_player_by_name(name)`: Get an `ObjectRef` to a player * `minetest.get_player_by_name(name)`: Get an `ObjectRef` to a player
* Returns nothing in case of error (player offline, doesn't exist, ...). * Returns nothing in case of error (player offline, doesn't exist, ...).
* `minetest.get_objects_inside_radius(pos, radius)` * `minetest.get_objects_inside_radius(center, radius)`
* returns a list of ObjectRefs. * returns a list of ObjectRefs
* `radius`: using a Euclidean metric * `radius`: using a Euclidean metric
* `minetest.get_objects_in_area(pos1, pos2)` * **Warning**: Any kind of interaction with the environment or other APIs
* returns a list of ObjectRefs. can cause later objects in the list to become invalid while you're iterating it.
* `pos1` and `pos2` are the min and max positions of the area to search. (e.g. punching an entity removes its children)
It is recommended to use `minetest.objects_inside_radius` instead, which
transparently takes care of this possibility.
* `minetest.objects_inside_radius(center, radius)`
* returns an iterator of valid objects
* example: `for obj in minetest.objects_inside_radius(center, radius) do obj:punch(...) end`
* `minetest.get_objects_in_area(min_pos, max_pos)`
* returns a list of ObjectRefs
* `min_pos` and `max_pos` are the min and max positions of the area to search
* **Warning**: The same warning as for `minetest.get_objects_inside_radius` applies.
Use `minetest.objects_in_area` instead to iterate only valid objects.
* `minetest.objects_in_area(min_pos, max_pos)`
* returns an iterator of valid objects
* `minetest.set_timeofday(val)`: set time of day * `minetest.set_timeofday(val)`: set time of day
* `val` is between `0` and `1`; `0` for midnight, `0.5` for midday * `val` is between `0` and `1`; `0` for midnight, `0.5` for midday
* `minetest.get_timeofday()`: get time of day * `minetest.get_timeofday()`: get time of day
@ -6727,6 +6742,7 @@ This allows you easy interoperability for delegating work to jobs.
### List of APIs available in an async environment ### List of APIs available in an async environment
Classes: Classes:
* `AreaStore` * `AreaStore`
* `ItemStack` * `ItemStack`
* `PerlinNoise` * `PerlinNoise`
@ -6740,17 +6756,20 @@ Classes:
* `Settings` * `Settings`
Class instances that can be transferred between environments: Class instances that can be transferred between environments:
* `ItemStack` * `ItemStack`
* `PerlinNoise` * `PerlinNoise`
* `PerlinNoiseMap` * `PerlinNoiseMap`
* `VoxelManip` * `VoxelManip`
Functions: Functions:
* Standalone helpers such as logging, filesystem, encoding, * Standalone helpers such as logging, filesystem, encoding,
hashing or compression APIs hashing or compression APIs
* `minetest.register_async_metatable` (see above) * `minetest.register_async_metatable` (see above)
Variables: Variables:
* `minetest.settings` * `minetest.settings`
* `minetest.registered_items`, `registered_nodes`, `registered_tools`, * `minetest.registered_items`, `registered_nodes`, `registered_tools`,
`registered_craftitems` and `registered_aliases` `registered_craftitems` and `registered_aliases`
@ -6802,6 +6821,7 @@ does not have a global step or timer.
### List of APIs available in the mapgen env ### List of APIs available in the mapgen env
Classes: Classes:
* `AreaStore` * `AreaStore`
* `ItemStack` * `ItemStack`
* `PerlinNoise` * `PerlinNoise`
@ -6815,6 +6835,7 @@ Classes:
* `Settings` * `Settings`
Functions: Functions:
* Standalone helpers such as logging, filesystem, encoding, * Standalone helpers such as logging, filesystem, encoding,
hashing or compression APIs hashing or compression APIs
* `minetest.get_biome_id`, `get_biome_name`, `get_heat`, `get_humidity`, * `minetest.get_biome_id`, `get_biome_name`, `get_heat`, `get_humidity`,
@ -6825,6 +6846,7 @@ Functions:
* these only operate on the current chunk (if inside a callback) * these only operate on the current chunk (if inside a callback)
Variables: Variables:
* `minetest.settings` * `minetest.settings`
* `minetest.registered_items`, `registered_nodes`, `registered_tools`, * `minetest.registered_items`, `registered_nodes`, `registered_tools`,
`registered_craftitems` and `registered_aliases` `registered_craftitems` and `registered_aliases`
@ -7797,13 +7819,18 @@ When you receive an `ObjectRef` as a callback argument or from another API
function, it is possible to store the reference somewhere and keep it around. function, it is possible to store the reference somewhere and keep it around.
It will keep functioning until the object is unloaded or removed. It will keep functioning until the object is unloaded or removed.
However, doing this is **NOT** recommended as there is (intentionally) no method However, doing this is **NOT** recommended - `ObjectRefs` should be "let go"
to test if a previously acquired `ObjectRef` is still valid. of as soon as control is returned from Lua back to the engine.
Instead, `ObjectRefs` should be "let go" of as soon as control is returned from
Lua back to the engine.
Doing so is much less error-prone and you will never need to wonder if the Doing so is much less error-prone and you will never need to wonder if the
object you are working with still exists. object you are working with still exists.
If this is not feasible, you can test whether an `ObjectRef` is still valid
via `object:is_valid()`.
Getters may be called for invalid objects and will return nothing then.
All other methods should not be called on invalid objects.
### Attachments ### Attachments
It is possible to attach objects to other objects (`set_attach` method). It is possible to attach objects to other objects (`set_attach` method).
@ -7822,6 +7849,8 @@ child will follow movement and rotation of that bone.
### Methods ### Methods
* `is_valid()`: returns whether the object is valid.
* See "Advice on handling `ObjectRefs`" above.
* `get_pos()`: returns position as vector `{x=num, y=num, z=num}` * `get_pos()`: returns position as vector `{x=num, y=num, z=num}`
* `set_pos(pos)`: * `set_pos(pos)`:
* Sets the position of the object. * Sets the position of the object.
@ -10654,7 +10683,10 @@ Used by `minetest.add_particle`.
texture = "image.png", texture = "image.png",
-- The texture of the particle -- The texture of the particle
-- v5.6.0 and later: also supports the table format described in the -- v5.6.0 and later: also supports the table format described in the
-- following section -- following section, but due to a bug this did not take effect
-- (beyond the texture name).
-- v5.9.0 and later: fixes the bug.
-- Note: "texture.animation" is ignored here. Use "animation" below instead.
playername = "singleplayer", playername = "singleplayer",
-- Optional, if specified spawns particle only on the player's client -- Optional, if specified spawns particle only on the player's client

View file

@ -71,7 +71,7 @@ end
-- Mese Pickaxe: special tool that digs "everything" instantly -- Mese Pickaxe: special tool that digs "everything" instantly
minetest.register_tool("basetools:pick_mese", { minetest.register_tool("basetools:pick_mese", {
description = "Mese Pickaxe".."\n".. description = "Mese Pickaxe".."\n"..
"Digs diggable nodes instantly", "Digs diggable nodes instantly.",
inventory_image = "basetools_mesepick.png", inventory_image = "basetools_mesepick.png",
tool_capabilities = { tool_capabilities = {
full_punch_interval = 1.0, full_punch_interval = 1.0,
@ -88,6 +88,28 @@ minetest.register_tool("basetools:pick_mese", {
}) })
-- A variant of the mese pickaxe that is not affected by the 0.15s digging delay
minetest.register_tool("basetools:pick_mese_no_delay", {
description = "Mese Pickaxe (no delay)".."\n"..
"Digs diggable nodes instantly.".."\n"..
"There is no delay between digging each node,\n"..
'but the "repeat_dig_time" setting is still respected.',
inventory_image = "basetools_mesepick_no_delay.png",
tool_capabilities = {
full_punch_interval = 1.0,
max_drop_level=3,
groupcaps={
cracky={times={[1]=0.001, [2]=0.001, [3]=0.001}, maxlevel=255},
crumbly={times={[1]=0.001, [2]=0.001, [3]=0.001}, maxlevel=255},
snappy={times={[1]=0.001, [2]=0.001, [3]=0.001}, maxlevel=255},
choppy={times={[1]=0.001, [2]=0.001, [3]=0.001}, maxlevel=255},
dig_immediate={times={[1]=0.001, [2]=0.001, [3]=0.001}, maxlevel=255},
},
damage_groups = {fleshy=100},
},
})
-- --
-- Pickaxes: Dig cracky -- Pickaxes: Dig cracky
-- --

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 B

View file

@ -64,6 +64,41 @@ local inv_style_fs = [[
list[current_player;main;.5,7;8,4] list[current_player;main;.5,7;8,4]
]] ]]
-- Some textures from textures/base/pack and Devtest, with many different sizes
-- and aspect ratios.
local image_column = "image,0=logo.png,1=rare_controls.png,2=checkbox_16.png," ..
"3=checkbox_32.png,4=checkbox_64.png,5=default_lava.png," ..
"6=progress_bar.png,7=progress_bar_bg.png"
local words = {
"esciunt", "repudiandae", "repellat", "voluptatem", "autem", "vitae", "et",
"minima", "quasi", "facere", "nihil", "ea", "nemo", "rem", "non", "eos",
"laudantium", "eveniet", "veritatis",
}
local reseed = math.random(2^31-1)
math.randomseed(1337)
local table_content = {}
for i = 1, 100 do
table.insert(table_content, words[math.random(#words)])
table.insert(table_content, words[math.random(#words)])
table.insert(table_content, words[math.random(#words)])
table.insert(table_content, math.random(0, 7))
table.insert(table_content, math.random(0, 7))
table.insert(table_content, math.random(0, 7))
table.insert(table_content, words[math.random(#words)])
end
math.randomseed(reseed)
local table_fs = table.concat({
"tablecolumns[text,align=left;text,align=right;text,align=center;",
image_column, ",align=left;",
image_column, ",align=right;",
image_column, ",align=center;text,align=right]",
"table[0,0;17,12;the_table;", table.concat(table_content, ","), ";1]"
})
local hypertext_basic = [[A hypertext element local hypertext_basic = [[A hypertext element
<bigger>Normal test</bigger> <bigger>Normal test</bigger>
This is a normal text. This is a normal text.
@ -350,6 +385,10 @@ local pages = {
"label[11,0.5;Noclip]" .. "label[11,0.5;Noclip]" ..
"container[11.5,1]" .. clip_fs:gsub("%%c", "true") .. "container_end[]", "container[11.5,1]" .. clip_fs:gsub("%%c", "true") .. "container_end[]",
-- Table
"size[18,13]real_coordinates[true]" ..
"container[0.5,0.5]" .. table_fs.. "container_end[]",
-- Hypertext -- Hypertext
"size[12,13]real_coordinates[true]" .. "size[12,13]real_coordinates[true]" ..
"container[0.5,0.5]" .. hypertext_fs .. "container_end[]", "container[0.5,0.5]" .. hypertext_fs .. "container_end[]",
@ -477,7 +516,7 @@ local function show_test_formspec(pname)
page = page() page = page()
end end
local fs = page .. "tabheader[0,0;11,0.65;maintabs;Real Coord,Styles,Noclip,Hypertext,Tabs,Invs,Window,Anim,Model,ScrollC,Sound,Background,Unsized;" .. page_id .. ";false;false]" local fs = page .. "tabheader[0,0;11,0.65;maintabs;Real Coord,Styles,Noclip,Table,Hypertext,Tabs,Invs,Window,Anim,Model,ScrollC,Sound,Background,Unsized;" .. page_id .. ";false;false]"
minetest.show_formspec(pname, "testformspec:formspec", fs) minetest.show_formspec(pname, "testformspec:formspec", fs)
end end

View file

@ -71,13 +71,13 @@ local function test_entity_lifecycle(_, pos)
-- with binary in staticdata -- with binary in staticdata
local obj = core.add_entity(pos, "unittests:callbacks", "abc\000def") local obj = core.add_entity(pos, "unittests:callbacks", "abc\000def")
assert(obj and obj:is_valid())
check_log({"on_activate(7)"}) check_log({"on_activate(7)"})
obj:set_hp(0) obj:set_hp(0)
check_log({"on_death(nil)", "on_deactivate(true)"}) check_log({"on_death(nil)", "on_deactivate(true)"})
-- objectref must be invalid now assert(not obj:is_valid())
assert(obj:get_velocity() == nil)
end end
unittests.register("test_entity_lifecycle", test_entity_lifecycle, {map=true}) unittests.register("test_entity_lifecycle", test_entity_lifecycle, {map=true})
@ -130,3 +130,57 @@ local function test_entity_attach(player, pos)
obj:remove() obj:remove()
end end
unittests.register("test_entity_attach", test_entity_attach, {player=true, map=true}) unittests.register("test_entity_attach", test_entity_attach, {player=true, map=true})
core.register_entity("unittests:dummy", {
initial_properties = {
hp_max = 1,
visual = "upright_sprite",
textures = { "no_texture.png" },
static_save = false,
},
})
local function test_entity_raycast(_, pos)
local obj1 = core.add_entity(pos, "unittests:dummy")
local obj2 = core.add_entity(pos:offset(1, 0, 0), "unittests:dummy")
local raycast = core.raycast(pos:offset(-1, 0, 0), pos:offset(2, 0, 0), true, false)
for pt in raycast do
if pt.type == "object" then
assert(pt.ref == obj1)
obj1:remove()
obj2:remove()
obj1 = nil -- object should be hit exactly one
end
end
assert(obj1 == nil)
end
unittests.register("test_entity_raycast", test_entity_raycast, {map=true})
local function test_object_iterator(pos, make_iterator)
local obj1 = core.add_entity(pos, "unittests:dummy")
local obj2 = core.add_entity(pos, "unittests:dummy")
assert(obj1 and obj2)
local found = false
-- As soon as we find one of the objects, we remove both, invalidating the other.
for obj in make_iterator() do
assert(obj:is_valid())
if obj == obj1 or obj == obj2 then
obj1:remove()
obj2:remove()
found = true
end
end
assert(found)
end
unittests.register("test_objects_inside_radius", function(_, pos)
test_object_iterator(pos, function()
return core.objects_inside_radius(pos, 1)
end)
end, {map=true})
unittests.register("test_objects_in_area", function(_, pos)
test_object_iterator(pos, function()
return core.objects_in_area(pos:offset(-1, -1, -1), pos:offset(1, 1, 1))
end)
end, {map=true})

6
lib/lua/README.md Normal file
View file

@ -0,0 +1,6 @@
## Lua 5.1.5 source
This copy has been patched with Minetest-specific changes (`lua_atccall`)
so it is *not* interchangeable with upstream PUC Lua.
A patch for CVE-2014-5461 has been applied.

View file

@ -340,13 +340,14 @@ static int luaB_assert (lua_State *L) {
static int luaB_unpack (lua_State *L) { static int luaB_unpack (lua_State *L) {
int i, e, n; int i, e;
unsigned int n;
luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 1, LUA_TTABLE);
i = luaL_optint(L, 2, 1); i = luaL_optint(L, 2, 1);
e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1)); e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1));
if (i > e) return 0; /* empty range */ if (i > e) return 0; /* empty range */
n = e - i + 1; /* number of elements */ n = (unsigned int)e - (unsigned int)i; /* number of elements minus 1 */
if (n <= 0 || !lua_checkstack(L, n)) /* n <= 0 means arith. overflow */ if (n > (INT_MAX - 10) || !lua_checkstack(L, ++n))
return luaL_error(L, "too many results to unpack"); return luaL_error(L, "too many results to unpack");
lua_rawgeti(L, 1, i); /* push arg[i] (avoiding overflow problems) */ lua_rawgeti(L, 1, i); /* push arg[i] (avoiding overflow problems) */
while (i++ < e) /* push arg[i + 1...e] */ while (i++ < e) /* push arg[i + 1...e] */

View file

@ -101,6 +101,7 @@ static int db_getinfo (lua_State *L) {
int arg; int arg;
lua_State *L1 = getthread(L, &arg); lua_State *L1 = getthread(L, &arg);
const char *options = luaL_optstring(L, arg+2, "flnSu"); const char *options = luaL_optstring(L, arg+2, "flnSu");
luaL_argcheck(L, options[0] != '>', arg + 2, "invalid option '>'");
if (lua_isnumber(L, arg+1)) { if (lua_isnumber(L, arg+1)) {
if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) { if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) {
lua_pushnil(L); /* level out of range */ lua_pushnil(L); /* level out of range */

View file

@ -274,7 +274,7 @@ int luaD_precall (lua_State *L, StkId func, int nresults) {
CallInfo *ci; CallInfo *ci;
StkId st, base; StkId st, base;
Proto *p = cl->p; Proto *p = cl->p;
luaD_checkstack(L, p->maxstacksize); luaD_checkstack(L, p->maxstacksize + p->numparams);
func = restorestack(L, funcr); func = restorestack(L, funcr);
if (!p->is_vararg) { /* no varargs? */ if (!p->is_vararg) { /* no varargs? */
base = func + 1; base = func + 1;

View file

@ -133,7 +133,7 @@ static void inclinenumber (LexState *ls) {
if (currIsNewline(ls) && ls->current != old) if (currIsNewline(ls) && ls->current != old)
next(ls); /* skip `\n\r' or `\r\n' */ next(ls); /* skip `\n\r' or `\r\n' */
if (++ls->linenumber >= MAX_INT) if (++ls->linenumber >= MAX_INT)
luaX_syntaxerror(ls, "chunk has too many lines"); luaX_lexerror(ls, "chunk has too many lines", 0);
} }

View file

@ -209,14 +209,15 @@ static int l_strcmp (const TString *ls, const TString *rs) {
int temp = strcoll(l, r); int temp = strcoll(l, r);
if (temp != 0) return temp; if (temp != 0) return temp;
else { /* strings are equal up to a `\0' */ else { /* strings are equal up to a `\0' */
size_t len = strlen(l); /* index of first `\0' in both strings */ size_t zl1 = strlen(l); /* index of first '\0' in 'l' */
if (len == lr) /* r is finished? */ size_t zl2 = strlen(r); /* index of first '\0' in 'r' */
return (len == ll) ? 0 : 1; if (zl2 == lr) /* 'r' is finished? */
else if (len == ll) /* l is finished? */ return (zl1 == ll) ? 0 : 1; /* check 'l' */
return -1; /* l is smaller than r (because r is not finished) */ else if (zl1 == ll) /* 'l' is finished? */
/* both strings longer than `len'; go on comparing (after the `\0') */ return -1; /* 'l' is less than 'r' ('r' is not finished) */
len++; /* both strings longer than 'zl'; go on comparing after the '\0' */
l += len; ll -= len; r += len; lr -= len; zl1++; zl2++;
l += zl1; ll -= zl1; r += zl2; lr -= zl2;
} }
} }
} }

View file

@ -3987,8 +3987,12 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
runData.nodig_delay_timer = runData.nodig_delay_timer =
runData.dig_time_complete / (float)crack_animation_length; runData.dig_time_complete / (float)crack_animation_length;
// Don't add a corresponding delay to very time consuming nodes. // We don't want a corresponding delay to very time consuming nodes
runData.nodig_delay_timer = std::min(runData.nodig_delay_timer, 0.3f); // and nodes without digging time (e.g. torches) get a fixed delay.
if (runData.nodig_delay_timer > 0.3f)
runData.nodig_delay_timer = 0.3f;
else if (runData.dig_instantly)
runData.nodig_delay_timer = 0.15f;
// Ensure that the delay between breaking nodes // Ensure that the delay between breaking nodes
// (dig_time_complete + nodig_delay_timer) is at least the // (dig_time_complete + nodig_delay_timer) is at least the
@ -4392,8 +4396,8 @@ void Game::readSettings()
m_cache_enable_fog = g_settings->getBool("enable_fog"); m_cache_enable_fog = g_settings->getBool("enable_fog");
m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity", 0.001f, 10.0f); m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity", 0.001f, 10.0f);
m_cache_joystick_frustum_sensitivity = std::max(g_settings->getFloat("joystick_frustum_sensitivity"), 0.001f); m_cache_joystick_frustum_sensitivity = std::max(g_settings->getFloat("joystick_frustum_sensitivity"), 0.001f);
m_repeat_place_time = g_settings->getFloat("repeat_place_time", 0.15f, 2.0f); m_repeat_place_time = g_settings->getFloat("repeat_place_time", 0.16f, 2.0f);
m_repeat_dig_time = g_settings->getFloat("repeat_dig_time", 0.15f, 2.0f); m_repeat_dig_time = g_settings->getFloat("repeat_dig_time", 0.0f, 2.0f);
m_cache_enable_noclip = g_settings->getBool("noclip"); m_cache_enable_noclip = g_settings->getBool("noclip");
m_cache_enable_free_move = g_settings->getBool("free_move"); m_cache_enable_free_move = g_settings->getBool("free_move");

View file

@ -359,7 +359,7 @@ void set_default_settings()
settings->setDefault("invert_hotbar_mouse_wheel", "false"); settings->setDefault("invert_hotbar_mouse_wheel", "false");
settings->setDefault("mouse_sensitivity", "0.2"); settings->setDefault("mouse_sensitivity", "0.2");
settings->setDefault("repeat_place_time", "0.25"); settings->setDefault("repeat_place_time", "0.25");
settings->setDefault("repeat_dig_time", "0.15"); settings->setDefault("repeat_dig_time", "0.0");
settings->setDefault("safe_dig_and_place", "false"); settings->setDefault("safe_dig_and_place", "false");
settings->setDefault("random_input", "false"); settings->setDefault("random_input", "false");
settings->setDefault("aux1_descends", "false"); settings->setDefault("aux1_descends", "false");

View file

@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <IGUIFont.h> #include <IGUIFont.h>
#include "client/renderingengine.h" #include "client/renderingengine.h"
#include "debug.h" #include "debug.h"
#include "irrlicht_changes/CGUITTFont.h"
#include "log.h" #include "log.h"
#include "client/texturesource.h" #include "client/texturesource.h"
#include "gettime.h" #include "gettime.h"
@ -227,6 +228,8 @@ void GUITable::setTable(const TableOptions &options,
s32 content_index; s32 content_index;
// Next cell: Width in pixels // Next cell: Width in pixels
s32 content_width; s32 content_width;
// Next cell: Image scale (only for "image" column type)
f32 image_scale;
// Vector of completed cells in this row // Vector of completed cells in this row
std::vector<Cell> cells; std::vector<Cell> cells;
// Stores colors and how long they last (maximum column index) // Stores colors and how long they last (maximum column index)
@ -236,6 +239,17 @@ void GUITable::setTable(const TableOptions &options,
}; };
TempRow *rows = new TempRow[rowcount]; TempRow *rows = new TempRow[rowcount];
CGUITTFont *ttfont = dynamic_cast<CGUITTFont *>(m_font);
f32 desired_image_scale = 1.0f;
if (ttfont) {
// This gives us the effective font size, which is chosen taking display
// density and gui_scaling into account.
// Since row height scales with font size, this gives better results than
// just using display density and gui_scaling when a non-standard font
// size is used (e.g. Android default of 14).
desired_image_scale = std::max(1.0f, ttfont->getFontSize() / 16.0f);
}
// Get em width. Pedantically speaking, the width of "M" is not // Get em width. Pedantically speaking, the width of "M" is not
// necessarily the same as the em width, but whatever, close enough. // necessarily the same as the em width, but whatever, close enough.
s32 em = 6; s32 em = 6;
@ -373,8 +387,18 @@ void GUITable::setTable(const TableOptions &options,
if (row->content_index >= 0) if (row->content_index >= 0)
image = m_images[row->content_index]; image = m_images[row->content_index];
row->image_scale = 1.0f;
row->content_width = 0;
if (image) {
f32 max_image_scale = (f32)m_rowheight / (f32)image->getOriginalSize().Height;
// Scale with display density and make sure it fits into the row
row->image_scale = std::min(desired_image_scale, max_image_scale);
// When upscaling, fractional factors would cause artifacts
if (row->image_scale > 1.0f)
row->image_scale = std::floor(row->image_scale);
row->content_width = image->getOriginalSize().Width * row->image_scale;
}
// Get content width and update xmax // Get content width and update xmax
row->content_width = image ? image->getOriginalSize().Width : 0;
row->content_width = MYMAX(row->content_width, width); row->content_width = MYMAX(row->content_width, width);
s32 row_xmax = row->x + padding + row->content_width; s32 row_xmax = row->x + padding + row->content_width;
xmax = MYMAX(xmax, row_xmax); xmax = MYMAX(xmax, row_xmax);
@ -384,6 +408,7 @@ void GUITable::setTable(const TableOptions &options,
newcell.xmin = rows[i].x + padding; newcell.xmin = rows[i].x + padding;
alignContent(&newcell, xmax, rows[i].content_width, align); alignContent(&newcell, xmax, rows[i].content_width, align);
newcell.content_index = rows[i].content_index; newcell.content_index = rows[i].content_index;
newcell.image_scale = rows[i].image_scale;
rows[i].cells.push_back(newcell); rows[i].cells.push_back(newcell);
rows[i].x = newcell.xmax; rows[i].x = newcell.xmax;
} }
@ -740,23 +765,23 @@ void GUITable::drawCell(const Cell *cell, video::SColor color,
video::ITexture *image = m_images[cell->content_index]; video::ITexture *image = m_images[cell->content_index];
if (image) { if (image) {
core::position2d<s32> dest_pos =
row_rect.UpperLeftCorner;
dest_pos.X += cell->xpos;
core::rect<s32> source_rect( core::rect<s32> source_rect(
core::position2d<s32>(0, 0), core::position2d<s32>(0, 0),
image->getOriginalSize()); image->getOriginalSize());
s32 imgh = source_rect.LowerRightCorner.Y; core::rect<s32> dest_rect(
0, 0,
image->getOriginalSize().Width * cell->image_scale,
image->getOriginalSize().Height * cell->image_scale);
dest_rect += row_rect.UpperLeftCorner + v2s32(cell->xpos, 0);
s32 imgh = dest_rect.getHeight();
s32 rowh = row_rect.getHeight(); s32 rowh = row_rect.getHeight();
// Center vertically if needed
if (imgh < rowh) if (imgh < rowh)
dest_pos.Y += (rowh - imgh) / 2; dest_rect += v2s32(0, (rowh - imgh) / 2);
else
source_rect.LowerRightCorner.Y = rowh;
video::SColor color(255, 255, 255, 255); driver->draw2DImage(image, dest_rect, source_rect,
&client_clip, nullptr, true);
driver->draw2DImage(image, dest_pos, source_rect,
&client_clip, color, true);
} }
} }
} }

View file

@ -166,6 +166,7 @@ protected:
video::SColor color; video::SColor color;
bool color_defined; bool color_defined;
s32 reported_column; s32 reported_column;
f32 image_scale; // only for "image" type columns
}; };
struct Row { struct Row {

View file

@ -59,14 +59,8 @@ GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
m_menumgr(menumgr), m_menumgr(menumgr),
m_remap_click_outside(remap_click_outside) m_remap_click_outside(remap_click_outside)
{ {
m_gui_scale = std::max(g_settings->getFloat("gui_scaling"), 0.5f); m_gui_scale = g_settings->getFloat("gui_scaling", 0.5f, 20.0f) *
const float screen_dpi_scale = RenderingEngine::getDisplayDensity(); RenderingEngine::getDisplayDensity();
if (g_settings->getBool("enable_touch")) {
m_gui_scale *= 1.1f - 0.3f * screen_dpi_scale + 0.2f * screen_dpi_scale * screen_dpi_scale;
} else {
m_gui_scale *= screen_dpi_scale;
}
setVisible(true); setVisible(true);
m_menumgr->createdMenu(this); m_menumgr->createdMenu(this);

View file

@ -197,7 +197,8 @@ enum class ParticleTextureFlags : u8 {
* decltype everywhere */ * decltype everywhere */
using FlagT = std::underlying_type_t<ParticleTextureFlags>; using FlagT = std::underlying_type_t<ParticleTextureFlags>;
void ServerParticleTexture::serialize(std::ostream &os, u16 protocol_ver, bool newPropertiesOnly) const void ServerParticleTexture::serialize(std::ostream &os, u16 protocol_ver,
bool newPropertiesOnly, bool skipAnimation) const
{ {
/* newPropertiesOnly is used to de/serialize parameters of the legacy texture /* newPropertiesOnly is used to de/serialize parameters of the legacy texture
* field, which are encoded separately from the texspec string */ * field, which are encoded separately from the texspec string */
@ -213,14 +214,19 @@ void ServerParticleTexture::serialize(std::ostream &os, u16 protocol_ver, bool n
if (!newPropertiesOnly) if (!newPropertiesOnly)
os << serializeString32(string); os << serializeString32(string);
if (animated) if (!skipAnimation && animated)
animation.serialize(os, protocol_ver); animation.serialize(os, protocol_ver);
} }
void ServerParticleTexture::deSerialize(std::istream &is, u16 protocol_ver, bool newPropertiesOnly) void ServerParticleTexture::deSerialize(std::istream &is, u16 protocol_ver,
bool newPropertiesOnly, bool skipAnimation)
{ {
FlagT flags = 0; FlagT flags = 0;
deSerializeParameterValue(is, flags); deSerializeParameterValue(is, flags);
// new texture properties were missing in ParticleParameters::serialize
// before Minetest 5.9.0
if (is.eof())
return;
animated = !!(flags & FlagT(ParticleTextureFlags::animated)); animated = !!(flags & FlagT(ParticleTextureFlags::animated));
blendmode = BlendMode((flags & FlagT(ParticleTextureFlags::blend)) >> 1); blendmode = BlendMode((flags & FlagT(ParticleTextureFlags::blend)) >> 1);
@ -230,7 +236,7 @@ void ServerParticleTexture::deSerialize(std::istream &is, u16 protocol_ver, bool
if (!newPropertiesOnly) if (!newPropertiesOnly)
string = deSerializeString32(is); string = deSerializeString32(is);
if (animated) if (!skipAnimation && animated)
animation.deSerialize(is, protocol_ver); animation.deSerialize(is, protocol_ver);
} }
@ -254,6 +260,7 @@ void ParticleParameters::serialize(std::ostream &os, u16 protocol_ver) const
writeV3F32(os, drag); writeV3F32(os, drag);
jitter.serialize(os); jitter.serialize(os);
bounce.serialize(os); bounce.serialize(os);
texture.serialize(os, protocol_ver, true, true);
} }
template <typename T, T (reader)(std::istream& is)> template <typename T, T (reader)(std::istream& is)>
@ -291,4 +298,5 @@ void ParticleParameters::deSerialize(std::istream &is, u16 protocol_ver)
return; return;
jitter.deSerialize(is); jitter.deSerialize(is);
bounce.deSerialize(is); bounce.deSerialize(is);
texture.deSerialize(is, protocol_ver, true, true);
} }

View file

@ -276,8 +276,10 @@ struct ParticleTexture
struct ServerParticleTexture : public ParticleTexture struct ServerParticleTexture : public ParticleTexture
{ {
std::string string; std::string string;
void serialize(std::ostream &os, u16 protocol_ver, bool newPropertiesOnly = false) const; void serialize(std::ostream &os, u16 protocol_ver, bool newPropertiesOnly = false,
void deSerialize(std::istream &is, u16 protocol_ver, bool newPropertiesOnly = false); bool skipAnimation = false) const;
void deSerialize(std::istream &is, u16 protocol_ver, bool newPropertiesOnly = false,
bool skipAnimation = false);
}; };
struct CommonParticleParams struct CommonParticleParams

View file

@ -760,7 +760,7 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index)
f.setDefaultAlphaMode(); f.setDefaultAlphaMode();
warn_if_field_exists(L, index, "alpha", warn_if_field_exists(L, index, "alpha", "node " + f.name,
"Obsolete, only limited compatibility provided; " "Obsolete, only limited compatibility provided; "
"replaced by \"use_texture_alpha\""); "replaced by \"use_texture_alpha\"");
if (getintfield_default(L, index, "alpha", 255) != 255) if (getintfield_default(L, index, "alpha", 255) != 255)
@ -768,7 +768,7 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index)
lua_getfield(L, index, "use_texture_alpha"); lua_getfield(L, index, "use_texture_alpha");
if (lua_isboolean(L, -1)) { if (lua_isboolean(L, -1)) {
warn_if_field_exists(L, index, "use_texture_alpha", warn_if_field_exists(L, index, "use_texture_alpha", "node " + f.name,
"Boolean values are deprecated; use the new choices"); "Boolean values are deprecated; use the new choices");
if (lua_toboolean(L, -1)) if (lua_toboolean(L, -1))
f.alpha = (f.drawtype == NDT_NORMAL) ? ALPHAMODE_CLIP : ALPHAMODE_BLEND; f.alpha = (f.drawtype == NDT_NORMAL) ? ALPHAMODE_CLIP : ALPHAMODE_BLEND;
@ -1315,13 +1315,16 @@ void pushnode(lua_State *L, const MapNode &n)
} }
/******************************************************************************/ /******************************************************************************/
void warn_if_field_exists(lua_State *L, int table, void warn_if_field_exists(lua_State *L, int table, const char *fieldname,
const char *name, const std::string &message) std::string_view name, std::string_view message)
{ {
lua_getfield(L, table, name); lua_getfield(L, table, fieldname);
if (!lua_isnil(L, -1)) { if (!lua_isnil(L, -1)) {
warningstream << "Field \"" << name << "\": " warningstream << "Field \"" << fieldname << "\"";
<< message << std::endl; if (!name.empty()) {
warningstream << " on " << name;
}
warningstream << ": " << message << std::endl;
infostream << script_get_backtrace(L) << std::endl; infostream << script_get_backtrace(L) << std::endl;
} }
lua_pop(L, 1); lua_pop(L, 1);

View file

@ -120,7 +120,8 @@ void push_aabb3f_vector (lua_State *L, const std::vector<aabb3f>
void warn_if_field_exists(lua_State *L, int table, void warn_if_field_exists(lua_State *L, int table,
const char *fieldname, const char *fieldname,
const std::string &message); std::string_view name,
std::string_view message);
size_t write_array_slice_float(lua_State *L, int table_index, float *data, size_t write_array_slice_float(lua_State *L, int table_index, float *data,
v3u16 data_size, v3u16 slice_offset, v3u16 slice_size); v3u16 data_size, v3u16 slice_offset, v3u16 slice_size);

View file

@ -155,6 +155,7 @@ void LuaLBM::trigger(ServerEnvironment *env, v3s16 p,
int LuaRaycast::l_next(lua_State *L) int LuaRaycast::l_next(lua_State *L)
{ {
GET_PLAIN_ENV_PTR; GET_PLAIN_ENV_PTR;
ServerEnvironment *senv = dynamic_cast<ServerEnvironment*>(env);
bool csm = false; bool csm = false;
#ifndef SERVER #ifndef SERVER
@ -163,7 +164,17 @@ int LuaRaycast::l_next(lua_State *L)
LuaRaycast *o = checkObject<LuaRaycast>(L, 1); LuaRaycast *o = checkObject<LuaRaycast>(L, 1);
PointedThing pointed; PointedThing pointed;
env->continueRaycast(&o->state, &pointed); for (;;) {
env->continueRaycast(&o->state, &pointed);
if (pointed.type != POINTEDTHING_OBJECT)
break;
if (!senv)
break;
const auto *obj = senv->getActiveObject(pointed.object_id);
if (obj && !obj->isGone())
break;
// skip gone object
}
if (pointed.type == POINTEDTHING_NOTHING) if (pointed.type == POINTEDTHING_NOTHING)
lua_pushnil(L); lua_pushnil(L);
else else

View file

@ -1354,7 +1354,7 @@ int ModApiMapgen::l_register_ore(lua_State *L)
ore->flags = 0; ore->flags = 0;
//// Get noise_threshold //// Get noise_threshold
warn_if_field_exists(L, index, "noise_threshhold", warn_if_field_exists(L, index, "noise_threshhold", "ore " + ore->name,
"Deprecated: new name is \"noise_threshold\"."); "Deprecated: new name is \"noise_threshold\".");
float nthresh; float nthresh;
@ -1364,9 +1364,9 @@ int ModApiMapgen::l_register_ore(lua_State *L)
ore->nthresh = nthresh; ore->nthresh = nthresh;
//// Get y_min/y_max //// Get y_min/y_max
warn_if_field_exists(L, index, "height_min", warn_if_field_exists(L, index, "height_min", "ore " + ore->name,
"Deprecated: new name is \"y_min\"."); "Deprecated: new name is \"y_min\".");
warn_if_field_exists(L, index, "height_max", warn_if_field_exists(L, index, "height_max", "ore " + ore->name,
"Deprecated: new name is \"y_max\"."); "Deprecated: new name is \"y_max\".");
int ymin, ymax; int ymin, ymax;

View file

@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "lua_api/l_object.h" #include "lua_api/l_object.h"
#include <cmath> #include <cmath>
#include <lua.h>
#include "lua_api/l_internal.h" #include "lua_api/l_internal.h"
#include "lua_api/l_inventory.h" #include "lua_api/l_inventory.h"
#include "lua_api/l_item.h" #include "lua_api/l_item.h"
@ -106,6 +107,13 @@ int ObjectRef::l_remove(lua_State *L)
return 0; return 0;
} }
// is_valid(self)
int ObjectRef::l_is_valid(lua_State *L)
{
lua_pushboolean(L, getobject(checkObject<ObjectRef>(L, 1)) != nullptr);
return 1;
}
// get_pos(self) // get_pos(self)
int ObjectRef::l_get_pos(lua_State *L) int ObjectRef::l_get_pos(lua_State *L)
{ {
@ -2646,6 +2654,7 @@ const char ObjectRef::className[] = "ObjectRef";
luaL_Reg ObjectRef::methods[] = { luaL_Reg ObjectRef::methods[] = {
// ServerActiveObject // ServerActiveObject
luamethod(ObjectRef, remove), luamethod(ObjectRef, remove),
luamethod(ObjectRef, is_valid),
luamethod_aliased(ObjectRef, get_pos, getpos), luamethod_aliased(ObjectRef, get_pos, getpos),
luamethod_aliased(ObjectRef, set_pos, setpos), luamethod_aliased(ObjectRef, set_pos, setpos),
luamethod(ObjectRef, add_pos), luamethod(ObjectRef, add_pos),

View file

@ -67,6 +67,9 @@ private:
// remove(self) // remove(self)
static int l_remove(lua_State *L); static int l_remove(lua_State *L);
// is_valid(self)
static int l_is_valid(lua_State *L);
// get_pos(self) // get_pos(self)
static int l_get_pos(lua_State *L); static int l_get_pos(lua_State *L);

View file

@ -458,7 +458,7 @@ def generate_template(folder, mod_name):
dOut[s] = sources dOut[s] = sources
if len(dOut) == 0: if len(dOut) == 0:
return None return (None, None)
# Convert source file set to list, sort it and add comment symbols. # Convert source file set to list, sort it and add comment symbols.
# Needed because a set is unsorted and might result in unpredictable. # Needed because a set is unsorted and might result in unpredictable.