Lets call it TinyUID

A system for generating low byte unique names, since i didnt want to make a function to rotate a bee on client side. Even if its a client sided bee. So i can order the player to rotate the bee every frame from server side…

For people who create and modify client sided objects from server side, i guess.

Hope it works nice.

local RunService = game:GetService("RunService")

local charset = { --UTF-8 characters
    "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
    "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
    "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
}

local func = {}

func.TotalUIDs = 0
func.AvailableUIDs = {}
func.UIDtoIndex = {}

local base = #charset
local function encodeNumber(n)
    local str = {}

    repeat
        local remainder = (n % base) + 1
        table.insert(str, 1, charset[remainder])
        n = math.floor(n / base)
    until n == 0

    return table.concat(str)
end

--you do not need to use this. reserveUID function automatically constructs UIDs if there is none available.
func._generateUID = function(amount: number)
    for i = func.TotalUIDs + 1, func.TotalUIDs + amount do
        local uid = encodeNumber(i)
        func.AvailableUIDs[i] = uid
        func.UIDtoIndex[uid] = i
    end
    func.TotalUIDs += amount
end

--removes uid from available uids, sends it to be used. To be returned later on.
func.reserveUID = function(): string
    local index, UID = next(func.AvailableUIDs)
    if index then
        func.AvailableUIDs[index] = nil
        return UID
    else
        local start = tick() --remove
        func._generateUID(100)

        if RunService:IsStudio() then --remove
            print("Generated 100 new UIDs. Took " .. tick() - start .. " seconds.")
        end

        return func.reserveUID()
    end
end

--adds uid back to Available UIDs
func.returnUID = function(uid: string)
    local i = func.UIDtoIndex[uid]
    if i then
        func.AvailableUIDs[i] = uid
    end
end

return func
7 Likes

made this one which is faster and you don’t need to worry if anything leaks.
It uses more and more digits, but i doubt you get past 4-5 digits.

You could also use something like this which delays the returnUID for minutes or more, for safety.
But its still dangerous if you do not trust yourself about leaked instances also this is not good for memory i guess

t = os.clock()
for i = queueStart, queueEnd, 1 do
    local entry = pendingQueue[i]
    if t >= entry.t then
        func.AvailableUIDs[entry.i] = entry.u
        pendingQueue[i] = nil
        queueStart = i + 1
    else
        break -- entries are ordered.
    end
end

here is the safe one i suppose:

--if you are using this outside a debris holder, put "_" infront to make sure they are absolutely unique (if you do not have any instances that start with _), "_" is not in the characters
local byteCharset = {}
do
    local charsetStr = [[!"#$%&'()*+,-./:;<=>?@0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^`{|}~]]
    for i = 1, #charsetStr do
        byteCharset[i] = string.byte(charsetStr, i)
    end
end
local reusableChars = table.create(11) --impossible to reach
local base = 93

local function encodeNumber(n)
    local len = 0

    while n > 0 do
        len += 1
        reusableChars[len] = byteCharset[(n % base) + 1]
        n = math.floor(n / base)
    end

    return string.char(table.unpack(reusableChars, 1, len))
end

local func = {}
func.n = 0

func.generateUID = function()
    func.n += 1
    return encodeNumber(func.n)
end

return func