Uniquely Identifying objects & Mitigating duplication glitches

Introduction

Duplication glitches can be detrimental to a game’s economy if it is player based. If a duplication glitch breaks out it can do a great deal of damage to the economy.

Consider this:

You have just finished working on this great new mob for your game. It drops the single most rare item in the game. You go to bed satisfied with this week’s update. You wake up in the morning, eager to check how the update did only to find out that there’s an edge case where resetting your character during trading duplicates your inventory! You quickly fix this bug, push a patch update but by now, the damage has already been delt. That once rare item has now been duplicated hundreds of times, sold/traded to other players more times than you can count. You revert everyone’s data back to what it was before the update, upsetting a lot of the players who made a lot of progress during that time. The update ends up doing more bad to your game than good.

all of this could’ve been prevented had every item beared a UUID.

What is a UUID?

UUID stands for Universally Unique IDentifier. It is a randomly generated unique string used for identification purposes. A UUID can be used to tell apart from two objects of the same class having the exact same characteristics. The chance of two UUIDs generated ever being the same is mathematically negligible.

Okay, but how does using a UUID play out in game development?

Say you have a game which has an economy run by the players. Duplication glitches in such games can be detrimental as they can crash the economy of the game over night. Sure, testing a lot of edge cases can (and should be!) done before any major update is released but things can slip out of the testing phases. If every item ingame has a UUID then UUIDs of two objects can be compared before they’re traded, sold or added to the player’s inventory. Like I said, two objects having the same UUID is incredibly unlikely. If two items are ever found to have the same UUID, you can safely assume that they’ve been duplicated. You can then delete the item, and perhaps log the incident for future evaluation

How can I implement this?

there are several ways to implement such systems. For this post, I will be utilizing httpservice and attributes.

firstly we need to generate a UUID. You can create or your own system, pull from an external api or use the functions provided by roblox.

There’s a function provided by roblox called GenerateGUID() (GUID stands for globally unique identifier which is basically the same thing as UUID)

local HttpService = game:GetService("HttpService")
local UUID = HttpService:GenerateGUID()
 -- our unique string has been generated!

now that a UUID has been generated, we need to attach it to an object so that we can refer to it later on in other scripts. I will be using Attributes to accomplish this.

local HttpService = game:GetService("HttpService")

local function AttachUUID(object)
   local UUID = HttpService:GenerateGUID() -- generate a unique ID...
   object:SetAttribute("UUID",UUID) -- we've attached the UUID to the object!
end

AttachUUID(workspace.MySword)
print(workspace.MySword:GetAttribute("UUID")) -- prints {2B0FA56A-C1DF-43D1-8443-8D566ACDC13A}

Great! Now MySword has a unique id. It can now be differentiated from YourSword despite them both being Swords

Note: AttachUUID should only be called ONCE when the item is created. The UUID should then be saved to a datastore and pulled from there when we need to load that item again. You should NOT reattach a new UUID when loading the item!

Another way of indentification: Creation Timestamp

Apart from UUIDs, we can also assign a Creation Time to objects which refers to the time at which that particular object was created. This can be useful if older items in your game cost more than newer ones of the same class. This is also just as simple to do. For this, we will also be using Attributes and os.time() function

local function AttachCreationTime(object)
   local CreationTime = os.time() 
   object:SetAttribute("CreationTime",CreationTime) -- we've attached the CreationTime to the object!
end

AttachCreationTime(workspace.MySword)
print(workspace.MySword:GetAttribute("CreationTime")) -- prints 1625737115 (unix time)

the unix time can then be easily converted to months,days,hours etc. by dividing.

Note: AttachCreationTime should only be called ONCE when the item is created. The CreationTime should then be saved to a datastore and pulled from there when we need to load that item again. You should NOT reattach a new CreationTime when loading the item!

Example Usage

  • UUID in a trading system
local function GiveItem(player,item)
--assuming that an UUID was attached to the item when it was created..
    for _,object in pairs(player.Inventory) do
        if object:GetAttribute("UUID") == item:GetAttribute("UUID") then
            print("Duplicated item detected!")
            -- dont give the item to the player since it's a dupe
        else
            player.Inventory:Add(item) -- proceed to give the item if the UUIDs were different
        end
    end
end
  • Creation time for item worth evaluation
local function CalculateWorth(item)
    local oneYear = 365*24*60*60 -- 31536000 seconds in a year
    if item:GetAttribute("CreationTime") <= (os.time()-oneYear) then
        item.Price *= 2 --double the price if item is a year or older
    end
end

I hope that this post was useful. This is my first tutorial on the devforum so feedback & (constructive) criticism is greatly appreciated!

25 Likes

Really good explanation on how duplication occurs and how to prevent it,will use for future projects!

3 Likes

Hey your tutorial looks awesome!
I just want to contribute with what I’ve came up with.

This is probably the best way to store IDs without worry about being duplicated across all servers because your CreationTime method isn’t good enough to prevent duplicates (e.g. multiple people can generate asset within same second or even ms)

Here is the link to the post: Click on me!

1 Like

alternative solution avoiding string manipulation
“CreationTime method isn’t good enough to prevent duplicates (e.g. multiple people can generate asset within same second or even ms)” added measures to prevent this (if its even mathematically possible for it to happen)

local ids = {}
local function ID(obj)
      local id = os.clock()
      while ids[id] do
            id = os.clock()
      end
      ids[id] = obj
      return id
end

Two items can get the same UUID.

Heads up, not an expert, just a guy who gets curious about stuff.

While this is a possibility, it is very unlikely to happen. Assuming Roblox uses UUID4, A user would have to generate about 5.3 undecillion UUIDs for 1 collision to happen. For reference, getting directly struck by a meteor is more likely than 2 UUIDs being the same.

7 Likes

Yet as stated it is a possibility. You should always hope for the best, but be prepared for the worst.

1 Like

This would only work for one server. You will need external datastore to save all those IDs, solution that I provided is the most reliable - first part of the ID is time information and second is GUID. While this won’t prevent duplicates from happening entirely it’s very unlikely that UUID collision will happen in the first place (because every hour another set of UUIDs are available, - you can of course make it every second or so but I didn’t find it as good because you need to care about the data storage limitation as well).

I have a question. Say 2 players are in a trade, Player A gives a rare item to Player B. When the trade is processing, Player B suddenly leaves, creating a duplicate of the rare item. Now even if there is a unique identifier, how would we know that the duped item is actually duped since there’s no way we can compare both of the rare items since both players are in another server, what would be the solution to this?

2 Likes

That seems like an issue with the trade system itself. Items transfering should be the last thing to happen, after you are done processing whatever. So if a player leaves while the trade is processing, items wouldn’t have been transferred, the trade would cancel and thus no duplicate would’ve been created. If the player leaves after the trade has processed, items would’ve been transferred (and thus the trade would’ve been successful) and once again there wouldn’t be any dupes.

2 Likes