From 1df282c635cc33a2d94aa1d9bd776811488043fb Mon Sep 17 00:00:00 2001 From: SFENCE Date: Thu, 6 Feb 2025 20:44:27 +0100 Subject: [PATCH 1/7] Add GUIDs and unit tests. --- builtin/game/features.lua | 1 + doc/lua_api.md | 16 ++++++++- doc/world_format.md | 4 ++- games/devtest/mods/unittests/entity.lua | 18 ++++++++++ games/devtest/mods/unittests/player.lua | 8 +++++ src/CMakeLists.txt | 1 + src/guid.cpp | 48 +++++++++++++++++++++++++ src/guid.h | 45 +++++++++++++++++++++++ src/script/cpp_api/s_base.cpp | 30 +++++++++++++--- src/script/lua_api/l_object.cpp | 15 ++++++++ src/script/lua_api/l_object.h | 3 ++ src/script/scripting_server.cpp | 3 ++ src/server/luaentity_sao.cpp | 28 +++++++++++++-- src/server/luaentity_sao.h | 10 +++--- src/server/player_sao.cpp | 1 + src/server/player_sao.h | 2 ++ src/server/serveractiveobject.h | 4 +++ src/serverenvironment.h | 6 ++++ src/unittest/mock_serveractiveobject.h | 17 ++++++++- 19 files changed, 246 insertions(+), 14 deletions(-) create mode 100644 src/guid.cpp create mode 100644 src/guid.h diff --git a/builtin/game/features.lua b/builtin/game/features.lua index 1b329c7a3..4837f29bd 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -47,6 +47,7 @@ core.features = { particle_blend_clip = true, remove_item_match_meta = true, httpfetch_additional_methods = true, + object_guids = true, } function core.has_feature(arg) diff --git a/doc/lua_api.md b/doc/lua_api.md index ca3a5fee7..d387412b9 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5810,6 +5810,8 @@ Utilities remove_item_match_meta = true, -- The HTTP API supports the HEAD and PATCH methods (5.12.0) httpfetch_additional_methods = true, + -- objects have get_guid method (5.13.0) + object_guids = true, } ``` @@ -7848,7 +7850,11 @@ Global tables Note: changes to initial properties will only affect entities spawned afterwards, as they are only read when spawning. * `core.object_refs` - * Map of object references, indexed by active object id + * Map of active object references, indexed by active object id + * Obsolete: Use `core.objects_by_guid` instead. + GUIDs are strictly more useful than active object IDs. +* `core.objects_by_guid` + * Map of active object references, indexed by object GUID * `core.luaentities` * Map of Lua entities, indexed by active object id * `core.registered_abms` @@ -8546,6 +8552,14 @@ child will follow movement and rotation of that bone. -- Default: false } ``` +* `get_guid()`: returns a global unique identifier (a string) + * For players, this is a player name. + * For Lua entities, this is a uniquely generated string, guaranteed not to collide with player names. + * Example: `@bGh3p2AbRE29Mb4biqX6OA` + * GUIDs only use printable ASCII characters. + * GUIDs are persisted internally between object reloads; their format is guaranteed not to change. + Thus you can store GUIDs to identify objects persistently. + #### Lua entity only (no-op for other objects) diff --git a/doc/world_format.md b/doc/world_format.md index c519361d9..c7bd0f6cd 100644 --- a/doc/world_format.md +++ b/doc/world_format.md @@ -594,9 +594,11 @@ Object types: * `s32` yaw * 1000 Since protocol version 37: -* `u8` `version2` (=1) +* `u8` `version2` (=1 or 2) * `s32` pitch * 1000 * `s32` roll * 1000 +* if version2 >= 2: + * u8[16] guid # Itemstring Format diff --git a/games/devtest/mods/unittests/entity.lua b/games/devtest/mods/unittests/entity.lua index fad7d52e9..eed6ff334 100644 --- a/games/devtest/mods/unittests/entity.lua +++ b/games/devtest/mods/unittests/entity.lua @@ -255,3 +255,21 @@ local function test_item_drop(_, pos) assert(itemstack_ret:equals(itemstack_src)) end unittests.register("test_item_drop", test_item_drop, {map=true}) + +local function test_entity_guid(_, pos) + log = {} + + local obj0 = core.add_entity(pos, "unittests:callbacks") + check_log({"on_activate(0)"}) + local obj1 = core.add_entity(pos, "unittests:callbacks") + check_log({"on_activate(0)"}) + + assert(core.objects_by_guid[obj0:get_guid()] == obj0) + assert(core.objects_by_guid[obj1:get_guid()] == obj1) + + obj0:remove() + check_log({"on_deactivate(true)"}) + obj1:remove() + check_log({"on_deactivate(true)"}) +end +unittests.register("test_entity_guid", test_entity_guid, {map=true}) diff --git a/games/devtest/mods/unittests/player.lua b/games/devtest/mods/unittests/player.lua index f8945f320..b6dcec86f 100644 --- a/games/devtest/mods/unittests/player.lua +++ b/games/devtest/mods/unittests/player.lua @@ -204,3 +204,11 @@ local function run_player_hotbar_clamp_tests(player) player:hud_set_hotbar_itemcount(old_bar_size) end unittests.register("test_player_hotbar_clamp", run_player_hotbar_clamp_tests, {player=true}) + +-- +-- Player get GUID +-- +local function test_player_guid_tests(player) + assert(player:get_guid()==player:get_player_name()) +end +unittests.register("test_player_guid", test_player_guid_tests, {player=true}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1231f49ba..2bca969c8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -455,6 +455,7 @@ set(common_SRCS environment.cpp filesys.cpp gettext.cpp + guid.cpp inventorymanager.cpp itemdef.cpp light.cpp diff --git a/src/guid.cpp b/src/guid.cpp new file mode 100644 index 000000000..f9580458c --- /dev/null +++ b/src/guid.cpp @@ -0,0 +1,48 @@ +// Luanti +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright (C) 2024 SFENCE + +#include "guid.h" +#include +#include +#include + +#include "exceptions.h" +#include "util/base64.h" +#include "log.h" + +std::string MyGUID::base64() const +{ + return base64_encode(std::string_view(&bytes[0], bytes.size())); +} + +void MyGUID::serialize(std::ostringstream &os) const +{ + os.write(&bytes[0], bytes.size()); +} + +void MyGUID::deSerialize(std::istream &is) +{ + is.read(&bytes[0], bytes.size()); +} + +GUIDGenerator::GUIDGenerator() : + m_uniform(0, UINT64_MAX) +{ + if (m_rand.entropy() <= 0.01) + warningstream << + "The system's provided random generator reports low entropy." + "GUID generator can be affected. Suggest a system upgrade." + << std::endl; +} + +MyGUID GUIDGenerator::next() +{ + u64 rand1 = m_uniform(m_rand); + u64 rand2 = m_uniform(m_rand); + + std::array bytes; + std::memcpy(&bytes[0], reinterpret_cast(&rand1), 8); + std::memcpy(&bytes[8], reinterpret_cast(&rand2), 8); + return MyGUID{bytes}; +} diff --git a/src/guid.h b/src/guid.h new file mode 100644 index 000000000..87ea2ccf7 --- /dev/null +++ b/src/guid.h @@ -0,0 +1,45 @@ +// Luanti +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright (C) 2024 SFENCE + +#pragma once + +#include "irrlichttypes.h" +#include "util/basic_macros.h" +#include +#include +#include + +class ServerEnvironment; + +/** + * A global unique identifier. + * It is global because it stays valid forever. + * It is unique because there are no collisions. + */ +struct MyGUID { + std::array bytes; + + std::string base64() const; + void serialize(std::ostringstream &os) const; + void deSerialize(std::istream &is); +}; + +class GUIDGenerator { + DISABLE_CLASS_COPY(GUIDGenerator) + +public: + + GUIDGenerator(); + + /** + * Generates the next GUID, which it will never return again. + * @return the new GUID + */ + MyGUID next(); + +private: + + std::random_device m_rand; + std::uniform_int_distribution m_uniform; +}; diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index ba931b22a..336ffc25f 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -406,17 +406,17 @@ void ScriptApiBase::setOriginFromTableRaw(int index, const char *fxn) /* * How ObjectRefs are handled in Lua: * When an active object is created, an ObjectRef is created on the Lua side - * and stored in core.object_refs[id]. + * and stored in core.object_refs[id] and in core.objects_by_guids[GUID]. * Methods that require an ObjectRef to a certain object retrieve it from that * table instead of creating their own.(*) * When an active object is removed, the existing ObjectRef is invalidated - * using ::set_null() and removed from the core.object_refs table. + * using ::set_null() and removed from the core.object_refs and + * core.object_by_guids tables. * (*) An exception to this are NULL ObjectRefs and anonymous ObjectRefs * for objects without ID. * It's unclear what the latter are needed for and their use is problematic * since we lose control over the ref and the contained pointer. */ - void ScriptApiBase::addObjectReference(ServerActiveObject *cobj) { SCRIPTAPI_PRECHECKHEADER @@ -434,7 +434,18 @@ void ScriptApiBase::addObjectReference(ServerActiveObject *cobj) // object_refs[id] = object lua_pushinteger(L, cobj->getId()); // Push id - lua_pushvalue(L, object); // Copy object to top of stack + lua_pushvalue(L, object); + lua_settable(L, objectstable); + + // Get core.objects_by_guid table + lua_getglobal(L, "core"); + lua_getfield(L, -1, "objects_by_guid"); + luaL_checktype(L, -1, LUA_TTABLE); + objectstable = lua_gettop(L); + + // objects_by_guid[guid] = object + lua_pushstring(L, cobj->getGUID().c_str()); + lua_pushvalue(L, object); lua_settable(L, objectstable); } @@ -445,6 +456,7 @@ void ScriptApiBase::removeObjectReference(ServerActiveObject *cobj) // Get core.object_refs table lua_getglobal(L, "core"); + int core = lua_gettop(L); lua_getfield(L, -1, "object_refs"); luaL_checktype(L, -1, LUA_TTABLE); int objectstable = lua_gettop(L); @@ -460,6 +472,16 @@ void ScriptApiBase::removeObjectReference(ServerActiveObject *cobj) lua_pushinteger(L, cobj->getId()); // Push id lua_pushnil(L); lua_settable(L, objectstable); + + // Get core.objects_by_guid + lua_getfield(L, core, "objects_by_guid"); + luaL_checktype(L, -1, LUA_TTABLE); + objectstable = lua_gettop(L); + + // Set objects_by_guid[guid] = nil + lua_pushstring(L, cobj->getGUID().c_str()); + lua_pushnil(L); + lua_settable(L, objectstable); } void ScriptApiBase::objectrefGetOrCreate(lua_State *L, ServerActiveObject *cobj) diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 19c513dd3..78cf2a983 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -119,6 +119,20 @@ int ObjectRef::l_is_valid(lua_State *L) return 1; } +// get_guid() +int ObjectRef::l_get_guid(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkObject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + std::string guid = sao->getGUID(); + lua_pushlstring(L, guid.c_str(), guid.size()); + return 1; +} + // get_pos(self) int ObjectRef::l_get_pos(lua_State *L) { @@ -2836,6 +2850,7 @@ luaL_Reg ObjectRef::methods[] = { // ServerActiveObject luamethod(ObjectRef, remove), luamethod(ObjectRef, is_valid), + luamethod(ObjectRef, get_guid), luamethod_aliased(ObjectRef, get_pos, getpos), luamethod_aliased(ObjectRef, set_pos, setpos), luamethod(ObjectRef, add_pos), diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index f97d7d2da..5a3db157a 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -61,6 +61,9 @@ private: // is_valid(self) static int l_is_valid(lua_State *L); + // get_guid() + static int l_get_guid(lua_State *L); + // get_pos(self) static int l_get_pos(lua_State *L); diff --git a/src/script/scripting_server.cpp b/src/script/scripting_server.cpp index f30def03d..e13178eae 100644 --- a/src/script/scripting_server.cpp +++ b/src/script/scripting_server.cpp @@ -62,6 +62,9 @@ ServerScripting::ServerScripting(Server* server): lua_newtable(L); lua_setfield(L, -2, "object_refs"); + lua_newtable(L); + lua_setfield(L, -2, "objects_by_guid"); + lua_newtable(L); lua_setfield(L, -2, "luaentities"); diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp index 0ad3daba6..261783f6e 100644 --- a/src/server/luaentity_sao.cpp +++ b/src/server/luaentity_sao.cpp @@ -50,7 +50,14 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos, const std::string &d rotation.X = readF1000(is); rotation.Z = readF1000(is); - // if (version2 < 2) + if (version2 < 2) { + m_guid = env->getGUIDGenerator().next(); + break; + } + + m_guid.deSerialize(is); + + // if (version2 < 3) // break; // break; @@ -70,6 +77,14 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos, const std::string &d m_rotation = rotation; } +LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos, const std::string &name, + const std::string &state) : + UnitSAO(env, pos), + m_init_name(name), m_init_state(state), + m_guid(env->getGUIDGenerator().next()) +{ +} + LuaEntitySAO::~LuaEntitySAO() { if(m_registered){ @@ -294,11 +309,13 @@ void LuaEntitySAO::getStaticData(std::string *result) const writeF1000(os, m_rotation.Y); // version2. Increase this variable for new values - writeU8(os, 1); // PROTOCOL_VERSION >= 37 + writeU8(os, 2); // PROTOCOL_VERSION >= 37 writeF1000(os, m_rotation.X); writeF1000(os, m_rotation.Z); + m_guid.serialize(os); + // *result = os.str(); @@ -414,6 +431,13 @@ u16 LuaEntitySAO::getHP() const return m_hp; } +std::string LuaEntitySAO::getGUID() +{ + // The "@" ensures that entity GUIDs are easily recognizable + // and makes it obvious that they can't collide with player names. + return "@" + m_guid.base64(); +} + void LuaEntitySAO::setVelocity(v3f velocity) { m_velocity = velocity; diff --git a/src/server/luaentity_sao.h b/src/server/luaentity_sao.h index 9ea1d3f22..2a46fbc4f 100644 --- a/src/server/luaentity_sao.h +++ b/src/server/luaentity_sao.h @@ -6,6 +6,7 @@ #pragma once #include "unit_sao.h" +#include "guid.h" class LuaEntitySAO : public UnitSAO { @@ -15,11 +16,7 @@ public: LuaEntitySAO(ServerEnvironment *env, v3f pos, const std::string &data); // Used by the Lua API LuaEntitySAO(ServerEnvironment *env, v3f pos, const std::string &name, - const std::string &state) : - UnitSAO(env, pos), - m_init_name(name), m_init_state(state) - { - } + const std::string &state); ~LuaEntitySAO(); ActiveObjectType getType() const { return ACTIVEOBJECT_TYPE_LUAENTITY; } @@ -47,6 +44,7 @@ public: void setHP(s32 hp, const PlayerHPChangeReason &reason); u16 getHP() const; + std::string getGUID() override; /* LuaEntitySAO-specific */ void setVelocity(v3f velocity); @@ -86,6 +84,8 @@ private: std::string m_init_state; bool m_registered = false; + MyGUID m_guid; + v3f m_velocity; v3f m_acceleration; diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 068b2b29f..1334443a1 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -14,6 +14,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t p bool is_singleplayer): UnitSAO(env_, v3f(0,0,0)), m_player(player_), + m_player_name(player_->getName()), m_peer_id_initial(peer_id_), m_is_singleplayer(is_singleplayer) { diff --git a/src/server/player_sao.h b/src/server/player_sao.h index a19177a7e..a75f2bd3c 100644 --- a/src/server/player_sao.h +++ b/src/server/player_sao.h @@ -78,6 +78,7 @@ public: void addPos(const v3f &added_pos) override; void moveTo(v3f pos, bool continuous) override; void setPlayerYaw(const float yaw); + std::string getGUID() override { return m_player_name; } // Data should not be sent at player initialization void setPlayerYawAndSend(const float yaw); void setLookPitch(const float pitch); @@ -182,6 +183,7 @@ private: std::string generateUpdatePhysicsOverrideCommand() const; RemotePlayer *m_player = nullptr; + std::string m_player_name; ///< used as GUID session_t m_peer_id_initial = 0; ///< only used to initialize RemotePlayer // Cheat prevention diff --git a/src/server/serveractiveobject.h b/src/server/serveractiveobject.h index da3dc17bd..ba7863420 100644 --- a/src/server/serveractiveobject.h +++ b/src/server/serveractiveobject.h @@ -141,6 +141,10 @@ public: virtual u16 getHP() const { return 0; } + /// Always returns the same unique string for the same object. + /// Because these strings are very short, copying them is not expensive. + virtual std::string getGUID() = 0; + virtual void setArmorGroups(const ItemGroupList &armor_groups) {} virtual const ItemGroupList &getArmorGroups() const diff --git a/src/serverenvironment.h b/src/serverenvironment.h index 04153e944..b080e0a9f 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -10,6 +10,8 @@ #include "activeobject.h" #include "environment.h" #include "servermap.h" +#include "guid.h" +#include "map.h" #include "settings.h" #include "server/activeobjectmgr.h" #include "server/blockmodifier.h" @@ -123,6 +125,9 @@ public: float getSendRecommendedInterval() { return m_recommended_send_interval; } + GUIDGenerator & getGUIDGenerator() + { return m_guid_generator; } + // Save players void saveLoadedPlayers(bool force = false); void savePlayer(RemotePlayer *player); @@ -357,6 +362,7 @@ private: server::ActiveObjectMgr m_ao_manager; // on_mapblocks_changed map event receiver OnMapblocksChangedReceiver m_on_mapblocks_changed_receiver; + GUIDGenerator m_guid_generator; // Outgoing network message buffer for active objects std::queue m_active_object_messages; // Some timers diff --git a/src/unittest/mock_serveractiveobject.h b/src/unittest/mock_serveractiveobject.h index 9d886b4eb..613c2c420 100644 --- a/src/unittest/mock_serveractiveobject.h +++ b/src/unittest/mock_serveractiveobject.h @@ -2,16 +2,31 @@ // SPDX-License-Identifier: LGPL-2.1-or-later // Copyright (C) 2022 Minetest core developers & community +#include "guid.h" +#include "serverenvironment.h" #include +#include class MockServerActiveObject : public ServerActiveObject { public: MockServerActiveObject(ServerEnvironment *env = nullptr, v3f p = v3f()) : - ServerActiveObject(env, p) {} + ServerActiveObject(env, p) + { + if (env) + m_guid = "mock:" + env->getGUIDGenerator().next().base64(); + } virtual ActiveObjectType getType() const { return ACTIVEOBJECT_TYPE_TEST; } virtual bool getCollisionBox(aabb3f *toset) const { return false; } virtual bool getSelectionBox(aabb3f *toset) const { return false; } virtual bool collideWithObjects() const { return false; } + virtual std::string getGUID() + { + assert(!m_guid.empty()); + return m_guid; + } + +private: + std::string m_guid; }; From 0b423bdb223d43516863a7bc23adc874d570b061 Mon Sep 17 00:00:00 2001 From: SFENCE Date: Mon, 9 Jun 2025 19:09:16 +0200 Subject: [PATCH 2/7] Apply review. --- doc/lua_api.md | 14 +++++++------- doc/world_format.md | 2 +- games/devtest/mods/unittests/entity.lua | 12 ++++-------- games/devtest/mods/unittests/player.lua | 2 +- src/script/cpp_api/s_base.cpp | 4 +++- src/server/luaentity_sao.cpp | 2 +- src/server/serveractiveobject.h | 4 ++-- 7 files changed, 19 insertions(+), 21 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index d387412b9..c03220557 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -7849,12 +7849,12 @@ Global tables * Values in this table may be modified directly. Note: changes to initial properties will only affect entities spawned afterwards, as they are only read when spawning. -* `core.object_refs` - * Map of active object references, indexed by active object id - * Obsolete: Use `core.objects_by_guid` instead. - GUIDs are strictly more useful than active object IDs. * `core.objects_by_guid` * Map of active object references, indexed by object GUID +* `core.object_refs` + * **Obsolete:** Use `core.objects_by_guid` instead. + GUIDs are strictly more useful than active object IDs. + * Map of active object references, indexed by active object id * `core.luaentities` * Map of Lua entities, indexed by active object id * `core.registered_abms` @@ -8555,10 +8555,10 @@ child will follow movement and rotation of that bone. * `get_guid()`: returns a global unique identifier (a string) * For players, this is a player name. * For Lua entities, this is a uniquely generated string, guaranteed not to collide with player names. - * Example: `@bGh3p2AbRE29Mb4biqX6OA` + * example: `@bGh3p2AbRE29Mb4biqX6OA` * GUIDs only use printable ASCII characters. - * GUIDs are persisted internally between object reloads; their format is guaranteed not to change. - Thus you can store GUIDs to identify objects persistently. + * GUIDs persist internally between object reloads; their format is guaranteed not to change. + Thus you can use the GUID to identify an object in a particular world online nad offline. #### Lua entity only (no-op for other objects) diff --git a/doc/world_format.md b/doc/world_format.md index c7bd0f6cd..555c0793d 100644 --- a/doc/world_format.md +++ b/doc/world_format.md @@ -598,7 +598,7 @@ Since protocol version 37: * `s32` pitch * 1000 * `s32` roll * 1000 * if version2 >= 2: - * u8[16] guid + * `u8[16]` guid # Itemstring Format diff --git a/games/devtest/mods/unittests/entity.lua b/games/devtest/mods/unittests/entity.lua index eed6ff334..dfbe54115 100644 --- a/games/devtest/mods/unittests/entity.lua +++ b/games/devtest/mods/unittests/entity.lua @@ -257,19 +257,15 @@ end unittests.register("test_item_drop", test_item_drop, {map=true}) local function test_entity_guid(_, pos) - log = {} - - local obj0 = core.add_entity(pos, "unittests:callbacks") - check_log({"on_activate(0)"}) - local obj1 = core.add_entity(pos, "unittests:callbacks") - check_log({"on_activate(0)"}) + local obj0 = core.add_entity(pos, "unittests:dummy") + local obj1 = core.add_entity(pos, "unittests:dummy") + assert(obj0 ~= obj1) + assert(obj0:get_guid() ~= obj1:get_guid()) assert(core.objects_by_guid[obj0:get_guid()] == obj0) assert(core.objects_by_guid[obj1:get_guid()] == obj1) obj0:remove() - check_log({"on_deactivate(true)"}) obj1:remove() - check_log({"on_deactivate(true)"}) end unittests.register("test_entity_guid", test_entity_guid, {map=true}) diff --git a/games/devtest/mods/unittests/player.lua b/games/devtest/mods/unittests/player.lua index b6dcec86f..cc4b08b31 100644 --- a/games/devtest/mods/unittests/player.lua +++ b/games/devtest/mods/unittests/player.lua @@ -209,6 +209,6 @@ unittests.register("test_player_hotbar_clamp", run_player_hotbar_clamp_tests, {p -- Player get GUID -- local function test_player_guid_tests(player) - assert(player:get_guid()==player:get_player_name()) + assert(player:get_guid() == player:get_player_name()) end unittests.register("test_player_guid", test_player_guid_tests, {player=true}) diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index 336ffc25f..d6b041254 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -444,7 +444,9 @@ void ScriptApiBase::addObjectReference(ServerActiveObject *cobj) objectstable = lua_gettop(L); // objects_by_guid[guid] = object - lua_pushstring(L, cobj->getGUID().c_str()); + auto guid = cobj->getGUID(); + assert(!guid.empty()); + lua_pushstring(L, guid.c_str()); lua_pushvalue(L, object); lua_settable(L, objectstable); } diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp index 261783f6e..368bf9eeb 100644 --- a/src/server/luaentity_sao.cpp +++ b/src/server/luaentity_sao.cpp @@ -309,7 +309,7 @@ void LuaEntitySAO::getStaticData(std::string *result) const writeF1000(os, m_rotation.Y); // version2. Increase this variable for new values - writeU8(os, 2); // PROTOCOL_VERSION >= 37 + writeU8(os, 2); writeF1000(os, m_rotation.X); writeF1000(os, m_rotation.Z); diff --git a/src/server/serveractiveobject.h b/src/server/serveractiveobject.h index ba7863420..3fe8eb8b5 100644 --- a/src/server/serveractiveobject.h +++ b/src/server/serveractiveobject.h @@ -141,8 +141,8 @@ public: virtual u16 getHP() const { return 0; } - /// Always returns the same unique string for the same object. - /// Because these strings are very short, copying them is not expensive. + /// @brief Returns an unique ID for this object (persistent across unload, server restarts). + /// @note Because these strings are very short, copying them is not expensive. virtual std::string getGUID() = 0; virtual void setArmorGroups(const ItemGroupList &armor_groups) From 07bbe4f4547f367ca19deecce35df668e5639287 Mon Sep 17 00:00:00 2001 From: SFENCE Date: Mon, 9 Jun 2025 19:17:51 +0200 Subject: [PATCH 3/7] Apply review. --- src/server/player_sao.cpp | 6 +++++- src/server/player_sao.h | 3 +-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 1334443a1..83fca7ae2 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -14,7 +14,6 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t p bool is_singleplayer): UnitSAO(env_, v3f(0,0,0)), m_player(player_), - m_player_name(player_->getName()), m_peer_id_initial(peer_id_), m_is_singleplayer(is_singleplayer) { @@ -408,6 +407,11 @@ void PlayerSAO::setPlayerYaw(const float yaw) UnitSAO::setRotation(rotation); } +std::string PlayerSAO::getGUID() +{ + return m_player->getName(); +} + void PlayerSAO::setFov(const float fov) { if (m_player && fov != m_fov) diff --git a/src/server/player_sao.h b/src/server/player_sao.h index a75f2bd3c..492971ce9 100644 --- a/src/server/player_sao.h +++ b/src/server/player_sao.h @@ -78,7 +78,7 @@ public: void addPos(const v3f &added_pos) override; void moveTo(v3f pos, bool continuous) override; void setPlayerYaw(const float yaw); - std::string getGUID() override { return m_player_name; } + std::string getGUID() override; // Data should not be sent at player initialization void setPlayerYawAndSend(const float yaw); void setLookPitch(const float pitch); @@ -183,7 +183,6 @@ private: std::string generateUpdatePhysicsOverrideCommand() const; RemotePlayer *m_player = nullptr; - std::string m_player_name; ///< used as GUID session_t m_peer_id_initial = 0; ///< only used to initialize RemotePlayer // Cheat prevention From d393e7094327b7c110d78639dc90f91681152c9b Mon Sep 17 00:00:00 2001 From: SFENCE Date: Mon, 9 Jun 2025 19:45:57 +0200 Subject: [PATCH 4/7] Move guids to util. --- src/CMakeLists.txt | 1 - src/server/luaentity_sao.h | 2 +- src/serverenvironment.h | 2 +- src/unittest/mock_serveractiveobject.h | 2 +- src/util/CMakeLists.txt | 1 + src/{ => util}/guid.cpp | 0 src/{ => util}/guid.h | 0 7 files changed, 4 insertions(+), 4 deletions(-) rename src/{ => util}/guid.cpp (100%) rename src/{ => util}/guid.h (100%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2bca969c8..1231f49ba 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -455,7 +455,6 @@ set(common_SRCS environment.cpp filesys.cpp gettext.cpp - guid.cpp inventorymanager.cpp itemdef.cpp light.cpp diff --git a/src/server/luaentity_sao.h b/src/server/luaentity_sao.h index 2a46fbc4f..72ae8ffe3 100644 --- a/src/server/luaentity_sao.h +++ b/src/server/luaentity_sao.h @@ -6,7 +6,7 @@ #pragma once #include "unit_sao.h" -#include "guid.h" +#include "util/guid.h" class LuaEntitySAO : public UnitSAO { diff --git a/src/serverenvironment.h b/src/serverenvironment.h index b080e0a9f..5f0b9deaa 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -10,7 +10,7 @@ #include "activeobject.h" #include "environment.h" #include "servermap.h" -#include "guid.h" +#include "util/guid.h" #include "map.h" #include "settings.h" #include "server/activeobjectmgr.h" diff --git a/src/unittest/mock_serveractiveobject.h b/src/unittest/mock_serveractiveobject.h index 613c2c420..01dcc6469 100644 --- a/src/unittest/mock_serveractiveobject.h +++ b/src/unittest/mock_serveractiveobject.h @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LGPL-2.1-or-later // Copyright (C) 2022 Minetest core developers & community -#include "guid.h" +#include "util/guid.h" #include "serverenvironment.h" #include #include diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 7d5ac48fb..7fc662571 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -5,6 +5,7 @@ set(util_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/colorize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp ${CMAKE_CURRENT_SOURCE_DIR}/enriched_string.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/guid.cpp ${CMAKE_CURRENT_SOURCE_DIR}/hashing.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ieee_float.cpp ${CMAKE_CURRENT_SOURCE_DIR}/metricsbackend.cpp diff --git a/src/guid.cpp b/src/util/guid.cpp similarity index 100% rename from src/guid.cpp rename to src/util/guid.cpp diff --git a/src/guid.h b/src/util/guid.h similarity index 100% rename from src/guid.h rename to src/util/guid.h From e93c534065321fdeab4e28686fcb1e3cd2e67c9d Mon Sep 17 00:00:00 2001 From: SFENCE Date: Mon, 9 Jun 2025 20:14:30 +0200 Subject: [PATCH 5/7] Apply review. --- src/util/guid.cpp | 2 +- src/util/guid.h | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/util/guid.cpp b/src/util/guid.cpp index f9580458c..97f20607f 100644 --- a/src/util/guid.cpp +++ b/src/util/guid.cpp @@ -16,7 +16,7 @@ std::string MyGUID::base64() const return base64_encode(std::string_view(&bytes[0], bytes.size())); } -void MyGUID::serialize(std::ostringstream &os) const +void MyGUID::serialize(std::ostream &os) const { os.write(&bytes[0], bytes.size()); } diff --git a/src/util/guid.h b/src/util/guid.h index 87ea2ccf7..8bfff7454 100644 --- a/src/util/guid.h +++ b/src/util/guid.h @@ -17,15 +17,17 @@ class ServerEnvironment; * It is global because it stays valid forever. * It is unique because there are no collisions. */ -struct MyGUID { +struct MyGUID +{ std::array bytes; std::string base64() const; - void serialize(std::ostringstream &os) const; + void serialize(std::ostream &os) const; void deSerialize(std::istream &is); }; -class GUIDGenerator { +class GUIDGenerator +{ DISABLE_CLASS_COPY(GUIDGenerator) public: From 0fec74cbed06339130605b712fc0025742cd5d92 Mon Sep 17 00:00:00 2001 From: SFENCE Date: Mon, 9 Jun 2025 20:35:44 +0200 Subject: [PATCH 6/7] Revert "Apply review." (remove m_player_name from player_sao) This reverts commit 07bbe4f4547f367ca19deecce35df668e5639287. --- src/server/player_sao.cpp | 6 +----- src/server/player_sao.h | 3 ++- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 83fca7ae2..1334443a1 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -14,6 +14,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t p bool is_singleplayer): UnitSAO(env_, v3f(0,0,0)), m_player(player_), + m_player_name(player_->getName()), m_peer_id_initial(peer_id_), m_is_singleplayer(is_singleplayer) { @@ -407,11 +408,6 @@ void PlayerSAO::setPlayerYaw(const float yaw) UnitSAO::setRotation(rotation); } -std::string PlayerSAO::getGUID() -{ - return m_player->getName(); -} - void PlayerSAO::setFov(const float fov) { if (m_player && fov != m_fov) diff --git a/src/server/player_sao.h b/src/server/player_sao.h index 492971ce9..a75f2bd3c 100644 --- a/src/server/player_sao.h +++ b/src/server/player_sao.h @@ -78,7 +78,7 @@ public: void addPos(const v3f &added_pos) override; void moveTo(v3f pos, bool continuous) override; void setPlayerYaw(const float yaw); - std::string getGUID() override; + std::string getGUID() override { return m_player_name; } // Data should not be sent at player initialization void setPlayerYawAndSend(const float yaw); void setLookPitch(const float pitch); @@ -183,6 +183,7 @@ private: std::string generateUpdatePhysicsOverrideCommand() const; RemotePlayer *m_player = nullptr; + std::string m_player_name; ///< used as GUID session_t m_peer_id_initial = 0; ///< only used to initialize RemotePlayer // Cheat prevention From 77f05b5f735449fb306f3af97fe812c6bf7408d4 Mon Sep 17 00:00:00 2001 From: SFENCE Date: Mon, 23 Jun 2025 07:39:00 +0200 Subject: [PATCH 7/7] Review apply. --- src/server/player_sao.h | 1 + src/util/guid.cpp | 7 ++----- src/util/guid.h | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/server/player_sao.h b/src/server/player_sao.h index a75f2bd3c..030ed2516 100644 --- a/src/server/player_sao.h +++ b/src/server/player_sao.h @@ -183,6 +183,7 @@ private: std::string generateUpdatePhysicsOverrideCommand() const; RemotePlayer *m_player = nullptr; + // Replace with m_player->getName method from m_player cause SIGSEG std::string m_player_name; ///< used as GUID session_t m_peer_id_initial = 0; ///< only used to initialize RemotePlayer diff --git a/src/util/guid.cpp b/src/util/guid.cpp index 97f20607f..dda92fee9 100644 --- a/src/util/guid.cpp +++ b/src/util/guid.cpp @@ -29,11 +29,8 @@ void MyGUID::deSerialize(std::istream &is) GUIDGenerator::GUIDGenerator() : m_uniform(0, UINT64_MAX) { - if (m_rand.entropy() <= 0.01) - warningstream << - "The system's provided random generator reports low entropy." - "GUID generator can be affected. Suggest a system upgrade." - << std::endl; + std::random_device rd; + m_rand.seed(rd()); } MyGUID GUIDGenerator::next() diff --git a/src/util/guid.h b/src/util/guid.h index 8bfff7454..e9570d889 100644 --- a/src/util/guid.h +++ b/src/util/guid.h @@ -42,6 +42,6 @@ public: private: - std::random_device m_rand; + std::mt19937_64 m_rand; std::uniform_int_distribution m_uniform; };