Preventing item duplication via trading

For one of my previous projects, I had to set a debounce in the player’s game data every time I loaded or saved the player’s data, so that they couldn’t leave and immediately rejoin the server to duplicate an item (trading an item to say, an alt, and then immediately rejoining after trading the item [this was done and worked with Mining Simulator before they patched it with this method]). iirc, people that would duplicate items would have a program that allowed them to open 2+ Roblox clients at once, making their rejoin very fast. Because of this, when the player rejoined, chances are that the server would get the data from before the trade because the :SetAsync() wouldn’t have taken effect yet.

Is there a cleaner, better way to combat this? Or should I stick with my previous method?

You would have to cache the trade data on the server then. All trade data is stored on the server until the next SetAsync can be done, when you have achieved this, you can compare the cached data even with players that rejoin immediately after leaving, this technically makes the debounce useless, but just leave it there to be safe. This can be achieved, but may require slight modifications on your data loading handler to take into account possible cached data and not always rely on what’s on the data store. A possible setup would be making use of BindableFunctions, upon loading a player’s data, you could invoke the BindableFunction to check if there is cached data, if there is, return the cached data to the data loading handler and make the necessary changes, otherwise return false and the handler will load whatever’s retrieved from the data store.

This is one of those things that is very difficult to combat. I’ll list some of the things I do to try and make duplication next to impossible:

  1. Saving is disabled while they are in a trade. This makes it so once the data is finally saved at the end of the trade, the odds of throttling go down significantly. This also makes it so the players’ data will likely save in sync and quicken the end of the trade (harder to time the dupe).
  2. "Last load" timestamp prevention technique. Whenever a player joins a server, the server records the current os.time() in a DataStore. Then, in the trading code, a couple things need to be done. First of all, the os.time() of the trade beginning needs to be recorded. Secondly, after the data saving is complete, the trade code needs to check that “last load” timestamp. If the “last load” timestamp is after the trade began, then you know the player has loaded into another server during the trading process and has pre-trade data “secure” in some other server. After you know this, all you have to do is cancel the trade and revert each player’s data.

EDIT: I’ve noticed that some people are still refering to this post, but I have changed my practices to better combat this issue. I would recommend saving user data with incrementing version numbers through UpdateAsync(), as this post recommends. This technique (essentially) automatically accomplishes what point 2 above was trying to accomplish, plus other benefits in other areas. I still recommend disabling saving during the trade (point 1) as well.

13 Likes

I would highly recommend checking out this module by loleris, as item duplication is what it was designed to prevent from the ground up. Any other data saving/loading method that uses :GetAsync will fail to work much more.

3 Likes