Simple Twitter Code System for Beginners

This is a relatively short overview on how to get started implementing a code system into your game. With this system, you can update and add codes in live time (short delay ~30s) so you do not have to shutdown servers!

Step 1: Create an asset
For this simple system, we will need to have an assetId handy. To do this, publish a blank model, decal, audio, or place to Roblox. Anything with an assetId that has a description will work!

Step 2: Adding our codes
To store our codes, we will need to use a method of the HttpService. Lets start to build our system:

game:GetService("HttpService")

Now lets create our table of codes that we want to turn into a JSON encoded string.

local codes = {["GEMS"] = {"Gems", 25}}

“GEMS” = The code
“Gems” = The stat we want to change
25 = The value we want to change it by

This table set up allows us to store the code we want players to use as well as two important parameters:

  1. The type of reward we want to give the player
  2. The amount of reward we want to give the player

To turn this into a JSON encoded string we can then do the following:

local codes = {['GEMS'] = {'Gems', 25}}
print(game:GetService("HttpService"):JSONEncode(codes))

If we put this into the command line we get this

{"GEMS":["Gems",25]} 

Now, to add more codes, we don’t need to repeat this process. Since it is already set up, we can just add new codes like this:

{"GEMS":["Gems",25],"COINS":["Coins",25]}

Because this is how JSON string are encoded, we can be assured that this will work. We then copy and past this into the assets description!

Step 3: Accessing our codes
We now have our codes stored in an assets description, but how do we fetch them? It is actually fairly simple. Using the MarketplaceService, we can fetch the description using the assetId like so:

local description = game:GetService("MarketplaceService"):GetProductInfo(assetId).Description

To decode the JSON string back into a table we can use:

local description = game:GetService("MarketplaceService"):GetProductInfo(assetId).Description
local codes = game:GetService("HttpService"):JSONDecode(desc)

Because we are using JSONDecode, codes is a table. We can now easily search through the codes to see if the right one was called. Here is the finished product of our code system. Remember to keep this on the server, you wouldn’t want to leak the assetId where you store your codes!

local function codes(player, code)
	local codes = game:GetService("HttpService"):JSONDecode(game:GetService("MarketplaceService"):GetProductInfo(assetId).Description)
	local stats = PLAYERS_STATS
        -- check if code was already redeemed in your DataStore
	if(codes[code])then
		stats[codes[code][1]].Value = stats[codes[code][1]].Value + codes[code][2]
		return true
	else
		-- code doesn't exist
		return false
	end
end

Warnings:
The provided code may not fit your data structure. If you need assistance implementing the changing of stats, please feel free to send me a message!

TIPS:
If you do not feel comfortable using an asset to store codes, you can also do something more advanced without changing most of the code! Because we are decoding a JSON string, we can change our MarketplaceService call to a HttpService:GetAsync() request to a web server!

If you think I missed or overlooked anything, please feel free to send me feedback, this is my first time making a tutorial :slight_smile:

151 Likes

Very solid system! I currently use a datastore approach, however something like this is much more reliable.

7 Likes

You could also use pastebin.com, then do

local http = game:GetService("HttpService")
local res = http:RequestAsync({ Url = "https://pastebin.com/raw/(Insert_paste_ID)" })
local codes = res.Success and http:JSONDecode(res.Body) or {}
31 Likes

I was legitimately confused about what kind of code system this was.
At first I thought this was some kind of a code editor that does the job for you by placing elements (like Scratch :smile:)

Good tutorial, however, as this is intended for beginner scripters I recommend you to not use shortened names as this could be confusing, and just generally makes your code less readable and “beautiful”. (I know the code you provided was just as an example, but still, new scripters may find it confusing and will keep the bad habit of using shortened names).

You could probably add in the intro of your post what this code system is. I.e “This will be a short overview on how to create dynamical codes, in case you want to reward players for following you on Twitter by handing out codes” - bad example, but shows the point.

All in all, very nice :+1:

6 Likes

Nice!

As another addition, don’t forget to implement error handling. Any external calls from the game itself (e.g. HttpService, DataStoreService, MarketplaceService, etc.) should be wrapped with pcall.

21 Likes

Seems neat, said at the end I would use another service to manage codes as if they looked through your assets and find this table, and you have “tester” codes and if you use this for twitter it kinda defeats the purpose of making the user go to twitter. Instead they are over here looking at an asset.

5 Likes

Here is how I go about it. Hope its well comented.

   -- This is our codes table. it includes a Code as the index and a functioin as a value.
local CodesTable = {
	
	["FREE"] = function(player)
		 --Internal function that changes Money value of player
		PlayerStatManager:ChangeCurrency(player, "Money", 250, false)
	end;
	
	["EARLY"] = function(player) -- Early Access only
		if MarketplaceService:PlayerOwnsAsset(player, 2030764761) then -- Check if player has badge
			PlayerStatManager:ChangeCurrency(player, "Gems", 50, false) -- Change value of GEMS
		else
			return false
		end
	end;
}
-- Note that sessionData[player] is where data of the player is stored. Its all saved through JSONEncode

function PlayerStatManager:RedeemCode(player, Code) -- Function that is fired through a RemoteFunction
	for i, v in pairs(sessionData[player]["UsedCodes"]) do -- Loop through codes player already used
		if v == Code then return false end -- Check if player already redeemed the code he is trying to use
	end
	
	local res = false -- Make a varable that will hold our function from CodesTable
	
	for c, func in pairs(CodesTable) do -- Loop through the table of codes
		if c == Code then res = func break end -- if Code exists assign it to the variable and break out of the loop
	end
	
	
	if res ~= false then -- Check if function was found
		sessionData[player]["UsedCodes"][#sessionData[player]["UsedCodes"] + 1] = Code -- Add the used code to player data
		local success, msg = pcall(res, player) -- Call the function from the table and handle errors
		if not success then
			print("\t Error message:",msg)
			return false -- Return false if we have an error
		else
			return true -- Return true if everything is fine
		end
	end
	
	return false -- Return false if we didn't find a function that matched our code
end

Let me know what you think.

8 Likes

Great tutorial!

2 Likes

I was originally going to use this approach, but I decided not to due to have to wait for new servers in order to redeem new codes. I use a method similar to the one above now. Looks good to me though

4 Likes

I know, that is the only downside. Although you could probably just add conditions to the JSON string and make the server check for all things. Anyways nice tutorial :smiley:

1 Like

nice tutorial

So apparently the filter changes randomly and now the code “500K” will not allow you to update the description, just an FYI for anyone wanting to use that code.

2 Likes

Nice tutorial overall but I suggest you modify some things as I do not think they are right. First of all, why are you using asset descriptions to save your codes? That is not a good idea as there is a really small limit to the amount of characters that an asset can handle on its description and not only that but ROBLOX filters its asset description which means that your description might end up being tags, secondly if the players find your asset then instead of going to your twitter and finding codes they will just go to the assets, see the codes and enter them. That is something you do not want. Lastly, :GetProductInfo(assetId) caches, that means that you will have the same result as storing your codes in a table because the new codes will update when the server reboots only. And rebooting all of your servers just for a twitter code does not seem like a good idea. From what I can see you were using :GetProductInfo(assetId) on an asset so you could just update the asset and the changes would be made in real time to the server, but they won’t as :GetProductInfo(assetId) caches. I think using HttpServices or DataStores probably the best way of doing this. Also I suggest you store codes along with functions as @DevLuka suggested. It is a better way rather than storing the codes your way. What if you would want to give an item as a reward to the player? Not only that but it is also more practical and easier. Also if you really want to store your codes in assets, which imo is not a good idea you should use a proxy to send a request to your asset and get its description, it shouldn’t be that hard to program a proxy, from what I am aware of there is already an open source proxy made for ROBLOX. It is called rproxy iirc. The best way to make a twitter code system probably is to create a website so you can put your codes there and send a HttpRequest to your website and get the codes, alternatively put some security to the website so you would need to send a http request containing a passcode, if the passcode would be correct than it would return the JSON list of the codes. Real time changes and more efficient.

You do realize that the whole purpose of this post is to make a barebones twitter code system right? And what you are saying about caching isn’t correct either. Maybe you should look up the api and actually read the post before you make an entire rant? :upside_down_face:

2 Likes

It isn’t a rant, I am just saying what things could possibly be fixed or improved. And :GetProductInfo() does cache. What do you mean? Also I understand that this is supposed to be a basic twitter code system, I was just giving my opinion on what could be changed for good.

Wiki doesn’t state that it caches and in game I can easily update the assets description and have the codes work perfectly fine in live servers without shutting down. Also stating what can be fixed or improved doesn’t help at all because thats not the purpose of this resource…

2 Likes

I understand that that is not the purpose of the resource, and the fact that it says nothing in the wiki doesn’t mean it doesn’t cache, :GetRoleInGroup() caches too but there is nothing in the wiki. You can go ahead and test if :GetProductInfo() caches if you do not believe me, I just tested it today and it did cache.

It doesn’t cache, trust me. It will return the new description after a min or so after updating. If anything it caches for that small amount of time.

2 Likes

I thought :GetProductId() cached because I did not know it requierd like 1 minute for the changes to be applied, when I tested it, I only did so for like 10-20 seconds. Also ROBLOX should definitely say if a function caches or no to prevent such incidents. By the way:
image

This is a low quality tutorial. You’d never use a product’s description in a real game for reasons someone else already specified. Even if you didn’t want to make the tutorial more complicated with HttpService, storing codes in a table and / or using DataStoreService would be the obvious alternative., Additionally, there’s not a single check to see if the player has already redeemed a code.

Not to mention that code like this is just bad, and that it wouldn’t take much to improve it:

tats[codes[code][1]].Value = stats[codes[code][1]].Value + codes[code][2]

The code can error since it makes a remote call, causing the thread to terminate. The author was informed of this but ignored it.

This is sloppy. If an author isn’t willing to put in minimal effort when writing a tutorial, it’s best for them to refrain from doing so at all. There is no utility in an improperly implemented “barebones” system.

As one last point, Instance:FindFirstChild() should be used to locate the stat value in case someone wants to use a “Name” or “Value” stat during testing, even in a barebones system.

6 Likes