Skip to main content

EntityID - Using one value to share many

·693 words·4 mins
Code Entity-Component-System C/Cpp
Table of Contents

Being able to identify something allows to call for something using a form of shorthand, possibly from other arbitrary locations that may have little to no information of the context of what the item actually might be.

The most widely-used way of identifying people is to use either a legal name, social number or email. Through those three items, most individuals can be uniquely identified, even if separately each organization only has their small piece of the picture.

In programming, every item can be identified by a pointer. Many languages do abstract this away from the purview of a programmer, but down in each language’s core, there is a pointer pointing at a section of data, whether on the heap or on the stack. This can often, and typically is often used as a form of unique identification, as each memory can (or should) only be allocated once. This is excellent if objects don’t move about in memory, and are tightly coupled with any split off relationships to other objects. Not only that, but unless the memory is being allocated through a custom allocator, then there is no real control over the values, and thus can only represent a location in memory.

Here, though, that runs up against a bit of a problem, as we may want to use a single value to identify an object, ie entity, that might be made up of various pieces, ie components. Here is a basic set of requirements that have been set out here:

  • Object components might not stay in same location during runtime.
  • Entities should be ‘group’-able, as in entities should have some implicit information embedded in their existance
  • Entity identifier should be as low cost as possible, both in space and operational cost.

A Struct

Another idea is, of course, to use some sort of struct, where the information can be separated to their own concerns pretty easily. However, often due to padding or pre-set type sizes, this may not be as ideal as first believed. If trying to fit within a 32-bit space, then the options are limited to:

  • 32-bit value
  • 2x16-bit values
  • 2x8-bit + 16-bit values
  • 4x8-bit valies Unfortunately, the 16-bit values are just a bit on the small size, storing only ~65k values, and the 8-bit valus only capable of 256 each.

POD (Plain Old Data)

Rather, the best solution, as is usually the case in programming, is to take an existing solution, and modify it to suit one’s needs. Here, we’re going to repurpose the plain old data type, the uint32_t (unsigned int).

By itself, the uint32_t type can only store ~4.2 billion values through the use of all of its 32 bits. But that’s all of the bits. What can be done is to slice off some of those bits to represent a second set of values along with an original, if slightly smaller set.

So, say splitting the value by 8/24 bits. By doing that, we can store two values in that same space, without expanding the required size beyond the integer type’s original 4 bytes. One being 8 bits supporting 256 values and another 24 bit supporting ~16.7 million values.

So that means that here we can split everythong along a group/index for some form of at-a-glance information. As an example, the group id can be used to describe certain traits:

  • 0 Base game data
  • 1 DLC1
  • 2 DLC2
  • 3 Mod: New City Buildings
  • 4 Mod: New NPCs
  • 251 Temporary/Synchronized: Items generated in the session that are to be synchronized when in multiplayer, but not saved across sessions.
  • 252 Temporary/Local: Items generated int he session that are not to be synchronized or saved across sessions.
  • 253 Permanent/Synchronized: Items that are generated in the session that persist across sessions and are to be synchronized in multiplayer.
  • 254 Permanent/Local: Items generated in the session that are to persist across sessions but not multiplayer synchronized.
  • 255 Program: Items for running the program, technically persisting across sessions but not session specific data. ex. handles to audio streams, windows, file handles, etc.

Each of these groups can have upto ~16.7 million items each, which should be more than enough.