Generating UUIDs
UUIDs are an important part of Alacrity as they are used to relate entities to each other as well as relate buffs with entities, modifiers and conditions with buffs etc.
The most common way to generate an UUID is to generate a version 4 UUID
that is -a fully randomly generated UUID- this can be done using the
rust [uuid] crate's new_v4() function or by any other means like for
example, using the uuidgen command line tool on UNIX (linux, BSD, MacOS...).
Creating random UUIDs is the most secure and easy solution and usually you will have no problems while using completely random UUIDs as buff's IDs. But the thing gets a bit more complicated when one needs to also map these IDs to some game objects that might use just numeric IDs or any other kind of identification mechanism, this is usually more problematic when trying to relate game objects to Alacrity entities, we offer some suggestions in the sections below that could provide an acceptable solution for your own use cases.
Options for Entity UUIDs mapping and storing
Different games will have different needs. If you are starting a new game from scratch and you are using Alacrity, then you might use UUIDs for your in-game objects IDs and just use them to create your Alacrity entities while if you are integrating Alacrity in your already established game you might not be able to use UUIDs to identify your game objects for whatever reason.
Just using UUIDs for all of your IDs
This is the most simple and out of the box solution, just use UUIDs for all of your game objects IDs, then you can just use the same IDs to refer to your Alacrity entities and buffs. How does this works in a real scenario?
So, for example, let's say that a new player comes through your character's
creation system, after they have configured their character selecting
whatever options that are offered to them, the system would proceed to make
a new instance of the player's character game object and persist it. At that
specific point, the system would generate a new random v4 UUID and provide
that same UUID to Alacrity when instantiating a new Entity that represents
that player's attributes and state.
This way, when you need to extract any data from your Alacrity entity in regards of the player's character you could just extract it from whatever storage solution or persistence layer you are using to store Alacrity's data using the character's game object ID as they would match.
Store the Alacrity data into the Game Object
You can also store all of the alacrity data into your game objects, this way you can use whatever system to identify your game objects and you will not care at all about which random IDs you are using for your Alacrity entity instances as you can just reach that information directly from with-in your game object directly from its allocated memory.
This is also an easy approach but that comes with some warnings. If you plan to make use of Alacrity's buffs propagation system, then you will need to put some entity to entity communication layer in top of alacrity, how this layer is implemented is up to you and your game needs. It can be just an in memory instances communication approach or a fully TCP/IP networking solution, it all will depend on your use case and implementation. If you decide to store your entities data directly into your game objects, then your game objects will need to implement a way to listen for Alacrity entity updates and that could complicate your game objects logic unnecessarily.
If you are creating a game on which buffs does not affect each other in any way then this is a perfectly safe solution that is very easy to implement and to work with.
Map your IDs and persist them
A common solution is to just create a key value map where you can just relate your game objects one-to-one to specific Alacrity entities. This can be done with an external key value storage like [redis], [JetStreams KV], [Consul KV] for example, or can be stored in plan text files or any other key value storage solution that persist its data to the local file system.
Follow a deterministic procedure to translate Game Object IDs to Alacrity IDs
This is one of the preferred methods because it does not requires of any additional key value storage system or to hardly couple Alacrity entities to anything else in your game, basically, this can be achieved using UUID version 5 also known as UUID namespace or name-based.
The RFC 4122 lists four different name space IDs that are usually available and ready to use in any UUID library implementation, those are:
NameSpace_DNS- Name string is a fully-qualified domain name, its ID is6ba7b810-9dad-11d1-80b4-00c04fd430c8NameSpace_URL- Name string is a URL, its ID is6ba7b811-9dad-11d1-80b4-00c04fd430c8NameSpace_OID- Name string is an ISO OID, its ID is6ba7b812-9dad-11d1-80b4-00c04fd430c8NameSpace_X500- Name string is an X.500 Domain Name in DER or text output format, its ID is6ba7b814-9dad-11d1-80b4-00c04fd430c8
An example of how to create a new deterministic UUID from a game object that uses sequential integer IDs for identification purposes using the DNS namespace using the Alacrity's Lua API would looks like this
local uuid = alacrity.uuid
-- assume gameObject has been created elsewhere and has an `id` field
local entity_id = uuid.new_v5(uuid.namespace_dns, "com.my_game.game_objects." .. tostring(gameObject.id))
The code above would always generate the same UUID output for a given
gameObject.id so no need map and store the map would be necessary, we could
lookup Alacrity entities in whatever registry or storage we use to persist them
just using the UUID returned by the new_v5 function.
Of course we do not have to use an already defined namespace for this, we could also create our own namespaces and use those instead. We could generate a new random UUID for our namespace and hardcode it or pass it as a config parameter or any other solution and then use that instead.
For example, lets assume that we have hardcoded a Namespace_GameObject with
a value 8c2d4d36-4635-54d9-80d8-d957ba0fb224, we could just modify the example
above and it would produce a consistent and deterministic result always that we
used the same namespace with the game object ID
local uuid = alacrity.uuid
-- assume gameObject and Namespace_GameObject are defined elsewhere
local entity_id = uuid.new_v5(Namespace_GameObject, tostring(gameObject.id))
We could even have different namespaces to refer to entities related to the given game object but that differs in their context. For example, let's imagine that we design our game to have a hero character, a weapon and a relic. Let's also imagine that we model each of these game objects to own their own Alacrity entity. We could define three different namespaces for our game like:
Namespace_Hero = "4bdcd274-7841-48fd-9290-524b96dbe524"
Namespace_Weapon = "a73a6fcd-f499-4c3b-a781-c30d76234d0f"
Namespace_Relic = "e046b816-d6e1-487e-adee-3936120698ca"
Then, we could just use those to refer different Alacrity entities just using the id of our character.
local uuid = alacrity.uuid
---@param gameObjectID integer @ID of the Game Object owning the entity
---@param nameSpace string @NameSpace to determine the entity ID
---@return string
local function resolveEntityID(gameObjectID, namespace)
local entity_id = uuid.new_v5(namespace, tostring(gameObjectID))
return entity_id
end
-- get the hero entity ID (assume gameObject has been defined elsewhere)
local heroEntityID = resolveEntityID(gameObject.id, Namespace_Hero)
-- get the hero weapon entity ID (assume gameObject has been defined elsewhere)
local weaponEntityID = resolveEntityID(gameObject.id, Namespace_Weapon)
-- get the hero relic entity ID (assume gameObject has been defined elsewhere)
local relicEntityID = resolveEntityID(gameObject.id, Namespace_Relic)
The code above would generate always the same three different IDs in a fully deterministic way. Using this technique we can refer to a myriad of Alacrity entity IDs using a single game object ID