Skip to content

Commit

Permalink
Change GUId generator based on feedback.
Browse files Browse the repository at this point in the history
  • Loading branch information
sfence committed Jan 3, 2024
1 parent 3a3efb1 commit a71409b
Show file tree
Hide file tree
Showing 18 changed files with 122 additions and 303 deletions.
10 changes: 7 additions & 3 deletions doc/lua_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -7614,16 +7614,20 @@ child will follow movement and rotation of that bone.
-- Default: false
}
```
* `get_guid()`: returns a global unique identifier (a string)
* For players a global unique identiticator is a player name.
* For non-player objects, it is unique generated string.

#### Lua entity only (no-op for other objects)

* `remove()`: remove object
* The object is removed after returning from Lua. However the `ObjectRef`
itself instantly becomes unusable with all further method calls having
no effect and returning `nil`.
* `get_guid()`: returns a global unique identifier (a string)
* An object keeps its guid forever, even over server restarts.
* The same guid will never be reused for a different object.
* `set_guid(guid)`:
* Set a global unique identifier string of entity.
* Is valid to be called only in `on_activate` callback.
* Should be used ONLY for restoring saved guid from staticdata!!!
* `set_velocity(vel)`
* Sets the velocity
* `vel` is a vector, e.g. `{x=0.0, y=2.3, z=1.0}`
Expand Down
2 changes: 1 addition & 1 deletion games/devtest/mods/unittests/entity.lua
Original file line number Diff line number Diff line change
Expand Up @@ -141,5 +141,5 @@ local function test_entity_guid(_, pos)

obj:remove()
end
unittests.register("test_guid", test_entity_guid, {map=true})
unittests.register("test_entity_guid", test_entity_guid, {map=true})

7 changes: 7 additions & 0 deletions games/devtest/mods/unittests/player.lua
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,10 @@ local function run_player_add_pos_tests(player)
end
unittests.register("test_player_add_pos", run_player_add_pos_tests, {player=true})

--
-- Player get uuid
--
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})
86 changes: 6 additions & 80 deletions src/guid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,90 +20,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guid.h"
#include <sstream>

#define DIGITS_PER_U64 19
#define MAX_NUM_IN_U64 9999999999999999999ul
#include "serverenvironment.h"

GUIdGenerator::Validity GUIdGenerator::seed(const GUId &first)
GUId GUIdGenerator::next(const std::string &prefix)
{
Validity v = getValidity(first);
if (v != Valid)
return v;
std::stringstream s_guid;

m_next_v.clear();
s_guid << prefix << std::hex << m_next << '-' << std::hex << m_env->getGameTime();

size_t num_len = first.size() - 6;
m_next++;

while (num_len > DIGITS_PER_U64) {
num_len -= DIGITS_PER_U64;
m_next_v.push_back(std::stoull(first.substr(6 + num_len, DIGITS_PER_U64)));
}

m_next_v.push_back(std::stoull(first.substr(6, num_len)));

return Valid;
}

GUId GUIdGenerator::generateNext()
{
if (m_next_v.empty())
return "";

GUId ret = peekNext();

// increment m_next_v

if (m_next_v[0] < MAX_NUM_IN_U64) {
m_next_v[0] += 1ul;

} else { // very unlikely
m_next_v[0] = 0ul;
size_t s = m_next_v.size();
for (size_t i = 1; true; i++) {
if (i == s) {
m_next_v.push_back(1ul);
break;
}
if (m_next_v[i] < MAX_NUM_IN_U64) {
m_next_v[i] += 1ul;
break;
}
// very unlikely
m_next_v[i] = 0ul;
continue;
}
}

return ret;
return s_guid.str();
}

GUId GUIdGenerator::peekNext() const
{
if (m_next_v.empty())
return "";

std::ostringstream id_strm;

id_strm << "v1guid";

for (auto it = m_next_v.rbegin(); it != m_next_v.rend(); it++)
id_strm << *it;

return id_strm.str();
}

GUIdGenerator::Validity GUIdGenerator::getValidity(const GUId &id)
{
if (id.empty())
return Old;

size_t s = id.size();

if (s < 7 || id.substr(0, 6) != "v1guid" || id[6] == '0')
return Invalid;

for (size_t i = 7; i < s; i++)
if (!isdigit(id[i]))
return Invalid;

return Valid;
}
74 changes: 24 additions & 50 deletions src/guid.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once

#include "irrlichttypes.h"
#include "util/basic_macros.h"
#include <vector>
#include <string>

class ServerEnvironment;

/**
* A global unique identifier.
* It is global because it stays valid in a world forever.
* It is unique because there are no collisions.
*
* It is a string of the following form:
* "v1guid<deci_num>"
* where <deci_num> should be replaced by a number of decimal digits, with the
* first digit != 0 ("0" and "" are invalid).
* The number after the v is the version number (currently always 1).
*
* There is a partial order for guids:
* guid A is newer than guid B iff
* A's version number is higher than B's and B's version number is 1 or
* A and B both have version number 1 and A's deci_num is higher
*/
typedef std::string GUId;

Expand All @@ -48,59 +40,41 @@ class GUIdGenerator {
public:
/**
* Creates a new uninitialized generator.
* @param env ServerEnvironment where generator is running
* @param prefix Prefix of generated GUId
* @param next next value getted from loadMeta
*/
GUIdGenerator() = default;

~GUIdGenerator() = default;

/**
* The validity of a guid.
* Old means that it was a valid guid in an older version, this includes
* the empty string.
* Invalid guids are strings that have an invalid form, eg. "v1guid1a23", but
* also guids of newer versions.
* Everything else is valid.
*/
enum Validity {
Valid,
Old,
Invalid,
};

/**
* Seeds the generator with the first guid to return next.
* Can fail for invalid seeds.
* Do only seed with guids of which you know that itself and newer guids are
* not used yet.
* @param first the first guid that should be returned by generateNext
* @return the validity of the given id; Valid iff the generator is seeded
*/
Validity seed(const GUId &first);
DISABLE_CLASS_COPY(GUIdGenerator)

/**
* Generates the next guid, which it will never return again.
* @return the new guid, or "" if the generator is not yet initialized
*/
GUId generateNext();
GUId next(const std::string &prefix);

private:
/**
* Returns the guid that would be generated next.
* @return the next guid
* Set ServerEnvironment,
* @param env ServerEnvironment.
*/
GUId peekNext() const;

void setServerEnvironment(ServerEnvironment *env) { m_env = env; };
/**
* Returns the validity for a guid.
* @param id the guid
* @return the validity of id
* Set state to defined/known state,
* @param next New state value.
*/
static Validity getValidity(const GUId &id);

private:
void setState(u32 state) { m_next = state; };
/**
* Internal representation of the next guid.
* Each u64 stores 19 decimal digits of the next deci_num.
* The u64s are ordered little-endian-wise.
* Get state,
* @return Return aactual state value.
*/
std::vector<u64> m_next_v;
u32 getState() const { return m_next; };

private:
ServerEnvironment *m_env;
u32 m_next;

friend class ServerEnvironment;
};
19 changes: 19 additions & 0 deletions src/script/lua_api/l_object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1132,6 +1132,25 @@ int ObjectRef::l_set_sprite(lua_State *L)
return 0;
}

// set_guid(self, guid)
int ObjectRef::l_set_guid(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkObject<ObjectRef>(L, 1);
LuaEntitySAO *entitysao = getluaobject(ref);
if (entitysao == nullptr)
return 0;

std::string guid = readParam<std::string>(L, 2, "");

if (!guid.empty())
lua_pushboolean(L, entitysao->setGuid(guid));
else
lua_pushboolean(L, false);

return 1;
}

// DEPRECATED
// get_entity_name(self)
int ObjectRef::l_get_entity_name(lua_State *L)
Expand Down
3 changes: 3 additions & 0 deletions src/script/lua_api/l_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,9 @@ class ObjectRef : public ModApiBase {

// set_sprite(self, start_frame, num_frames, framelength, select_x_by_camera)
static int l_set_sprite(lua_State *L);

// set_guid(self, guid)
static int l_set_guid(lua_State *L);

// DEPRECATED
// get_entity_name(self)
Expand Down
38 changes: 22 additions & 16 deletions src/server/luaentity_sao.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos, const std::string &d
u16 hp = 1;
v3f velocity;
v3f rotation;
std::string guid;

while (!data.empty()) { // breakable, run for one iteration
std::istringstream is(data, std::ios::binary);
Expand Down Expand Up @@ -66,11 +65,7 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos, const std::string &d
rotation.X = readF1000(is);
rotation.Z = readF1000(is);

if (version2 < 2)
break;
guid = deSerializeString32(is);

// if (version2 < 3)
// if (version2 < 2)
// break;
// <read new values>
break;
Expand All @@ -88,20 +83,13 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos, const std::string &d
m_hp = hp;
m_velocity = velocity;
m_rotation = rotation;
if (!guid.empty()) {
m_guid = guid;
} else {
m_guid = env->generateNextLuaentGUId();
m_force_write_staticdata = true;
}
}

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->generateNextLuaentGUId();
}

LuaEntitySAO::~LuaEntitySAO()
Expand Down Expand Up @@ -132,6 +120,10 @@ void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
// Activate entity, supplying serialized state
m_env->getScriptIface()->
luaentity_Activate(m_id, m_init_state, dtime_s);
// if Activate callback does not set guid, set it.
if (m_guid.empty()) {
getGuid();
}
} else {
// It's an unknown object
// Use entitystring as infotext for debugging
Expand Down Expand Up @@ -323,13 +315,11 @@ 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, 1); // PROTOCOL_VERSION >= 37

writeF1000(os, m_rotation.X);
writeF1000(os, m_rotation.Z);

os << serializeString32(m_guid);

// <write new values>

*result = os.str();
Expand Down Expand Up @@ -443,6 +433,22 @@ u16 LuaEntitySAO::getHP() const
return m_hp;
}

bool LuaEntitySAO::setGuid(std::string &guid)
{
if (m_guid.empty()) {
m_guid = guid;
return true;
}
return false;
}
std::string LuaEntitySAO::getGuid()
{
if (m_guid.empty()) {
m_guid = m_env->getGUIdGenerator().next(std::string("@"));
}
return m_guid;
}

void LuaEntitySAO::setVelocity(v3f velocity)
{
m_velocity = velocity;
Expand Down
3 changes: 2 additions & 1 deletion src/server/luaentity_sao.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ class LuaEntitySAO : public UnitSAO

void setHP(s32 hp, const PlayerHPChangeReason &reason);
u16 getHP() const;
std::string getGuid() const override { return m_guid; }
bool setGuid(std::string &guid);
std::string getGuid() override;

/* LuaEntitySAO-specific */
void setVelocity(v3f velocity);
Expand Down
4 changes: 2 additions & 2 deletions src/server/player_sao.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -414,9 +414,9 @@ void PlayerSAO::setPlayerYaw(const float yaw)
UnitSAO::setRotation(rotation);
}

std::string PlayerSAO::getGuid() const
std::string PlayerSAO::getGuid()
{
return std::string("vpguid") + m_player->getName();
return m_player->getName();
}

void PlayerSAO::setFov(const float fov)
Expand Down
Loading

0 comments on commit a71409b

Please sign in to comment.