mirror of
https://github.com/minetest/minetest.git
synced 2024-07-04 15:05:27 +02:00
Compare commits
11 Commits
4a6e07dbba
...
484b4e3b8e
Author | SHA1 | Date | |
---|---|---|---|
|
484b4e3b8e | ||
|
9a1501ae89 | ||
|
514e106414 | ||
|
d74ec349c7 | ||
|
dc116f0a44 | ||
|
e00f1d0763 | ||
|
89079a6619 | ||
|
be14c86320 | ||
|
0810275640 | ||
|
0ad415c18d | ||
|
d4e8da1041 |
@ -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
|
||||||
|
@ -3902,6 +3902,7 @@ Operators
|
|||||||
---------
|
---------
|
||||||
|
|
||||||
Operators can be used if all of the involved vectors have metatables:
|
Operators can be used if all of the involved vectors have metatables:
|
||||||
|
|
||||||
* `v1 == v2`:
|
* `v1 == v2`:
|
||||||
* Returns whether `v1` and `v2` are identical.
|
* Returns whether `v1` and `v2` are identical.
|
||||||
* `-v`:
|
* `-v`:
|
||||||
@ -6129,12 +6130,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
|
||||||
@ -7796,13 +7809,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).
|
||||||
@ -7821,6 +7839,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.
|
||||||
|
@ -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})
|
||||||
|
@ -129,9 +129,9 @@ EM_BOOL CIrrDeviceSDL::MouseLeaveCallback(int eventType, const EmscriptenMouseEv
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool CIrrDeviceSDL::keyIsKnownSpecial(EKEY_CODE key)
|
bool CIrrDeviceSDL::keyIsKnownSpecial(EKEY_CODE irrlichtKey)
|
||||||
{
|
{
|
||||||
switch (key) {
|
switch (irrlichtKey) {
|
||||||
// keys which are known to have safe special character interpretation
|
// keys which are known to have safe special character interpretation
|
||||||
// could need changes over time (removals and additions!)
|
// could need changes over time (removals and additions!)
|
||||||
case KEY_RETURN:
|
case KEY_RETURN:
|
||||||
@ -189,24 +189,68 @@ bool CIrrDeviceSDL::keyIsKnownSpecial(EKEY_CODE key)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int CIrrDeviceSDL::findCharToPassToIrrlicht(int assumedChar, EKEY_CODE key)
|
int CIrrDeviceSDL::findCharToPassToIrrlicht(uint32_t sdlKey, EKEY_CODE irrlichtKey, bool numlock)
|
||||||
{
|
{
|
||||||
|
switch (irrlichtKey) {
|
||||||
// special cases that always return a char regardless of how the SDL keycode
|
// special cases that always return a char regardless of how the SDL keycode
|
||||||
// looks
|
// looks
|
||||||
switch (key) {
|
|
||||||
case KEY_RETURN:
|
case KEY_RETURN:
|
||||||
case KEY_ESCAPE:
|
case KEY_ESCAPE:
|
||||||
return (int)key;
|
return (int)irrlichtKey;
|
||||||
|
|
||||||
|
// This is necessary for keys on the numpad because they don't use the same
|
||||||
|
// keycodes as their non-numpad versions (whose keycodes correspond to chars),
|
||||||
|
// but have their own SDL keycodes and their own Irrlicht keycodes (which
|
||||||
|
// don't correspond to chars).
|
||||||
|
case KEY_MULTIPLY:
|
||||||
|
return '*';
|
||||||
|
case KEY_ADD:
|
||||||
|
return '+';
|
||||||
|
case KEY_SUBTRACT:
|
||||||
|
return '-';
|
||||||
|
case KEY_DIVIDE:
|
||||||
|
return '/';
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (numlock) {
|
||||||
|
// Number keys on the numpad are also affected, but we only want them
|
||||||
|
// to produce number chars when numlock is enabled.
|
||||||
|
switch (irrlichtKey) {
|
||||||
|
case KEY_NUMPAD0:
|
||||||
|
return '0';
|
||||||
|
case KEY_NUMPAD1:
|
||||||
|
return '1';
|
||||||
|
case KEY_NUMPAD2:
|
||||||
|
return '2';
|
||||||
|
case KEY_NUMPAD3:
|
||||||
|
return '3';
|
||||||
|
case KEY_NUMPAD4:
|
||||||
|
return '4';
|
||||||
|
case KEY_NUMPAD5:
|
||||||
|
return '5';
|
||||||
|
case KEY_NUMPAD6:
|
||||||
|
return '6';
|
||||||
|
case KEY_NUMPAD7:
|
||||||
|
return '7';
|
||||||
|
case KEY_NUMPAD8:
|
||||||
|
return '8';
|
||||||
|
case KEY_NUMPAD9:
|
||||||
|
return '9';
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SDL in-place ORs values with no character representation with 1<<30
|
// SDL in-place ORs values with no character representation with 1<<30
|
||||||
// https://wiki.libsdl.org/SDL2/SDLKeycodeLookup
|
// https://wiki.libsdl.org/SDL2/SDLKeycodeLookup
|
||||||
if (assumedChar & (1 << 30))
|
// This also affects the numpad keys btw.
|
||||||
|
if (sdlKey & (1 << 30))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
switch (key) {
|
switch (irrlichtKey) {
|
||||||
case KEY_PRIOR:
|
case KEY_PRIOR:
|
||||||
case KEY_NEXT:
|
case KEY_NEXT:
|
||||||
case KEY_HOME:
|
case KEY_HOME:
|
||||||
@ -218,7 +262,7 @@ int CIrrDeviceSDL::findCharToPassToIrrlicht(int assumedChar, EKEY_CODE key)
|
|||||||
case KEY_NUMLOCK:
|
case KEY_NUMLOCK:
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
return assumedChar;
|
return sdlKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -825,7 +869,8 @@ bool CIrrDeviceSDL::run()
|
|||||||
irrevent.KeyInput.PressedDown = (SDL_event.type == SDL_KEYDOWN);
|
irrevent.KeyInput.PressedDown = (SDL_event.type == SDL_KEYDOWN);
|
||||||
irrevent.KeyInput.Shift = (SDL_event.key.keysym.mod & KMOD_SHIFT) != 0;
|
irrevent.KeyInput.Shift = (SDL_event.key.keysym.mod & KMOD_SHIFT) != 0;
|
||||||
irrevent.KeyInput.Control = (SDL_event.key.keysym.mod & KMOD_CTRL) != 0;
|
irrevent.KeyInput.Control = (SDL_event.key.keysym.mod & KMOD_CTRL) != 0;
|
||||||
irrevent.KeyInput.Char = findCharToPassToIrrlicht(mp.SDLKey, key);
|
irrevent.KeyInput.Char = findCharToPassToIrrlicht(mp.SDLKey, key,
|
||||||
|
(SDL_event.key.keysym.mod & KMOD_NUM) != 0);
|
||||||
postEventFromUser(irrevent);
|
postEventFromUser(irrevent);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
@ -273,10 +273,10 @@ class CIrrDeviceSDL : public CIrrDeviceStub
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
// Check if a key is a known special character with no side effects on text boxes.
|
// Check if a key is a known special character with no side effects on text boxes.
|
||||||
static bool keyIsKnownSpecial(EKEY_CODE key);
|
static bool keyIsKnownSpecial(EKEY_CODE irrlichtKey);
|
||||||
|
|
||||||
// Return the Char that should be sent to Irrlicht for the given key (either the one passed in or 0).
|
// Return the Char that should be sent to Irrlicht for the given key (either the one passed in or 0).
|
||||||
static int findCharToPassToIrrlicht(int assumedChar, EKEY_CODE key);
|
static int findCharToPassToIrrlicht(uint32_t sdlKey, EKEY_CODE irrlichtKey, bool numlock);
|
||||||
|
|
||||||
// Check if a text box is in focus. Enable or disable SDL_TEXTINPUT events only if in focus.
|
// Check if a text box is in focus. Enable or disable SDL_TEXTINPUT events only if in focus.
|
||||||
void resetReceiveTextInputEvents();
|
void resetReceiveTextInputEvents();
|
||||||
|
@ -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;
|
||||||
|
for (;;) {
|
||||||
env->continueRaycast(&o->state, &pointed);
|
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
|
||||||
|
@ -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),
|
||||||
|
@ -67,6 +67,9 @@ class ObjectRef : public ModApiBase {
|
|||||||
// 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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user