I’d highly recommend against using _G
or metatables for this. Additionally, you’re not using weak keys, which means that UUIDs for instances that aren’t reachable anymore will be kept around forever. This would be useful if it was possible to retrieve an instance by UUID, but that functionality isn’t provided. Basically, you have a memory leak.
You should probably use a ModuleScript instead. I’ve gone ahead and written a UuidStore example for you, complete with Luau types and documentation. You’re free to use it as long as you keep my name in there somewhere. This features both strong and weak references (weak by default), reference upgrading/downgrading, retrieving an Instance by UUID, and so on. Took me about an hour and 10 minutes.
--!strict
--------------------------------------------------------------------------------
--- UuidStore is a small module to help keep track of Instances through the use
--- of UUIDs (universally-unique identifiers). Although Instances can be stored
--- in any variable just like strings, sometimes strings can be used where
--- Instances cannot, for example when trying to pass certain Instances through
--- RemoteEvents.
---
--- UuidStore provides no synchronization between client and server, so UUIDs
--- are local only to the client (or server), but if one source becomes the
--- authoritative source of UUIDs (like how Instance replication IDs work), you
--- can communicate information about Instances that the other side cannot see,
--- for example ones in ServerStorage or in the local Camera.
---
--- UuidStore provides both strong and weak UUID references, where strong
--- references are guaranteed to always be retrievable later, and weak
--- references are guaranteed not to inhibit garbage collection.
---
--- To retrieve an Instance's UUID, call UuidStore:getUuid(Instance, boolean?).
--- The optional boolean argument can be set to `true` to create a strong
--- reference if the Instance did not already have a UUID. To upgrade existing
--- references, you can use UuidStore:upgradeWeakRef(uuid: string).
---
--- Once you have a UUID, you can retrieve the Instance it refers to by calling
--- the method UuidStorage:getInstanceByUuid(uuid: string). Note that this only
--- works on weak references if the Instance hasn't been garbage-collected; if
--- it has, then the method returns nil.
---
--- Use Luau autocomplete or read the module source for additional functions and
--- documentation.
---
--- Module donated by qwertyexpert to DrWhoInTARDIS
--------------------------------------------------------------------------------
local UuidStore = {}
UuidStore.strongInstances = {} :: {[Instance]: string}
UuidStore.strongUuids = {} :: {[string]: Instance}
UuidStore.weakInstances = setmetatable({} :: {[Instance]: string}, {__mode = 'k'})
UuidStore.weakUuids = {} :: {[string]: boolean}
local hex = {[0] = '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}
local function generateUuid(): string
return string.gsub('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx', '[xy]', function(c)
return hex[if c == 'x' then math.random(0, 0xf) else math.random(8, 0xb)]
end)
end
--------------------------------------------------------------------------------
--- Generates a new UUID which is guaranteed not to collide with any existing
--- UUIDs in the UuidStore.
---
--- Multiple calls to this function MAY return the same UUID unless the returned
--- UUIDs are used in the store.
--------------------------------------------------------------------------------
function UuidStore.generateUuid(self: typeof(UuidStore)): string
local uuid = generateUuid()
while self.strongUuids[uuid] or self.weakUuids[uuid] do uuid = generateUuid() end
return uuid
end
--------------------------------------------------------------------------------
--- Registers a new UUID in the store, for example if you are receiving UUIDs
--- from a server and need to track them locally. This is an advanced function
--- that can corrupt state, especially if this UUID exists in the store already.
--- Take care only to call it when you're sure this UUID and Instance have BOTH
--- never been seen before.
--------------------------------------------------------------------------------
function UuidStore.registerUuid(self: typeof(UuidStore), instance: Instance, uuid: string, strong: boolean)
if strong then
self.strongInstances[instance], self.strongUuids[uuid] = uuid, instance
else
self.weakInstances[instance], self.weakUuids[uuid] = uuid, true
end
end
--------------------------------------------------------------------------------
--- Gets the UUID of an Instance. This method can never fail and will commit a
--- new UUID to the store if one is not already found.
---
--- The optional `strong` argument dictates whether to use a strong reference or
--- not, which will inhibit garbage collection but guarantee that
--- getInstanceByUuid will always be able to find the Instance.
--------------------------------------------------------------------------------
function UuidStore.getUuid(self: typeof(UuidStore), instance: Instance, strong: boolean?): string
local found = self.strongInstances[instance] or self.weakInstances[instance]
if not found then
local strong = strong == true
local uuid = self:generateUuid()
self:registerUuid(instance, uuid, strong)
found = uuid
end
return found
end
--------------------------------------------------------------------------------
--- Returns the Instance with the specified UUID. This method MAY return nil iff
--- the Instance only had a weak reference and was garbage-collected since its
--- UUID was generated.
--------------------------------------------------------------------------------
function UuidStore.getInstanceByUuid(self: typeof(UuidStore), uuid: string): Instance?
local found = self.strongUuids[uuid] or self.weakUuids[uuid]
if typeof(found) == 'Instance' then return found end -- if found something in strong, return it
if found == nil then return nil end -- if found nothing, return nil
-- found is probably true which means there MAY be a weak reference in weakInstances
for instance, instanceUuid in pairs(self.weakInstances) do
if instanceUuid == uuid then return instance end
end
return nil
end
--------------------------------------------------------------------------------
--- Returns whether or not the specified UUID holds a strong reference to an
--- Instance. A return value of false does not necessarily mean that the UUID is
--- in the store.
--------------------------------------------------------------------------------
function UuidStore.uuidIsStrong(self: typeof(UuidStore), uuid: string): boolean
return self.strongUuids[uuid] ~= nil
end
--------------------------------------------------------------------------------
--- Returns whether or not the specified UUID holds or held a weak reference to
--- an Instance. A return value of false does not necessarily mean that the UUID
--- is in the store, and a return value of true does not necessarily mean that
--- the Instance hasn't been garbage-collected.
--------------------------------------------------------------------------------
function UuidStore.uuidIsWeak(self: typeof(UuidStore), uuid: string): boolean
return self.weakUuids[uuid] == true
end
--------------------------------------------------------------------------------
--- Downgrades a UUID's strong reference to a weak reference. This is a no-op if
--- the reference is already weak. This enables the Instance to be
--- garbage-collected.
--------------------------------------------------------------------------------
function UuidStore.downgradeStrongRef(self: typeof(UuidStore), uuid: string)
local instance = self.strongUuids[uuid]
if instance then
self.weakInstances[instance], self.weakUuids[uuid] = uuid, true
self.strongInstances[instance], self.strongUuids[uuid] = nil, nil
end
end
--------------------------------------------------------------------------------
--- Upgrades a UUID's weak reference to a strong reference. This is a no-op if
--- the reference is already strong or if the Instance has been
--- garbage-collected. Returns false if the reference is weak and the Instance
--- has been garbage-collected.
--------------------------------------------------------------------------------
function UuidStore.upgradeWeakRef(self: typeof(UuidStore), uuid: string): boolean
local instance = if self.weakUuids[uuid] then self:getInstanceByUuid(uuid) else nil
if instance then
self.strongInstances[instance], self.strongUuids[uuid] = uuid, instance
self.weakInstances[instance], self.weakUuids[uuid] = nil, nil
return true
end
return UuidStore:uuidIsStrong(uuid)
end
return UuidStore